【问题标题】:Macro compiles with GCC but not with VS11宏可以用 GCC 编译,但不能用 VS11
【发布时间】:2013-12-01 15:31:46
【问题描述】:

我编写了以下代码来帮助将我的模板函数限制为某些类型,并在使用其他类型时显示有意义的错误消息。我从 stackoverflow 中的另一个问题中得到了这个想法,我仍然无法对此发表评论,因为我是新来的。

该宏在 linux GCC 下可以完美编译,但在 Visual Studio 2012 下无法编译。

#include <string>
#include <iostream>
#include <vector>
#include <cassert>
#include <type_traits>

#define ISALLOWED(DerivedT) (std::is_same<T, DerivedT>::value)||(std::is_base_of<T,DerivedT>::value)
#define FE_1(WHAT, X) WHAT(X)
#define FE_2(WHAT, X, ...) WHAT(X) || FE_1(WHAT, __VA_ARGS__)
#define FE_3(WHAT, X, ...) WHAT(X) || FE_2(WHAT, __VA_ARGS__)
#define FE_4(WHAT, X, ...) WHAT(X) || FE_3(WHAT, __VA_ARGS__)
#define FE_5(WHAT, X, ...) WHAT(X) || FE_4(WHAT, __VA_ARGS__)
#define FE_6(WHAT, X, ...) WHAT(X) || FE_5(WHAT, __VA_ARGS__)
//... repeat as needed

#define GET_MACRO(_1,_2,_3,_4,_5,_6,NAME,...) NAME
#define FOR_EACH(action,...) \
  GET_MACRO(__VA_ARGS__,FE_6,FE_5,FE_4,FE_3,FE_2,FE_1)(action,__VA_ARGS__)

// this is where you need to add types
#define ASSERTIOTYPES \
    static_assert(FOR_EACH(ISALLOWED,\
            int,double,std::string\
            ),"Type not defined for this template.");

template<class T> std::ostream & operator<<(std::ostream &os,
        const std::vector<T> & v) {
    ASSERTIOTYPES;
    os << "[";
    for (size_t i = 0; i < v.size(); i++) {
        os << v[i];
        if (i != v.size() - 1)
            os << ", ";
    }
    os << "]";
    return os;
}

错误信息是: 错误 C2977:“std::is_same”:模板参数太多

只有当我用一种以上的类型定义 ASSERTIOTYPES 时,它才会出现,但是当它只用一种类型定义时,例如:

#define ASSERTIOTYPES \
    static_assert(FOR_EACH(ISALLOWED,\
            int\
            ),"Type not defined for this template.");

...代码编译正常。

知道如何解决这个问题吗?

【问题讨论】:

  • 做高级预处理器的人认为 VS 预处理器严重损坏。如果可能的话,我建议你使用Boost.Preprocessor 而不是手写宏——它们涵盖了这些特性。
  • 逗号分隔的标记序列扩展得太晚,导致宏替换期间的参数-参数匹配不正确。这个问题在__VA_ARGS__ 的常见用法中尤其严重(比如这个)。您可以使用间接来解决错误行为。参见例如我展示的示例in an answer to another question。也就是说,Yakk 的解决方案是正确的解决方案;比使用宏更干净。

标签: c++ gcc visual-studio-2012 c++11 macros


【解决方案1】:
template<typename T> struct is_io_type : std::false_type {};
template<> struct is_io_type<int> : std::true_type {};
template<> struct is_io_type<double> : std::true_type {};
template<> struct is_io_type<std::string> : std::true_type {};

然后,不用你的宏,只需输入:

static_assert( is_io_type<T>::value, "Type not defined for this template." )

这差不多是冗长的,而不是几乎那么模糊或难以弄清楚到底发生了什么。

现在,假设我们真的需要is_base_of 功能。

template<typename T,typename=void> struct is_io_type : std::false_type {};
template<> struct is_io_type<int,void> : std::true_type {};
template<typename T> struct is_io_type<T,typename std::enable_if<std::is_base_of<int, T>::type> : std::true_type {};
template<> struct is_io_type<double,void> : std::true_type {};
template<typename T> struct is_io_type<T,typename std::enable_if<std::is_base_of<double, T>::type> : std::true_type {};
template<> struct is_io_type<std::string,void> : std::true_type {};
template<typename T> struct is_io_type<T,typename std::enable_if<std::is_base_of<std::string, T>::type> : std::true_type {};

这变得非常冗长。

但不是在我们使用宏的地方创建可变宏扩展,我们可以使用宏来构建上述类型特征特化。

所以你首先创建你的类型特征,然后扩展它:

CREATE_TYPE_CATEGORY( is_io_type );
ADD_BASE_TYPE_CATEGORY( is_io_type, int );
ADD_BASE_TYPE_CATEGORY( is_io_type, double );
ADD_BASE_TYPE_CATEGORY( is_io_type, std::string );

这将使上面的代码写得不那么冗长。它还支持分布式 trait 修改:如果有人创建了一个应该是 is_io_type 的新类型,他们可以在引入类型后使用上述宏,并且它(及其后代)成为一个 io 类型。

【讨论】:

  • +1 Traits 是一个 PITA,但它是最清晰、最容易阅读/扩展的东西。对于您的宏修复,您始终可以编写一个宏来获取类型列表并为您编写模板专业化。
【解决方案2】:

从你描述的问题来看,很可能是VA_ARGS

的bug

我建议您要么使用静态内联,要么您可以编写一个简单的测试。复制上面的大部分代码并检查 GET_MACRO 给你什么。然后您可以发布结果,这将有助于进一步诊断。

【讨论】:

    猜你喜欢
    • 2013-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-10
    • 2020-03-05
    • 1970-01-01
    • 2018-05-21
    相关资源
    最近更新 更多