简体   繁体   中英

Why can't you use C++11 brace initialization with macros?

I just discovered that you cannot always use brace initialization when passing arguments to macros. I found this when an ASSERT() macro failed to compile. However, the following example illustrates the problem:

#include <iostream>
#include <string>
using namespace std;

#define PRINT_SIZE( f ) cout << "Size=" << (f).size() << endl;

int main()
{
  PRINT_SIZE( string("ABC") );  // OK, prints: "Size=3"
  PRINT_SIZE( string{"ABC"} );  // OK, prints: "Size=3"

  PRINT_SIZE( string("ABCDEF",3) ); // OK, prints: "Size=3"
  PRINT_SIZE( string{"ABCDEF",3} ); // Error: macro 'PRINT_SIZE' passed 2 arguments, but takes just 1

   return 0;
}

Is there a reason why macros cannot be made to work with brace initialization?

Edit:

I have since discovered that you can also use a variadic macro, and that solves the problem perfectly:

#include <iostream>
#include <string>
using namespace std;

#define PRINT_SIZE( ... ) cout << "Size=" << (__VA_ARGS__).size() << endl;

int main()
{
  PRINT_SIZE( string("ABC") );  // OK, prints: "Size=3"
  PRINT_SIZE( string{"ABC"} );  // OK, prints: "Size=3"

  PRINT_SIZE( string("ABCDEF",3) ); // OK, prints: "Size=3"
  PRINT_SIZE( string{"ABCDEF",3} ); // OK, prints: "Size=3"

  return 0;
}

The list is split into several macro parameters. When you write

PRINT_SIZE( string{"ABCDEF",3} );

This attempts to expand the macro PRINT_SIZE with two parameters, one string{"ABCDEF" and one 3} , which fails. This can be worked around in many cases (including yours) by adding another pair of parentheses:

PRINT_SIZE( (string{"ABCDEF",3}) );

These parentheses prevent the splitting of the argument, so that PRINT_SIZE is expanded with a single argument (string{"ABCDEF",3}) (note that the parentheses are part of the argument).

Yes, there is a reason: The preprocessor is not aware of braces. It only respects string literals and parentheses, to other C/C++ language structures it is ignorant. As such, the call

PRINT_SIZE( string{"ABCDEF",3} );

is parsed as a macro invocation with two parameters string{"ABCDEF" and 3} . Since the macro PRINT_SIZE() expects only one parameter, the preprocessor bails out. Note that the C++ compiler has not even been invoked at this point!

What about this ?

#define SOME_BRACE  \
{                   \
    6, 8, 1, 3, 7,  \
    1, 4, 2, 0, 9   \
}

#define BRACE_SIZE(brace) (sizeof((int[]) SOME_BRACE) / sizeof(int))

int main(int argc, char *argv[])
{
    int array[BRACE_SIZE(SOME_BRACE)] = SOME_BRACE, i, s;

    for (i = 0, s = 0; i < BRACE_SIZE(SOME_BRACE); i++) {
        s = s + array[i];
    }
    /* 41 is expected */
    return s;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM