Detect empty macro arguments

The macro NARG2 that we introduced in post still has a major disadvantage, it will not be able to detect an empty argument list. This is due to a fundamental difference between C and its preprocessor. For C a parenthesis () is empty and contains no argument. For the preprocessor it contains just one argument, and this argument is the empty token.

So in fact NARG2 is cheating. It doesn’t count the number of arguments that it receives, but returns the number of commas plus one. In particular, even if it receives an empty argument list it will return 1. The following two macros better expresses this property:

#define _ARG16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) _15
#define HAS_COMMA(...) _ARG16(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0)

As before, these are constrained to a maximum number of arguments (here 16), but you will easily work out how to extend it to larger maximum values.

Now, when we want to write a macro that detects an empty argument we will be using the feature that a function macro that is not followed by an open parenthesis will be left alone. This we will do with the following macro that just transforms the following parenthesis and contents into a comma.

#define _TRIGGER_PARENTHESIS_(...) ,

The idea is to put the arguments that we want to test between the macro and its parenthesis, such that the macro only triggers if the arguments are empty:

_TRIGGER_PARENTHESIS_ __VA_ARGS__ (/*empty*/)

To do so we, must first check for some corner cases where special properties of the argument might mimic the behavior that we want to test.

#define ISEMPTY(...)                                                    \
_ISEMPTY(                                                               \
          /* test if there is just one argument, eventually an empty    \
             one */                                                     \
          HAS_COMMA(__VA_ARGS__),                                       \
          /* test if _TRIGGER_PARENTHESIS_ together with the argument   \
             adds a comma */                                            \
          HAS_COMMA(_TRIGGER_PARENTHESIS_ __VA_ARGS__),                 \
          /* test if the argument together with a parenthesis           \
             adds a comma */                                            \
          HAS_COMMA(__VA_ARGS__ (/*empty*/)),                           \
          /* test if placing it between _TRIGGER_PARENTHESIS_ and the   \
             parenthesis adds a comma */                                \
          HAS_COMMA(_TRIGGER_PARENTHESIS_ __VA_ARGS__ (/*empty*/))      \
          )

Here we distinguish four different cases, of which the last is just the main idea as exposed above. The first helps to exclude the trivial case, that __VA_ARGS__ already contains a comma by itself. The two others test if the two possible combinations of _TRIGGER_PARENTHESIS_, __VA_ARGS__ and (/*empty*/) also already trigger a comma to their output.

Now the outcome of this will be calling the macro _ISEMPTY with four different 0-1-values according to different cases that __VA_ARGS__ presents. In particular, the case that __VA_ARGS__ is empty corresponds exactly to the outcome _ISEMPTY(0, 0, 0, 1). All other outcomes will indicate that it was non-empty. We will detect this case with the following helper macro.

#define _IS_EMPTY_CASE_0001 ,

and we leave all the other 15 cases undefined. Now with

#define PASTE5(_0, _1, _2, _3, _4) _0 ## _1 ## _2 ## _3 ## _4
#define _ISEMPTY(_0, _1, _2, _3) HAS_COMMA(PASTE5(_IS_EMPTY_CASE_, _0, _1, _2, _3))

we will exactly detect the case we are interested in.

As a test here comes all of that together in a block. This is not a reasonable C program but just something to run through the preprocessor to test the validity of the approach.

#define _ARG16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) _15
#define HAS_COMMA(...) _ARG16(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0)
#define _TRIGGER_PARENTHESIS_(...) ,

#define ISEMPTY(...)                                                    \
_ISEMPTY(                                                               \
          /* test if there is just one argument, eventually an empty    \
             one */                                                     \
          HAS_COMMA(__VA_ARGS__),                                       \
          /* test if _TRIGGER_PARENTHESIS_ together with the argument   \
             adds a comma */                                            \
          HAS_COMMA(_TRIGGER_PARENTHESIS_ __VA_ARGS__),                 \
          /* test if the argument together with a parenthesis           \
             adds a comma */                                            \
          HAS_COMMA(__VA_ARGS__ (/*empty*/)),                           \
          /* test if placing it between _TRIGGER_PARENTHESIS_ and the   \
             parenthesis adds a comma */                                \
          HAS_COMMA(_TRIGGER_PARENTHESIS_ __VA_ARGS__ (/*empty*/))      \
          )

#define PASTE5(_0, _1, _2, _3, _4) _0 ## _1 ## _2 ## _3 ## _4
#define _ISEMPTY(_0, _1, _2, _3) HAS_COMMA(PASTE5(_IS_EMPTY_CASE_, _0, _1, _2, _3))
#define _IS_EMPTY_CASE_0001 ,

#define EATER0(...)
#define EATER1(...) ,
#define EATER2(...) (/*empty*/)
#define EATER3(...) (/*empty*/),
#define EATER4(...) EATER1
#define EATER5(...) EATER2
#define MAC0() ()
#define MAC1(x) ()
#define MACV(...) ()
#define MAC2(x,y) whatever
ISEMPTY()
ISEMPTY(/*comment*/)
ISEMPTY(a)
ISEMPTY(a, b)
ISEMPTY(a, b, c)
ISEMPTY(a, b, c, d)
ISEMPTY(a, b, c, d, e)
ISEMPTY((void))
ISEMPTY((void), b, c, d)
ISEMPTY(_TRIGGER_PARENTHESIS_)
ISEMPTY(EATER0)
ISEMPTY(EATER1)
ISEMPTY(EATER2)
ISEMPTY(EATER3)
ISEMPTY(EATER4)
ISEMPTY(MAC0)
ISEMPTY(MAC1)
ISEMPTY(MACV)
/* This one will fail because MAC2 is not called correctly */
ISEMPTY(MAC2)

Edit: What is presented here is a version with minor improvement to capture the case of MAC0. Notice the restriction on the argument of ISEMPTY that is apparent with MAC2. In fact ISEMPTY should work when it is called with macros as argument that expect 0, 1 or a variable list of arguments. If called with a macro X as an argument that itself expects more than one argument (such as MAC2) the expansion leads to an invalid use of that macro X.

40 thoughts on “Detect empty macro arguments”

  1. Clever but non-standard; see C99 §6.10.3 ¶4. Referring to a macro whose identifier-list ends with an ellipsis, the standard says:

    … there shall be more arguments in the invocation than there are parameters in the macro definition (excluding the ...).

    1. I am not sure that I completely understand what you are referring to. C and the preprocessor are two different languages.

      • For C an expression like f() is a function call with an empty argument list.
      • For the preprocessor f() is a macro call (if f is defined as such) with one argument, namely the empty token. You see that if you look at f(,): this has two arguments, both empty tokens.

      What the standard means here that a macro

      #define f2(A, ...) 
      

      must be called with at least one comma inside the ().

      (If you found such an occurrence please point me to it, there are certainly still bugs and incompatibilities in there.)

  2. As I understand it, given a macro defined
    #define X(...) {__VA_ARGS__},
    it is undefined behavior to invoke it in the form
    X();
    it may (and under GCC it does) resolve to { }, it may crash the compiler, it may resolve to {nasal_demons(), etc., etc., etc.

    1. I don’t see how you come to that conclusion. Maybe I didn’t explain myself well. The standard makes explicit reference to arguments that are empty in the context of the preprocessor. Before the text you cite, the standard explicitly allows for empty tokens in parameter lists:

      including those arguments consisting of no preprocessing tokens

      So the usage is you are demonstrating is completely in line with the standard. This is a macro that has zero identifiers and that is called with one argument, consisting of no preprocessing token.

      You also have other mention of empty arguments, e.g for stringyfication

      The character string literal corresponding to an empty argument is “”.

        1. Cool, I don’t know. Actually this was just one of the difficulties from the start. The PP_NARG macro that you were citing was not able to distinguish f()from f(1) just because of that.

          So at a first glance you would call that an inconsistency between the preprocessor and the rest of the language 😦

          At a second, I have learned to live with it and even to take advantage 😉

  3. You can make a work around for empty arguments case. The issue is that additional comma produces for empty parameters. You just can add a concatenation operator ## and it will resolve this issue. Here is an example.

    #define COUNT(…) COUNT_ (0, ## __VA_ARGS__)
    #define COUNT_(…) COUNT__ (__VA_ARGS__, 3, 2, 1, 0)
    #define COUNT__(_0, _1, _2, _3, N, …) COUNT_ ## N (_1, _2, _3)
    #define COUNT_0(…) 0
    #define COUNT_1(…) 1
    #define COUNT_2(…) 2
    #define COUNT_3(…) 3

    1. Would be nice such swallowing of the comma by the ##. Unfortunately this is only a GNU extension, which is even switched off when working in C99 mode. To cite the gcc documentation:

      In this case the C99 standard is clear that the comma must remain, however the existing GCC extension used to swallow the comma. So CPP retains the comma when conforming to a specific C standard, and drops it otherwise.

  4. Hi it mentions easily figuring out how to get around the 16 argument limitation. Could you please help me out on that one? Would love to pass 32 arguments to a c (…) function but __VA_ARGS_ seems to have that 16 argument limitation you note.

    1. Hi, I don’t know exactly what you tried but the easiest would probably to download P99 and take it from there. The 16 arguments limit is really only for the one I give in the post, since the long one would be completely unreadable.

      BTW, the C99 standard enforces that macros can take up to 127 arguments so that really shouldn’t be the limit.

  5. So I’m trying to get this example working in Visual Studio 2008, but all ISEMPTY’s resolve to 0. Do you have any suggestions?

    1. Hello Mark,
      as far as I understand the Microsoft compilers don’t implement C99. In particular I remember having read that their preprocessor is not conforming to the C99 standard, and that there is a “wouldn’t fix” bug on the macro-argument counting trick.

      So the only “simple” solution to that would be to use a different preprocessor, sorry.

      1. Darn. Alright. Thank you! If I happen to figure anything out, I’ll be sure to post back here.

  6. #define AMACRO(x) ()
    IS_EMPTY(AMACRO) is true, where of course it should be false.

    This is the flaw in your presentation and it is well-known. See Paul Mensonides post at http://boost.2283326.n4.nabble.com/problem-with-BOOST-PP-IS-EMPTY-macro-td2603454.html.

    I give a better and less complicated version of IS_EMPTY in my variadic data macro library in the Boost sandbox which is almost wholly based on Paul Mensonides brilliant version ( I have to do some tweaks for VC8 ).

    1. Your assertion is incorrect. ISEMPTY(AMACRO) gives the correct result, namely 1.

      During the expansion of the fourth case of the _ISEMPTY the macro _TRIGGER_PARENTHESIS_ will only be expanded if and only if __VA_ARGS__ has been expanded to the empty token.

      What your example and the discussion on the boost mailing list suggests, though, is that ISEMPTY is not completely robust when it is called with macro names as parameters that receive multiple arguments. I improved the macro a bit to cover one more case and documented the cases for which it fails.

  7. The preprocessor macro code to detect an empty argument list works fine on the flavors of Unix that I have tested (Linux, FreeBSD, OSX) with gcc, but fails on MS/Windows XP with MSVC. I have traced the problem down to how the MSVC preprocessor deals with the __VAR_ARGS__ token in a variadic macro that is defined in terms of another variadic macro. The following source code demonstrates the problem:


    /* test_VA_ARGS
    */
    #include
    #include
    #include
    using namespace std;

    int
    main ()
    {
    #define THIRD_ARG(_1, _2, _3, ...) _3
    #define TEST_THIRD_ARG(...) THIRD_ARG(__VA_ARGS__, 4, 5)
    cout << "TEST_THIRD_ARG(1) = "
    << TEST_THIRD_ARG (1) << endl;
    cout << "TEST_THIRD_ARG(1, 2) = "
    << TEST_THIRD_ARG (1, 2) << endl;
    cout << "TEST_THIRD_ARG(1, 2, 10, 11) = "
    << TEST_THIRD_ARG (1, 2, 10, 11) << endl;
    }

    Here the TEST_THIRD_ARG variadic macro is defined in terms of the THIRD_ARG variadic macro where the argument list from TEST_THIRD_ARG, represented by __VA_ARGS__ in its definition, is expected to become the leading argument list parameters of the THIRD_ARG macro. This is the same as the _ARG16 and HAS_COMMA macros in the “Detect empty macro arguments” description. The result of using the TEST_THIRD_ARG macro should be the third argument of its argument list with arguments 4 and 5 being appended to its list.

    On Unix systems with gcc the resulting executable produces the following output:


    TEST_THIRD_ARG(1) = 5
    TEST_THIRD_ARG(1, 2) = 4
    TEST_THIRD_ARG(1, 2, 10, 11) = 10

    This is what is expected. However, on MS/Windows XP with the MSVC compiler – Microsoft Visual Studio 9.0 (Compiler Version 15.00.30729.01 for 80×86) – the result is always the same:


    TEST_THIRD_ARG(1) = 5
    TEST_THIRD_ARG(1, 2) = 5
    TEST_THIRD_ARG(1, 2, 10, 11) = 5

    It seems that the __VA_ARGS__ token is not being expanded in the TEST_THIRD_ARG definition before the THIRD_ARG macro is being evaluated, or __VA_ARGS__ is being treated as a single argument regardless of whether it expands to a value that contains commas. To test this hypothesis the following can be added to the source code:


    #define FIRST_ARG(_1, _2, _3, ...) _1
    #define TEST_FIRST_ARG(...) FIRST_ARG(__VA_ARGS__, 4, 5)
    cout << "TEST_FIRST_ARG(1) = "
    << TEST_FIRST_ARG(1) << endl;
    cout << "TEST_FIRST_ARG(1, 2) = "
    << TEST_FIRST_ARG (1, 2) << endl;

    This compiles and executes correctly on Unix with gcc, but will not compile on MS/Windows with MSVC:


    .\test_VA_ARGS.cc(24) : error C2563: mismatch in formal parameter list
    .\test_VA_ARGS.cc(24) : error C2568: '<<' : unable to resolve function overload

    To confirm that __VA_ARGS__ is being treated by MSVC as a single argument in the TEST_FIRST_ARG definition, the last source code statement, above, can replaced with:


    #define STRINGIFIED(...) #__VA_ARGS__
    #define AS_STRING(...) STRINGIFIED(__VA_ARGS__)
    cout << "AS_STRING (TEST_FIRST_ARG(1, 2)) = "
    << AS_STRING (TEST_FIRST_ARG (1, 2)) << endl;

    On Unix with gcc this produces the expected result:


    AS_STRING (TEST_FIRST_ARG(1, 2)) = 1

    But on MS/Windows with MSVC this produces:


    AS_STRING (TEST_FIRST_ARG(1, 2)) = 1, 2

    So MSVC is evaluating the variadic macro definition in an unexpected manner. Is there a cl compiler option, or an in code pragma, to get it to expand __VA_ARGS__ and treat the result as additional parameters, if it contains commas, in the enclosing macro evaluation? If not, is there some clever way to work around this problem?

    1. Hello,
      yes MSVC is simply not complying to C99 and they don’t have the intention to be. So this has nothing to do with windows or unix as OS, but just with a compiler vendor being stubborn.

      I will study your discoveries more precisely, but the first thing that strikes me is that you are using it clearly in C++ mode. I don’t have MSVC at hand, so I would very much appreciate if you could repeat the same tests with C. These two languages are sufficiently different such that this may matter, here.

      Jens

  8. Here is the C language version of the test source code:


    /* test_VA_ARGS
    */
    #include

    int
    main ()
    {
    #define THIRD_ARG(_1, _2, _3, ...) _3
    #define TEST_THIRD_ARG(...) THIRD_ARG(__VA_ARGS__, 4, 5)
    printf ("TEST_THIRD_ARG(1) = %d\n",
    TEST_THIRD_ARG (1));
    printf ("TEST_THIRD_ARG(1, 2) = %d\n",
    TEST_THIRD_ARG (1, 2));
    printf ("TEST_THIRD_ARG(1, 2, 10, 11) = %d\n",
    TEST_THIRD_ARG (1, 2, 10, 11));
    #define FIRST_ARG(_1, _2, _3, ...) _1
    #define TEST_FIRST_ARG(...) FIRST_ARG(__VA_ARGS__, 4, 5)
    printf ("TEST_FIRST_ARG(1) = %d\n",
    TEST_FIRST_ARG(1));
    #define STRINGIFIED(...) #__VA_ARGS__
    #define AS_STRING(...) STRINGIFIED(__VA_ARGS__)
    printf ("AS_STRING (TEST_FIRST_ARG(1, 2)) = %s\n",
    AS_STRING (TEST_FIRST_ARG (1, 2)));
    }

    Output on Unix with gcc:


    TEST_THIRD_ARG(1) = 5
    TEST_THIRD_ARG(1, 2) = 4
    TEST_THIRD_ARG(1, 2, 10, 11) = 10
    TEST_FIRST_ARG(1) = 1
    AS_STRING (TEST_FIRST_ARG(1, 2)) = 1

    Output on MS/Windows with MSVC:


    TEST_THIRD_ARG(1) = 5
    TEST_THIRD_ARG(1, 2) = 5
    TEST_THIRD_ARG(1, 2, 10, 11) = 5
    TEST_FIRST_ARG(1) = 1
    AS_STRING (TEST_FIRST_ARG(1, 2)) = 1, 2

    The problem is the same as with the C++ version of the test source code. Note that the production code is C++. I would expect the same preprocessor to be used for both languages.

    I’d be happy to run any further tests with MSVC on potential solutions.

    Thnx.

    1. Ok, now I see and this explains well how MSVC is not conforming to the standard. The standard prescribes very much in detail in which order parameter replacement has to take place, here:

      • First all parameters are replaced, here e.g TEST_THIRD_ARG(1, 2) leads to THIRD_ARG(1, 2, 4, 5)
      • Then, the result of that macro expansion has to be scanned again, and now the macro THIRD_ARG sees four arguments.

      So effectively the correct answer is 4.

      And it follows that MSVC is not standard compliant for C but probably there is also the same problem for C++. The two committees tend to try to have this sort of properties in sync.

      This property of the evaluation order is deeply build into all that P99 does, so I don’t think there would be an easy way out of this. You’d somehow have to convince the compiler to expand all macro arguments once more. Adding such a thing would make the code of P99 even more terrible than it is already and in particular it would then be completely impossible to maintain.

      The only solutions I see for you are 1. complain 2. use another preprocessor 3. use another compiler. There seem to be many alternatives, even on windows. Or you could look in BOOST and see how they work around that problem.

  9. A bug report has been submitted to Microsoft.

    Using another preprocessor or compiler are not viable options for the production project that has encountered the problem.

    I’ll delve into Boost and see what I can find.

    Thnx.

  10. Hello,

    Just writing a comment to thank you for the detailed post, explaining the use of this macro. In my project I am trying to heavily utilize the preprocessor and needed a way to detect if there are no arguments to a macro or not. The macros above is really clever and had my mind blown. Will attempt to integrate it into my project immediately. Thanks!

      1. I will admit I have checked out most of P99 code and most of the functionality offered I don’t need so I just omitted it. On the other hand what I needed I took in and altered the calling code to conform with the calling convention of my project. It’s a GUI engine which at least for now does not seem to be going open-source. Might change in the future.

        So in short, basically for clarity of code and just cause I am a bit picky as to how I want my code to look I just adopted it into my project and did not use it directly.
        Of course I give due credit in all comments and whenever I release the project the P99 headers and this blog will be in the credits.

        It’s not just this NARG macro but the way that you use the preprocessor. By studying it I realized that there are so many potential and safe uses of the preprocessor that I had totally disregarded. You opened up a lot of new ideas in my mind. Again thanks!

  11. Hello!
    Thanx for the idea, I have small improvement for your realization, see:

    #define IS_EMPTY_HELPER__D4F25C4D_AE8C_4C3D_8432_248644302ABE , // comma
     
    #define IS_EMPTY( ... ) \
    \                                  
      BOOST_PP_AND( \
        /* Expands to 1 if __VA_ARGS__ contains comma itself */ \
        BOOST_PP_NOT( HAS_COMMA( __VA_ARGS__ ) ), \                              
        /* Expands to 1 if __VA_ARGS__ contains comma itself or if __VA_ARGS__ is nothing */ \
        HAS_COMMA( IS_EMPTY_HELPER_ ## __VA_ARGS__ ## _D4F25C4D_AE8C_4C3D_8432_248644302ABE ) )
    

    The main idea is the same as yours but instead of messing with macro-calls I’m using concatenation in order to detect if __VA_ARGS__ is *empty*.

    This is not only simpler but also doesn’t generates C4003 (not enough actual parameters for macro ‘identifier’) for expressions like “__VA_ARGS__ (/*empty*/)” if
    __VA_ARGS__ is equal to the name of some non variadic macro.

    1. Hi, thanks for the comment.
      Actually your solution only works with a very restricted context, namely that your argument starts and ends with a token that can be concatenated to identifier tokens.
      My solution, and all of P99 builds on that, also works if the macro argument(s) contain arbitrary token sequences.
      Jens

      1. Hello!
        Can you provide an example?
        I don’t see any problems: my compiler expands IS_EMPTY(arg) to 1 if arg expands to (event if arg is a macro or macro call). Otherwise IS_EMPTY expands to 0.

        #define F(A,B)
        IS_EMPTY( F(1,2) ) // expands to 1 since F expands to nothing
        #define G
        IS_EMPTY( G ) // expands to 1 since G expands to nothing
        #define X ,
        IS_EMPTY( X ) // expands to 0 since X expands to comma (which is not nothing)
        IS_EMPTY( "test" ) // expands to 0
        
        1. An example would be IS_EMPTY( "test" ). If I see it correctly such a call unconditionally forces the prepropressing phase to do the following operation:

           IS_EMPTY_HELPER_ ## "test" ## _D4F25C4D_AE8C_4C3D_8432_248644302ABE
          

          maybe your compiler accepts this, but this is a constraint violation. Your compiler should issue a diagnostic in the vein of

          error: pasting "IS_EMPTY_HELPER_" and ""test"" does not give a valid preprocessing token
          

          You didn’t tell me which compiler you are using, the above message is from gcc. And from all that I can read in the standard(s) this interpretation of gcc is correct.

      2. > I don’t see any problems: my compiler expands IS_EMPTY(arg) to 1 if arg expands to (event if arg is a macro or macro call). Otherwise IS_EMPTY expands to 0.
        Read as “I don’t see any problems: my compiler expands IS_EMPTY(arg) to 1 if arg expands to nothing (event if arg is a macro or macro call). Otherwise IS_EMPTY expands to 0.”

      3. I’ve read the documentation, you’re seemed to be right, but it works for me. Compiler is MSVС 2010. As you know, MSVC preprocessor does not standard-complaint and has a lot of problems…

        1. Ah, this explains it.

          But otherwise, do you have some success by using P99 with MSVC 2010? This would be the first positive report for that.

          Jens

      4. I’m actually using C++, so macros are used only if there is no other ways to generate code. Usually they are simple and BOOST::PREPROCESSOR is enough for me. As for BOOST::PREPROCESSOR, I’ve never saw any problems with it.

        Actually, with C++ I need only “preprocessing loops”, e.g. generate some code for each func argument. All other metaprograming things are done using templates magic.

  12. The two center cases of _ISEMPTY seem pointless:

    First test:

              HAS_COMMA(__VA_ARGS__)
    

    From this it is known that __VA_ARGS__ is either empty or has a single argument. I’m assuming this will be the case in all following examples:

    Second test:

              HAS_COMMA(_TRIGGER_PARENTHESIS_ __VA_ARGS__)
    

    Possibilities:
    . __VA_ARGS__ is empty: HAS_COMMA is 0
    . __VA_ARGS__ final (only) argument is (). HAS_COMMA is 1
    . __VA_ARGS__ is anything else. HAS_COMMA is 0
    Therefore, no useful information gleaned.

    Third test:

              HAS_COMMA(__VA_ARGS__ (/*empty*/)),   
    

    Possibilities:
    . __VA_ARGS__ is empty: HAS_COMMA is 0
    . __VA_ARGS__ final (only) argument is a function macro name. This is expanded, and HAS_COMMA value depends on the output
    . __VA_ARGS__ is anything else: HAS_COMMA is 0
    Therefore, once again, no useful information gleaned.

    The only important test is the fourth:

              HAS_COMMA(_TRIGGER_PARENTHESIS_ __VA_ARGS__ (/*empty*/))
    

    From which it can be discerned whether or not __VA_ARGS__ is truly empty or simply has a single argument.

    1. Nice analysis, unfortunately it is incomplete. Look at the last case

      HAS_COMMA(_TRIGGER_PARENTHESIS_ __VA_ARGS__ (/*empty*/))
      

      and let us have a look under which circumstances the inner part will expand in something that contains a comma:

      1. __VA_ARGS__ is empty, this is obviously the case that we want to detect.
      2. __VA_ARGS__ expands to something with a comma. This is captured by the first test.
      3. __VA_ARGS__ expands to “( something )“, this is discriminated by the second test, that, as you state gives us exactly that information.
      4. __VA_ARGS__ expands to a function like macro, which presented with parenthesis produces a comma. That case is captured by the third test.

      So the test 2 and 3 that you question are well necessary for a complete case analysis.

  13. Edit: From looking at the resulting examples, it seems you actually intended for function macros to be called when __VA_ARGS__ contains either a function macro name or (). Why?

    1. I am not sure that I completely understand your question. The difficulties of the implementation of that macro are actually the cases that you mention, when there are () or a function macro. I just want the macro to be able to handle these cases in addition to all the other more trivial cases that require less machinery.

    1. Looks tempting. Two arguments against its general use:

      1. I am not sure what happens with white space that is passed in the argument. I have a vague recollection that the preprocessor may or may not squeeze all white space into one blank.
      2. This expression only works after preprocessing has taken place. So in particular it doesn’t work in #if and in the other P99_IF macros that P99 has.

Comments are closed.