Токен в определении макроса не был объявлен в этой области

#c #define-syntax

#c #определить-синтаксис

Вопрос:

Я пытаюсь создать логическую систему, использующую макросы defines для реализации моего регистратора, которая превратится в ничто, когда будут определены определенные переключатели. Проблема в том, что когда я складываю несколько таких переключателей, вложенных один в другой (например, при вызове макроса, подобного функции IF_SWITCH_1), я получаю множество ошибок, перечисленных в блоке кода. Что вызывает эти ошибки? Как я мог их исправить?

     //Creation of the switches
    #define _ADD_PARTS2(part1, part2, ...) part1 ## part2 (__VA_ARGS__)
    #define _ADD_PARTS(part1, part2, ...) _ADD_PARTS2(part1, part2, __VA_ARGS__)
    #define _LOGIC_SWITCH_(name, cond, ...) _ADD_PARTS(name, cond, __VA_ARGS__)
    
    //Toggles
    #define CONDITION_1 true
    #define CONDITION_2 true
    
    //Switches
    #define IF_SWITCH_2_true(a, b, c) std::cout << "Passed" << std::endl
    #define IF_SWITCH_2_false(...)
    
    #define IF_SWITCH_2(a, b, c) _LOGIC_SWITCH_(IF_SWITCH_2_, CONDITION_1, a, b, c)
    
    #define IF_SWITCH_1_true(a, b, c) IF_SWITCH_2(a, b, c)
    #define IF_SWITCH_1_false(...)
    
    #define IF_SWITCH_1(a, b, c) _LOGIC_SWITCH_(IF_SWITCH_1_, CONDITION_1, a, b, c)
    
    //Use
    IF_SWITCH_2(1, 1, 1); //Compiles and passes
    IF_SWITCH_1(1, 1, 1); //"IF_SWITCH_2" was not declared in this scope; 
    //_LOGIC_SWITCH_ was not declared in this scope; 
    //Use of undeclared indentifier IF_SWITCH_2_
    
    //Switching on and off
    #undef CONDITION_2 
    #define CONDITION_2 false //Any invocation from this point on wont pass past the logic switch
    IF_SWITCH_2(1, 1, 1); //Wont pass
  

Насколько я знаю, изменение порядка определений не повлияло на ошибки.

Скомпилирован с 64-разрядной версией MinGW 8.10

Комментарии:

1. Так почему бы просто не #if CONDITION_2 ..... #else .... #endif ?

2. Я хочу иметь возможность переключать эти переключатели для частей кода ie. #undef CONDITION_1 и #define CONDITION_1 false который с этого момента отключит все переключатели в реализации макроса. #if не может быть реализован подобным образом.

3. Так почему бы просто не #define IF_SWITCH_1(a, b, c) (CONDITION_1 ? std::cout << "Passed" << std::endl : (void)0) ? Тем не менее, расширение макроса в ничто очень сбивает с толку циклами и if s, т.Е. while (true) IF_SWITCH_1(1, 1, 1) — когда IF_SWITCH_1 он расширяется в ничто, он переходит к следующему оператору…

4. Проблема в том, что логика, связанная с протоколированием, намного сложнее, чем показано здесь в сокращенном виде (серьезность журнала, флаги для того, что регистрировать и т.д.). Я боюсь, что это в сочетании с частотой использования действительно увеличит время компиляции (особенно при компиляции в режиме отладки). Из-за этого я бы предпочел, чтобы макросы действительно могли расширяться в ничто, даже если (true == true) инструкции могли бы работать так же хорошо, как и не должны (как насчет режима отладки?) оказывает какое-либо влияние на производительность

5. Это не решает вопрос, но имена, которые начинаются с символа подчеркивания, за которым следует заглавная буква ( _ADD_PARTS2 и т.д.), И имена, которые содержат два последовательных подчеркивания, зарезервированы для использования реализацией. Не используйте их в своем коде.

Ответ №1:

Что вызывает эти ошибки?

Расширения выполняются примерно один раз. Как только макрос развернут, он не будет развернут снова, также при вызовах вложенного макроса. В любом случае цепочка:

 IF_SWITCH_1(1, 1, 1)
_LOGIC_SWITCH_(IF_SWITCH_1_, CONDITION_1, 1, 1, 1)  // step 2
_ADD_PARTS(IF_SWITCH_1_, true, 1, 1, 1)
_ADD_PARTS2(IF_SWITCH_1_, true, 1, 1, 1)
IF_SWITCH_1_true(1, 1, 1)
IF_SWITCH_2(1, 1, 1)
_LOGIC_SWITCH_(IF_SWITCH_2_, CONDITION_1, 1, 1, 1)  
// _LOGIC_SWITCH_ was expanded at step #2
_LOGIC_SWITCH_(IF_SWITCH_2_, true, 1, 1, 1)  
// expanding stops here
  

Как я мог их исправить?

Прежде всего, идентификаторы с начальным подчеркиванием, за которым следует заглавная буква, зарезервированы для реализации. Не используйте такие идентификаторы в своем собственном коде.

Если вы хотите выполнить оценку во время выполнения, я предлагаю для:

 static inline void if_switch_2_execute(int a, int b, int c) {
    std::cout << "passed" << std::endl;
}
#define IF_SWITCH_2(a, b, c)  (CONDITION_2?if_switch_2_execute(a, b, c):(void)0)
#define IF_SWITCH_1(a, b, c)  (CONDITION_1?IF_SWITCH_2(a, b, c):(void)0)
  

В любом случае, исправление заключается в перемещении вычисления _LOGIC_SWITCH_ из вложенного вызова внутри _LOGIC_SWITCH_ на верхний уровень, так что _LOGIC_SWITCH_ оно расширяется только один раз в пределах одной цепочки вычислений (я не знаю, как это объяснить, вот как я это понимаю … : / ). Вот почему это обычно делается #define MACRO(something) CHOOSE_FUNCTION_TO_CALL(__VA_ARGS__)(__VA_ARGS__) .

 #define CONDITION_1 true
#define CONDITION_2 true

#define CONCAT(a, b) a##b
#define XCONCAT(a, b) CONCAT(a, b)   // this is your _LOGIC_SWITCH_

#define IF_SWITCH_2_true(a, b, c)  std::cout << "Passed" << std::endl
#define IF_SWITCH_2_false(...)
#define IF_SWITCH_2(a, b, c) XCONCAT(IF_SWITCH_2_, CONDITION_2)(a, b, c)

#define IF_SWITCH_1_true(a, b, c) IF_SWITCH_2(a, b, c)
#define IF_SWITCH_1_false(...)
#define IF_SWITCH_1(a, b, c)  XCONCAT(IF_SWITCH_1_, CONDITION_1)(a, b, c)
  

Комментарии:

1. Спасибо за ответ. Все еще хотел бы знать, почему это так, но я думаю, это укажет мне правильную продолжительность.

2. why that is , ну, почему что? _LOGIC_SWITCH_ расширяется в IF_SWITCH_1 . Таким образом, второй раз _LOGIC_SWITCH_ из IF_SWITCH_2 не будет расширен, потому что он уже был расширен один раз. Каждый макрос расширяется только один раз в одной цепочке. Это знаменитый технический термин для макрорасширений «помеченный синим». В любом случае, 6.10.3.4p2: Furthermore, if any nested replacements encounter the name of the macro being replaced, it is not replaced

3. Это отвечает на это. Спасибо