I am trying to make a logic system using defines macros for implementation of my logger that will expand into nothing when certain toggles are defined. The problem is that when I stack multiple of these switches one nested inside the other (like with calling of the IF_SWITCH_1 function-like macro) I get multiple errors as listed in the code block. What causes these errors? How could I fix them?
//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
As far as I know changing the order of the definitions had no impact on the errors.
Compiled with MinGW 8.10 64-bit
What causes these errors?
The expansions are run kind-of once. Once a macro is expanded, it wont be expanded again, also in a nested macro calls. Anyway the chain is:
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
How could I fix them?
First of all identifiers with leading underscore followed by uppercase letter are reserved for implementation. Do not use such identifiers in your own code.
If you wish for runtime evaluation, I suggest for:
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)
Anyway, the fix is to move the evaluation of _LOGIC_SWITCH_
from the nested call within _LOGIC_SWITCH_
to upper level, so that _LOGIC_SWITCH_
is expanded only once within one evaluation chain (I do not know how to explain it, that's how I understand it... :/ ). That's why it's typical to do #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)