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
.
Clever but non-standard; see C99 §6.10.3 ¶4. Referring to a macro whose identifier-list ends with an ellipsis, the standard says:
I am not sure that I completely understand what you are referring to. C and the preprocessor are two different languages.
f()
is a function call with an empty argument list.f()
is a macro call (if f is defined as such) with one argument, namely the empty token. You see that if you look atf(,)
: this has two arguments, both empty tokens.What the standard means here that a macro
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.)
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.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:
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
So the empty token counts as a parameter? Cool—I had not realized that.
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 distinguishf()
fromf(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 😉
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
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:Well done. You’re real expert in PP.
Check out my lib that heavily uses PP.
https://github.com/alexanderchuranov/Metaresc
This is an implementation of reflection mechanism in C. Definition of structures and other metadata in one one place.
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.
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.
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?
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.
Darn. Alright. Thank you! If I happen to figure anything out, I’ll be sure to post back here.
In the mean time I found a bug report and a workaround for this bug when occurring
VisualStudion C++. My guess would be that for their C implementation this is the same.
#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 ).
Your assertion is incorrect.
ISEMPTY(AMACRO)
gives the correct result, namely1
.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.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?
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
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.
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:
TEST_THIRD_ARG(1, 2)
leads toTHIRD_ARG(1, 2, 4, 5)
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.
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.
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!
Thanks for the feedback.
Is there anything I could do to convince you to use P99 directly?
Jens
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!
Hello!
Thanx for the idea, I have small improvement for your realization, see:
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.
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
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.
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:maybe your compiler accepts this, but this is a constraint violation. Your compiler should issue a diagnostic in the vein of
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.
> 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.”
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…
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
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.
The two center cases of _ISEMPTY seem pointless:
First test:
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:
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:
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:
From which it can be discerned whether or not __VA_ARGS__ is truly empty or simply has a single argument.
Nice analysis, unfortunately it is incomplete. Look at the last case
and let us have a look under which circumstances the inner part will expand in something that contains a comma:
__VA_ARGS__
is empty, this is obviously the case that we want to detect.__VA_ARGS__
expands to something with a comma. This is captured by the first test.__VA_ARGS__
expands to “( something )
“, this is discriminated by the second test, that, as you state gives us exactly that information.__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.
I hadn’t thought of that. Very nice!
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?
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.
#define ISEMPTY(…) (sizeof(#__VA_ARGS__) == 1)
Looks tempting. Two arguments against its general use:
#if
and in the otherP99_IF
macros that P99 has.