8

Consider a simple macro:

#define ECHO(x) x

ECHO(foo(1, 2))

This produces the exact output we expect:

foo(1, 2)

The above example works because the parenthesis adjacent to the function call are recognized by the preprocessor.

Now consider what happens if I use a template instead of a function call:

ECHO(template<int, bool>)

This causes an error because the preprocessor interprets the template<int and the bool> as two separate arguments to the macro. The preprocessor doesn't recognize <> for scope!

Is there anyway to use a template like this in a macro?

chrisaycock
  • 36,470
  • 14
  • 88
  • 125
  • possible duplicate of [C++ macro problem (interpretation of comma ,)](http://stackoverflow.com/questions/5348077/c-macro-problem-interpretation-of-comma) – Oliver Charlesworth Feb 23 '12 at 16:06
  • 1
    @OliCharlesworth Ah, I didn't find that one in my search. The answer there doesn't seem to answer my exact issue, though it does provide some food for thought. Thanks. – chrisaycock Feb 23 '12 at 16:09
  • A newer question has some excellent answers: http://stackoverflow.com/questions/13842468/comma-in-c-c-macro/13842784#13842784 – jjrv Jul 17 '14 at 05:35

3 Answers3

11
#define COMMA ,
ECHO(template<int COMMA bool>)

A little painful, but it works.

FWIW, if the syntax for the argument allows ()s, you don't need the substitution, e.g.,

ECHO((a, b))

will work for a single argument macro but that doesn't work in all cases (including yours).

smparkes
  • 13,807
  • 4
  • 36
  • 61
  • 2
    Note that you have `BOOST_PP_COMMA` if you can include `"boost/preprocessor/punctuation/comma.hpp"`. http://www.boost.org/doc/libs/1_31_0/libs/preprocessor/doc/ref/comma.html – alfC Dec 13 '12 at 05:40
5

A variadic macro may help:

#define ECHO(x...) x

ECHO(foo(1, 2))
ECHO(template<int, bool>)
Yan
  • 886
  • 7
  • 13
  • 2
    Note that this uses a GNU extension. The standard (C++11) way would be `#define ECHO(...) __VA_ARGS__`. – a3f Dec 25 '15 at 19:27
1

if you allow usage of Cog in your project, your code becomes more readable:

/*[[[cog
# definitions ----
import cog

def AddDeclaration( templateArg , restOfDeclaration ):
    cog.outl(' %s struct %s; ' % (templateArg , restOfDeclaration))

# generation ---
types = ['bool' , 'std::string']
names = ['Foo' , 'Bar']
for index in range(len(names)):
    AddDeclaration( 'template<int, %s>' % types[index] , names[index])
]]]*/
//[[[end]]]

After you run cog over this file, you'll get:

/*[[[cog
# definitions ----
import cog

def AddDeclaration( templateArg , restOfDeclaration ):
    cog.outl(' %s struct %s; ' % (templateArg , restOfDeclaration))

# generation ---
types = ['bool' , 'std::string']
names = ['Foo' , 'Bar']
for index in range(len(names)):
    AddDeclaration( 'template<int, %s>' % types[index] , names[index])
]]]*/
template<int, bool> struct Foo; <---------------- generated by Cog!!
template<int, std::string> struct Bar;
//[[[end]]]

You can even move your definitions in separate .py files and the cog section will look like:


declarations.py

import cog

def AddDeclaration( templateArg , restOfDeclaration ):
    cog.outl(' %s struct %s; ' % (templateArg , restOfDeclaration))

my.h

/*[[[cog
# definitions ----
import declarations
# generation ---
types = ['bool' , 'std::string']
names = ['Foo' , 'Bar']
for index in range(len(names)):
    AddDeclaration( 'template<int, %s>' % types[index] , names[index])
]]]*/
template<int, bool> struct Foo;
template<int, std::string> struct Bar;
//[[[end]]]

the main advantage of using cog is that you gain complete control of your code generation, completely avoid unreadable messes using boost preprocessor or stuff like that.

the main disadvantage is that you add a new tool dependency to your project, also since you need to wrap its usage in comments with the cog section markers, it might actually be worse than writing the code manually for small usages. It really pays off when you need to declare big enums or lot of unavoidable boilerplate code

lurscher
  • 25,930
  • 29
  • 122
  • 185