说我有这样的宏:
#define SET_TYPE_NAME(TYPE, NAME) \
template<typename T> \
std::string name(); \
\
template<> \
std::string name<TYPE>() { \
return NAME; \
}
如果我传递一个包含多个参数的模板,这将无效,因为<int, int>
中的逗号被解释为分隔宏参数,而不是模板参数。
SET_TYPE_NAME(std::map<int, int>, "TheMap")
// Error: macro expects two arguments, three given
这个问题似乎可以解决这个问题:
SET_TYPE_NAME((std::map<int, int>), "TheMap")
但是现在出现了另一个问题,我真的没想到这个问题:
template<>
std::string name<(std::map<int, int>)>()
// template argument 1 is invalid
似乎额外的括号使模板参数无效。有没有办法解决这个问题?
答案 0 :(得分:9)
除typedef
之外,您可以切换参数的顺序并使用可变参数宏(需要C99或C ++ 11兼容的编译器):
#define SET_TYPE_NAME(NAME, ...) \
template<typename T> \
std::string name(); \
\
template<> \
std::string name<__VA_ARGS__>() { \
return NAME; \
}
...
SET_TYPE_NAME("TheMap", std::map<int, int>)
答案 1 :(得分:8)
您可以使用typedef
:
typedef std::map<int, int> int_map;
SET_TYPE_NAME(int_map, "TheMap");
提升BOOST_FOREACH遭遇同样的问题。
答案 2 :(得分:6)
前一段时间(在网上搜索Identity<T>
的实用程序时),我得到this page。
简而言之,要回答这个问题,如果你没有C ++ 11支持和/或不能(或者不想)使用typedef
,那么你可以调用你的宏“正确的“方式:
// If an argument contains commas, enclose it in parentheses:
SET_TYPE_NAME((std::map<int, int>), "TheMap")
// For an argument that doesn't contain commas, both should work:
SET_TYPE_NAME((SomeType1), "TheType1")
SET_TYPE_NAME(SomeType2, "TheType2")
然后摆脱类型周围的(可能的)不需要的括号,你可以使用这样的“帮手”:
template<typename> struct RemoveBrackets;
template<typename T> struct RemoveBrackets<void (T)> {
typedef T Type;
};
并在您的宏中更改行:
std::string name<TYPE>() { \
为:
std::string name< RemoveBrackets<void (TYPE)>::Type >() { \
(或定义辅助宏,比如说
#define REMOVE_BRACKETS(x) RemoveBrackets<void (x)>::Type
然后用
替换该行 std::string name< REMOVE_BRACKETS(TYPE) >() { \
)。
(有关完整的故事,请阅读上文链接的文章末尾的“更好的解决方案”。)
编辑:刚刚找到that。但它确实应该使用<void X>
<void (X)>
时使用get_first_param<void X>::type
;如果你传递一个“简单的”非括号的参数(如上面代码中的SomeType2
),那么括号确实是 - 如果X
它们不会受到伤害已被括起来(例如,void ((SomeType1))
相当于void (SomeType1)
;再次,请参阅文章)。 (顺便说一下,我注意到另一个SO页面上的很多答案实质上是“宏都很愚蠢”。但我不会发表评论。)
答案 3 :(得分:4)
我想为自己的问题添加一个答案。现在Boost 1.50.0已经发布,Boost.Utility有一个新的迷你库可以帮助解决这类问题。如果您查看源代码,您会看到它的实现方式与gx_'s solution相同。以下是如何使用它:
#include <boost/utility/identity_type.hpp>
SET_TYPE_NAME(BOOST_IDENTITY_TYPE((std::map<int, int>)), "TheMap");
答案 4 :(得分:3)
我喜欢hmjd提出的typedef方式更好,但是对于记录,我看到的常用方法是将尖括号从宏中踢出来并写下:
#define SET_TYPE_NAME(TYPE, NAME) \
template<typename T> \
std::string name(); \
\
template<> \
std::string name TYPE() { \
return NAME; \
}
用法:
SET_TYPE_NAME(<std::map<int, int> >, "TheMap")
这是用于错误消息报告和fprintf
的旧技术的变体:
#define Error(args) do { \
printf("ERROR: "); \
printf args; \
printf("\n"); \
return 1; \
} while(0)
跟:
Error(("Index out of range: %d not in %d ... %d.", var, min, max));
这很难看,但确实有效。如果编码样式规则禁止typedef
,则很有用。
答案 5 :(得分:1)
typedef std::map<int,int> IntMap_t;
SET_TYPE_NAME(IntMap_t, "TheMap")
您可以声明一个typedef并在宏
中使用它答案 6 :(得分:1)
更通用的方法是始终在可能包含逗号的参数周围使用()并使用CONCAT删除括号。如果您这样做,您可以定义多个参数包,这些参数包里面有逗号,或者以您喜欢的顺序放置参数
#ifndef CONCAT
#define CONCAT __VA_ARGS__
#endif
#define MYMACRO(tparam,classname)\
template < CONCAT tparam >\
class CONCAT classname {};
//create a template class X<T,U>
MYMACRO( (typename T,typename U) , (X<T,U>) )