Trouble with template parameters used in macros
I'm trying to compile the following piece of code, I get an error on the line which specializes std::vector, it seems the one parameter being passed-in is somehow being assumed to be two parameters. Is it perhaps something to do with angle-brackets?
Is there a special way/mechanism where by such parameters can be correctly passed to the macro?
#include <vector>
template<typename A>
struct AClass {};
#define specialize_AClass(X)\
template<> struct AClass<X> { X a; };
specialize_AClass(int) //ok
specialize_AClass(std::vector<int,std::allocator<int> >) //error
int main()
{
return 0;
}
The error tha开发者_Go百科t I get is as follows:
1 Line 55: error: macro "specialize_AClass" passed 2 arguments, but takes just 1
2 Line 15: error: expected constructor, destructor, or type conversion before 'int'
3 compilation terminated due to -Wfatal-errors.
Link: http://codepad.org/qIiKsw4l
template<typename TypeX, typename TypeY>
class Test
{
public:
void fun(TypeX x, TypeY y)
{
std::wcout << _T("Hello") << std::endl;
std::wcout << x << std::endl;
std::wcout << y << std::endl;
}
};
#define COMMOA ,
#define KK(x) x val;
void main()
{
KK(Test<int COMMOA int>);
val.fun(12, 13);
}
I have a new way to solve this trouble. hope it can help you :)
You have two options. One of which was mentioned already: Using __VA_ARGS__
. This however has the disadvantage that it doesn't work in strict C++03, but requires a sufficiently C99/C++0x compatible preprocessor.
The other option is to parenthesize the type-name. But unlike another answer claims, it's not as easy as just parenthesizing the type name. Writing a specialization as follows is ill-formed
// error, NOT valid!
template<> struct AClass<(int)> { X a; };
I have worked around this (and boost probably uses the same under the hood) by passing the type name in parentheses, and then building up a function type out of it
template<typename T> struct get_first_param;
template<typename R, typename P1> struct get_first_param<R(P1)> {
typedef P1 type;
};
With that, get_first_param<void(X)>::type
denotes the type X
. Now you can rewrite your macro to
#define specialize_AClass(X) \
template<> struct AClass<get_first_param<void X>::type> {
get_first_param<void X>::type a;
};
And you just need to pass the type wrapped in parentheses.
There is a couple of issues here.
First of all, macros are extremely dumb, they're complicated, but essentially amounts to a pure text replacement processus.
There are therefore 2 (technical) issues with the code you exposed:
- You cannot use a comma in the middle of a macro invocation, it just fails,
BOOST_FOREACH
is a well-known library and yet the only thing they could do was to told the user that it's arguments should not contain commas, unless they could be wrapped in parenthesis, which is not always the case - Even if the replacement occurred, your code would fail in C++03, because it would create a
>>
symbol at the end of the template specialization, which would not be parsed correctly.
There are preprocessing / template metaprogramming tricks, however the simpler solution is to use a type without commas:
typedef std::vector<int, std::allocator<int> > FooVector;
specialize_AClass(FooVector)
Finally, there is an aesthetic issue, because of their pervasiveness, macros are best given names that cannot possibly clash with "regular" (types, functions, variables) names. The consensus is usually to use all upper case identifiers, like in:
SPECIALIZE_ACLASS
Note that this cannot begin by an underscore, because the standard restricts the use of identifiers matching _[A-Z].*
or [^_]*__.*
to the compiler writers for the standard library or whatever they feel like (those are not smileys :p)
Since the preprocessor runs before semantic analysis, the comma in your template parameter is being interpreted as the argument separator for the macro. Instead, you should be able to use variadic macros to do something like this:
#define specialize_AClass(...)\
template<> struct AClass< __VA_ARGS__ > { X a; };
If you are willing to add a little more code before calling your macro, you could always do this as a workaround:
typedef std::vector<int,std::allocator<int> > myTypeDef;
specialize_AClass(myTypeDef) //works
#define EMPTY()
#define DEFER( ... ) __VA_ARGS__ EMPTY()
specialize_AClass( DEFER (std::vector<int,std::allocator<int> >) )
For simple things you can use typedef
#include <vector>
template<typename A>
struct AClass {};
#define specialize_AClass(X)\
template<> struct AClass<X> { X a; };
specialize_AClass(int) //ok
typedef std::vector<int,std::allocator<int>> AllocsVector;
specialize_AClass(AllocsVector) //ok
int main()
{
return 0;
}
There are lots of other problems with your code, but to address the specific question, the preprocessor just treats <
and >
as less-than and greater-than operators.
That's the extent of its knowledge about C++.
There are some tricks that can be used to allow template expressions to be passed as macro arguments, but the simple and by an extremely large margin best answer for a beginner is:
DON'T DO THAT.
Cheers & hth.,
精彩评论