Home >Backend Development >C++ >Why does MSVC variadic macro expansion differ from GCC, and how can we overcome this discrepancy?
MSVC Variadic Macro Expansion
Variadic macros offer versatile expansion capabilities, allowing you to handle multiple arguments within macros. In GCC, macros like the following work as expected:
<code class="cpp">#define VA_NARGS_IMPL(_1, _2, _3, _4, _5, N, ...) N #define VA_NARGS(...) VA_NARGS_IMPL(__VA_ARGS__, 5, 4, 3, 2, 1) #define FULLY_EXPANDED(count, ...) \ MAC ## count (__VA_ARGS__) #define SEMI_EXPANDED(count, ...) FULLY_EXPANDED(count, __VA_ARGS__) #define EXPAND_THESE(...) SEMI_EXPANDED(VA_NARGS(__VA_ARGS__), __VA_ARGS__) #define ACTUAL_MACRO(x) parent->GetProperty<x>(); #define MAC1(a) ACTUAL_MACRO(a) #define MAC2(a,b) MAC1(a) ACTUAL_MACRO(b) #define MAC3(a,b,c) MAC2(a,b) ACTUAL_MACRO(c) #define MAC4(a,b,c,d) MAC3(a,b,c) ACTUAL_MACRO(d) #define MAC5(a,b,c,d,e) MAC4(a,b,c,d) ACTUAL_MACRO(e)</code>
MSVC Expansion Behavior
However, in Microsoft's C Compiler (MSVC ), the macro expansion behaves differently. Instead of expanding each argument separately, MSVC concatenates all arguments into a single expression.
struct MyStructure<br>{<br> void Foo()<br> {</p> <pre class="brush:php;toolbar:false">EXPAND_THESE(Property1, Property2, Property3, Property4)
}
Base * parent;
};
GCC Expansion:
struct MyStructure<br>{<br> void Foo()<br> {</p> <pre class="brush:php;toolbar:false">parent->GetProperty<Property1>(); parent->GetProperty<Property2>(); parent->GetProperty<Property3>(); parent->GetProperty<Property4>();
}
Base * parent;
}
MSVC Expansion:
struct MyStructure<br>{<br> void Foo()<br> {</p> <pre class="brush:php;toolbar:false">parent->GetProperty<Property1, Property2, Property3, Property4>();
}
Base * parent;
}
Solution:
Jeff Walden proposed a workaround using the following pattern:
<code class="cpp">#define ERRORn(...) ERROR_CHOOSE_HELPERn(COUNT_ARGS_MAX5(__VA_ARGS__), __VA_ARGS__) #define ERROR_CHOOSE_HELPER1(count) ERROR1 #define ERROR_CHOOSE_HELPER2(count) ERROR2 #define ERROR_CHOOSE_HELPER(count) ERROR_CHOOSE_HELPER##count</code>
This approach requires defining a CHOOSE_HELPER macro for each desired variadic macro count (e.g., ERROR1, ERROR2, etc.). However, it is essential to declare each CHOOSE_HELPER macro within the scope of the variadic macro's definition.
A more concise and portable solution is to leverage the following technique:
<code class="cpp">#define GLUE(x, y) x y #define RETURN_ARG_COUNT(_1_, _2_, _3_, _4_, _5_, count, ...) count #define EXPAND_ARGS(args) RETURN_ARG_COUNT args #define COUNT_ARGS_MAX5(...) EXPAND_ARGS((__VA_ARGS__, 5, 4, 3, 2, 1, 0)) #define OVERLOAD_MACRO2(name, count) name##count #define OVERLOAD_MACRO1(name, count) OVERLOAD_MACRO2(name, count) #define OVERLOAD_MACRO(name, count) OVERLOAD_MACRO1(name, count) #define CALL_OVERLOAD(name, ...) GLUE(OVERLOAD_MACRO(name, COUNT_ARGS_MAX5(__VA_ARGS__)), (__VA_ARGS__))</code>
With this setup, macros can be defined as:
<code class="cpp">#define ERROR1(title) printf("Error: %s\n", title) #define ERROR2(title, message)\ ERROR1(title);\ printf("Message: %s\n", message) #define ERROR(...) CALL_OVERLOAD(ERROR, __VA_ARGS__) #define ASSERT1(expr) singleArgumentExpansion(expr) #define ASSERT2(expr, explain) twoArgumentExpansion(expr, explain) #define ASSERT(...) CALL_OVERLOAD(ASSERT, __VA_ARGS__)</code>
By using the OVERLOAD_MACRO hierarchy, it is possible to avoid defining CHOOSE_HELPER macros.
The above is the detailed content of Why does MSVC variadic macro expansion differ from GCC, and how can we overcome this discrepancy?. For more information, please follow other related articles on the PHP Chinese website!