如何将多参数模板传递给宏?

时间:2012-01-20 14:30:45

标签: c++ templates macros

说我有这样的宏:

#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

似乎额外的括号使模板参数无效。有没有办法解决这个问题?

7 个答案:

答案 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>) )