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?
#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).
A variadic macro may help:
#define ECHO(x...) x
ECHO(foo(1, 2))
ECHO(template<int, bool>)
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