Search code examples
cvisual-c++c-preprocessorvariadic-macros

How to Correctly use Variadic Macros to Call Other Macros


I know this isn't a fix my build error site, but I've exhausted all my options. None of my coworkers can see anything wrong.

I have a set of macros in the format EventWriteQuic* and take variable number of arguments. For example:

#define EventWriteQuicLibraryInitialized(PartitionCount, DatapathFeatures) \
        MCGEN_EVENT_ENABLED(QuicLibraryInitialized) \
        ? McTemplateU0qq(&MICROSOFT_MSQUIC_PROVIDER_Context, &QuicLibraryInitialized, PartitionCount, DatapathFeatures) : 0

I am then trying to define my own macro like this:

#define QuicTraceEvent(Name, ...) EventWriteQuic##Name(__VA_ARGS__)

Finally, I invoke my macro like this:

    QuicTraceEvent(LibraryInitialized,
        MsQuicLib.PartitionCount,
        QuicDataPathGetSupportedFeatures(MsQuicLib.Datapath));

For some reason I get the following build warning:

.\core\library.c(192,1): warning C4003: not enough arguments for function-like macro invocation 'EventWriteQuicLibraryInitialized'

The final bit of info I have is that for EventWriteQuic* functions that take no arguments, the above code seems to work just fine, which makes me think it's some how ignoring my __VA_ARGS__.

EDIT:

The output generated when using the /P flag:

((((const unsigned char*)Microsoft_QuicEnableBits)[0 >> 3] & (1u << (0 & 7))) != 0) ? McTemplateU0qq_EventWriteTransfer(&MICROSOFT_MSQUIC_PROVIDER_Context, &QuicLibraryInitialized, MsQuicLib.PartitionCount, QuicDataPathGetSupportedFeatures(MsQuicLib.Datapath), ) : 0;

For some reason it's adding an extra comma at the end it seems. When I call the EventWriteQuicLibraryInitialized macro directly, this does no occur.


Solution

  • Apparently, MSVC has a bug that does not allow it to directly pass __VA_ARGS__ successfully to another macro.

    The sample that failed for me was:

    #define A(X, ...) A_##X(__VA_ARGS__)
    #define A_TEST(A, B) ((A)+(B))
    
    printf("%d\n", A(TEST, 1, 2));
    

    However, if I wrap the __VA_ARGS__ within parentheses, the contents are treated as a single argument. I can pass this to another macro to actually do the invocation, and it works.

    #define A(X, ...) A_(X, (__VA_ARGS__))
    #define A_(X, Y) A_##X Y
    #define A_TEST(A, B) ((A)+(B))