Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c++

c++11

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;
}
like image 764
Barnett Avatar asked Apr 11 '15 14:04

Barnett


People also ask

What is brace initialization?

If a type has a default constructor, either implicitly or explicitly declared, you can use brace initialization with empty braces to invoke it. For example, the following class may be initialized by using both empty and non-empty brace initialization: C++ Copy.

What is uniform initialization in C++?

Uniform initialization is a feature in C++ 11 that allows the usage of a consistent syntax to initialize variables and objects ranging from primitive type to aggregates. In other words, it introduces brace-initialization that uses braces ({}) to enclose initializer values.


2 Answers

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!

like image 174
cmaster - reinstate monica Avatar answered Oct 27 '22 01:10

cmaster - reinstate monica


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

like image 25
Wintermute Avatar answered Oct 26 '22 23:10

Wintermute