Barnett
Barnett

Reputation: 1541

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;
}

Upvotes: 12

Views: 3807

Answers (3)

CyrIng
CyrIng

Reputation: 363

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;
}

Upvotes: 0

Wintermute
Wintermute

Reputation: 44063

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).

Upvotes: 18

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!

Upvotes: 8

Related Questions