Comma in C/C++ Macro

Comma in C/C++ macro

Because angle brackets can also represent (or occur in) the comparison operators <, >, <= and >=, macro expansion can't ignore commas inside angle brackets like it does within parentheses. (This is also a problem for square brackets and braces, even though those usually occur as balanced pairs.) You can enclose the macro argument in parentheses:

FOO((std::map<int, int>), map_var);

The problem is then that the parameter remains parenthesized inside the macro expansion, which prevents it being read as a type in most contexts.

A nice trick to workaround this is that in C++, you can extract a typename from a parenthesized type name using a function type:

template<typename T> struct argument_type;
template<typename T, typename U> struct argument_type<T(U)> { typedef U type; };
#define FOO(t,name) argument_type<void(t)>::type name
FOO((std::map<int, int>), map_var);

Because forming function types ignores extra parentheses, you can use this macro with or without parentheses where the type name doesn't include a comma:

FOO((int), int_var);
FOO(int, int_var2);

In C, of course, this isn't necessary because type names can't contain commas outside parentheses. So, for a cross-language macro you can write:

#ifdef __cplusplus__
template<typename T> struct argument_type;
template<typename T, typename U> struct argument_type<T(U)> { typedef U type; };
#define FOO(t,name) argument_type<void(t)>::type name
#else
#define FOO(t,name) t name
#endif

comma (,) in C Macro Definition

Macros are just text replacement, so they can expand to just about anything you want. In this case, the macro is being used to expand into two arguments to a function. The function expects a string and the number of characters in the string as arguments, and the SNS() macro generates them. So

ft_display_fatal(SNS("File name missing.\n"), 2, 1)

expands into

ft_display_fatal(("File name missing.\n"),(sizeof("File name missing.\n")-1), 2, 1)

This is basically only useful when the parameter is a string literal: sizeof("string") is the size of the char array including the trailing null byte, and -1 subtracts that byte to get the number of significant characters in the string. This is the len argument to the ft_display_fatal function (I'm not sure why it can't just use strlen() to get this by itself -- I guess it's a performance optimization).

Using the comma operator when defining a macro

melpomene provided an example. Another example, although you could argue whether it's good or bad, is if you want to use the macro in a loop header.

#define MACRO(X) (X--, X>0)

int x=5;
while(MACRO(x)) {
// Do stuff
}

This example is definitely not the best in the world, but the point is that it would not work if you used semicolons instead to separate the expressions, no matter how you encapsulate them.

trailing comma in define macro

In this case it appears to simply be an array initializer list where the macro expands the list to:

{
foo_auth_detach,
0
};

where the comma is necessary to make the array initializer syntax valid. In this specific case, it didn't make much sense - LIST_ENTRY(foo), had been much clearer.

But there are other very similar cases where you wish to avoid code repetition by using "X-macros" to centralize all data to one place, and have something like this:

#define LIST \
X(foo) \
X(bar) \

...

static int (*func[])(int x) =
{
#define X(_type) _type##_auth_detach,
LIST
#undef X
0
};

which expands to

{
foo_auth_detach,
bar_auth_detach,
0
};

In this "X macro" case, the , is not only necessary to for the final 0 argument, but for every argument provided in the list.

Note that array initializer lists may end with a trailing , so 0, would have been valid. This is also true for enum lists, since C99. Macro tricks like the ones above is one reason why.

C preprocessor macro doesn't parse comma separated tokens?

It sounds to me like you're carrying over intuitions from the C language itself back to the C preprocessor, and those intuitions are biting you because the CPP doesn't work the same way. Generically in C, functions take typed values as arguments. Expressions are not typed values; they get evaluated to give those things. So what you wind up with when you chain things is a type of inner-out evaluation; and this shapes your intuitions. For example, in evaluating f(g(h(),h()),m()), f is passed two arguments, but it can't do anything with g(h(),h()); that has to be evaluated, and the result is a value, and that's the argument passed to f. Say h returns 1, m returns 7, g returns a sum, and f a product. Then g evaluates on the values 1 and 1. f evaluates on the values 2 and 7. Most of C coding uses this language, and you get used to the idea that these inner expressions evaluate, and the resulting values get passed to the functions. But that's not how macros work.

In the weird world of macro invocations (phrased carefully; I'm intentionally ignoring conditional directives), your functions don't take typed values; they take token sequences. The CPP does match parentheses for you, meaning F(()) is an invocation of F with the argument (), as opposed to an invocation with the argument ( followed by a ) token. But in macro land, F(G(H(),H()),M()) invokes F with two arguments. Argument 1 is the token sequence G(H(),H()); and argument 2 is the token sequence M(). We don't evaluate the expression G to get a typed value, because there aren't typed values; there's only token sequences.

The steps of macro invocation for a function like macro begins with (6.10.3.1) argument substitution (a.s.). During a.s., the CPP looks first at the definition of the macro being called, and notes where the macro's parameters are mentioned in its replacement list. For any such mentions that are not being stringified, and not participating in a paste, the CPP evaluates the corresponding argument, and its evaluated result replaces these qualifying mentions of the parameter in the replacement list. Next, the CPP stringifies (6.10.3.2) and pastes (6.10.3.3) in no particular order. Once all of that is done, the resulting replacement list (6.10.3.4) undergoes rescan and further replacement (r.a.f.r) where it is, as the name suggests, rescanned for further replacements; during this rescanning the particular macro is temporarily disabled ("painted blue", as per 6.10.3.4p2).

So let's walk through this; I'll ignore the fact that you're using a language extension (gcc? clang?) where you're invoking a variadic macro with an insufficient number of arguments (you're not doing that intentionally anyway). We start with:

FX()

That invokes FX, with a single argument that is an empty token list (note that to the CPP, zero arguments only make sense if you define the macro with zero parameters; F() is called with an empty argument just as F(,) is called with two empty ones). So then a.s. happens, which transforms FX's replacement list from this... to this:

RM_FIRST (FF(__VA_ARGS__) ())  => RM_FIRST (FF() ())

Skipping stringification/pastes since there are none, we then do r.a.f.r. That recognizes RM_FIRST as a macro. RM_FIRST has one argument: FF() (). So we jump into a recursion level 2... invoking RM_FIRST.

That invocation of RM_FIRST itself begins with a.s. Assuming the variadic part is treated as empty, we have the parameter x associated with FF() (), but here's where your intuition really fails. x isn't mentioned in the replacement list, so nothing happens to FF() (). That's a.s. for you. Treating as per whatever extension applies __VA_ARGS__ as if it's empty, we just get this:

__VA_ARGS__ => 

...IOW, there's nothing there any more. So we're basically done.

I'm guessing you were C-function-intuiting this; in doing so, you were expecting FF() () to evaluate, and the result to be passed into RM_FIRST as an argument, but that's not how macros evaluate.

You can, however, get that to happen with indirection. If you did this instead:

#define RM_FIRST(...) RM_FIRST_I(__VA_ARGS__)
#define RM_FIRST_I(x,...) __VA_ARGS__

...and we go back to when RM_FIRST is invoked, we have a different story. Here, FF() () is part of your variadic list, and __VA_ARGS__ is mentioned. So at that a.s. step, we would get:

RM_FIRST_I(__VA_ARGS__) => RM_FIRST_I( () () ,NULL,NULL ())

(Just being literal... I'm guessing the extra litter is part of your diagnostic; but I'm pretty sure you know where to remove the redundant ()'s). Then, during r.a.f.r., we see RM_FIRST_I being invoked, and so the story goes.

Comma in C/C++ macro passed to another macro

This solved my problem

__VA_ARGS__ expansion using MSVC

so now I am using

 #define EXPAND( x ) x
#define Log_printf( file_name,line, message,...) _snprintf_s(nullptr, 0,0,message, __VA_ARGS__)
#define Log(...) EXPAND (Log_printf(__VA_ARGS__))

C: How to Shield Commas in Macro Arguments?

Use parentheses to shield the comma, and then pass them through a special unparen macro, defined in the example below:

#include <stdio.h>

#define really_unparen(...) __VA_ARGS__
#define invoke(expr) expr
#define unparen(args) invoke(really_unparen args)


#define fancy_macro(a) printf("%s %s\n", unparen(a))

int main()
{
fancy_macro(("Hello", "World"));
}

The trick here is that the invoke macro forces an extra expansion, allowing really_unparen to be called even though it's not followed by parentheses in the source.

Edit: per comment below, this appears to not be necessary in this case. Though I'm sure I've hit a case where I needed it sometime ... and it doesn't hurt.

Macro Expansion: Argument with Commas

MSVC is non-conformant. The standard is actually clear on the point, although it does not feel the need to mention this particular case, which is not exceptional.

When a function-like macro invocation is encountered, the preprocessor:

  1. §6.10.3/11 identifies the arguments, which are possibly empty sequences of tokens separated by non-protected commas , (a comma is protected if it is inside parentheses ()).

  2. §6.10.3.1/1 does a first pass over the macro body, substituting each parameter which is not used in a # or ## operation with the corresponding fully macro-expanded argument. (It does no other substitutions in the macro body in this step.)

  3. §6.10.3.4/1 rescans the substituted replacement token sequence, performing more macro replacements as necessary.

(The above mostly ignores stringification (#) and token concatenation (##), which are not relevant to this question.)

This order of operations unambiguously leads to the behaviour expected by whoever wrote the software.

Apparently (according to @dxiv, and verified here) the following standards-compliant workaround works on some versions of MS Visual Studio:

#define CALL(A,B) A B
#define OUTER(PARAM) CALL(INNER,(PARAM))
#define INNER(A,B,C) whatever

For reference, the actual language from the C11 standard, skipping over the references to # and ## handling:

§6.10.3 11 The sequence of preprocessing tokens bounded by the outside-most matching parentheses forms the list of arguments for the function-like macro. The individual arguments within the list are separated by comma preprocessing tokens, but comma preprocessing tokens between matching inner parentheses do not separate arguments.…

§6.10.3.1 1 After the arguments for the invocation of a function-like macro have been identified, argument substitution takes place. A parameter in the replacement list… is replaced by the corresponding argument after all macros contained therein have been expanded. Before being substituted, each argument’s preprocessing tokens are completely macro replaced as if they formed the rest of the preprocessing file…

§6.10.3.4 1 After all parameters in the replacement list have been substituted… [t]he resulting preprocessing token sequence is then rescanned, along with all subsequent preprocessing tokens of the source file, for more macro names to replace.

C-preprocessor: iteratively expand macro to comma-separated list

As the comma is both important to your output and is a syntactic element, you need to make a substitute comma for outputting.

#define COMMA() ,

We will also need some deferring functions so that COMMA isn't evaluated immediately.

#define EMPTY()
#define DEFER(id) id EMPTY()

Now we can redefine your two macros into

#define CHAIN_COMMA_1(x) DEFER(COMMA)() x CHAIN_COMMA_2
#define CHAIN_COMMA_2(x) DEFER(COMMA)() x CHAIN_COMMA_1

However, your SEE macro also doesn't like the commas that are placed, and so will error for having too many parameters passed.

You can see that the macro is performing correctly by looking at the output of the preprocessor with the -E option.

Using comma as macro name in C or C++

Warning, black magic ahead.

Macros can indeed be used, albeit with a preset number of arguments. This number can be arbitrary, but each must be written by hand:

#include <stdio.h>
#include <stdlib.h>

#define MERGE_EXPAND( a , b ) a##b
#define MERGE( a , b ) MERGE_EXPAND( a , b )

#define COUNT_PICK( a , b , c , pick , ... ) pick

#define COUNT( ... ) COUNT_PICK( __VA_ARGS__ , 3 , 2 , 1 , 0 )

#define JOIN_1( a ) a
#define JOIN_2( a , b ) a##b
#define JOIN_3( a , b , c ) a##b##c

#define JOIN( ... ) MERGE( JOIN_ , COUNT( __VA_ARGS__ ) )( __VA_ARGS__ )

int main( void )
{
printf( "%d\n" , JOIN( 12345 ) ) ;
printf( "%d\n" , JOIN( 100,44 ) ) ;
printf( "%d\n" , JOIN( -10,44,9999 ) ) ;

return EXIT_SUCCESS ;
}

The macro COUNT count the number of arguments passed to it. This is done by passing arguments to the helper macro COUNT_PICK, and adding additional argument which are consecutive numbers in reverse order. The number of original arguments passed to COUNT then manipulates the arguments of COUNT_PICK, so that one of the numbers is chosen.

That chosen number is then merged wtih JOIN, resulting in either JOIN_1, JOIN_2, or JOIN_3. The chosen macro is then used with original arguments and simply merges them into a single integer literal.

This example can be expanded by manually defining more JOIN_X macros where X is a consecutive number. Simultaneously the macros COUNT and COUNT_PICK, must be altered as well.

As an additional benefit, passing invalid arguments, like:

JOIN( 10,+44 );
JOIN( 10,-44 );
JOIN( 10,*44 );
JOIN( 10,/44 );
JOIN( /10,44 );
//etc...

will yield a compile time warning, but still allows for arguments that will result in a valid integer constant.

To be used with a Microsoft compiler, tested with SVC14 (Microsoft Visual Studio 2015 Update 3), the code must be amended. Macros COUNT_PICK and MERGE must be wrapped with an additional expand macro:

#define EXPAND(...)   __VA_ARGS__


Related Topics



Leave a reply



Submit