【问题标题】:Integrate type name in static_assert output?在 static_assert 输出中集成类型名称?
【发布时间】:2011-09-18 21:37:36
【问题描述】:

我喜欢提供有用的错误/消息,我也想为我的static_asserts 这样做。问题是,它们依赖于模板参数。通常,由于引发的错误,这些参数将在途中或其他地方显示,但它们要么是模糊的,要么没有分组,因此它们是有意义的。示例:

template<class T>
struct fake_dependency{
  static bool const value = false;
};

template<class T, class Tag>
struct Foo{
  Foo(){}

  template<class OtherTag>
  Foo(Foo<T, OtherTag> const&){
    static_assert(fake_dependency<T>::value, "Cannot create Foo<T,Tag> from Foo<T,OtherTag>.");
  }
};

int main(){
    Foo<int, struct TagA> fA;
    Foo<int, struct TagB> fB(fA);
}

MSVC 上的输出:

src\main.cpp(74): error C2338: Cannot create Foo<T,Tag> from Foo<T,OtherTag>.
          src\main.cpp(84) : see reference to function template instantiation 'Foo<T,Tag>::Foo<main::TagA>(const Foo<T,main::TagA> &)' being compiled
          with
          [
              T=int,
              Tag=main::TagB
          ]

一个标签在函数模板本身中被提及,另一个在下面的类模板中。不太好。让我们看看GCC outputs

prog.cpp: In constructor 'Foo<T, Tag>::Foo(const Foo<T, OtherTag>&) [with OtherTag = main()::TagA, T = int, Tag = main()::TagB]':
prog.cpp:18:32:   instantiated from here
prog.cpp:12:5: error: static assertion failed: "Cannot create Foo<T,Tag> from Foo<T,OtherTag>."

好多了,但仍然不是static_assert 所在的位置。现在想象一些更多的参数,或更多的模板,或两者兼而有之。 颤抖

解决这个问题的一种方法是使用中间结构,它将两个标签都作为模板参数:

template<class Tag, class OtherTag>
struct static_Foo_assert{
    static_assert(fake_dependency<Tag>::value, "Cannot create Foo<T,Tag> from Foo<T,OtherTag>.");
};

template<class T, class Tag>
struct Foo{
  Foo(){}

  template<class OtherTag>
  Foo(Foo<T, OtherTag> const&){
      static_Foo_assert<Tag, OtherTag> x;
  }
};

现在让我们再次查看输出:

src\main.cpp(70): error C2338: Cannot create Foo<T,Tag> from Foo<T,OtherTag>.
          src\main.cpp(79) : see reference to class template instantiation 'static_Foo_assert<Tag,OtherTag>' being compiled
          with
          [
              Tag=main::TagB,
              OtherTag=main::TagA
          ]

好多了!这是GCC says

prog.cpp: In instantiation of 'static_Foo_assert<main()::TagB, main()::TagA>':
prog.cpp:17:40:   instantiated from 'Foo<T, Tag>::Foo(const Foo<T, OtherTag>&) [with OtherTag = main()::TagA, T = int, Tag = main()::TagB]'
prog.cpp:23:32:   instantiated from here
prog.cpp:8:5: error: static assertion failed: "Cannot create Foo<T,Tag> from Foo<T,OtherTag>."

看起来不错。问题:我需要为每个模板创建这样一个结构,因为static_assert 中的错误消息需要是字符串文字...

现在,对于我的问题:我们能否以某种方式将类型名称直接包含到 static_assert 中?喜欢

static_assert(..., "Cannot create Foo<" T "," Tag "> from Foo<" T "," OtherTag ">.");

示例输出:

无法从Foo&lt;int,main::TagB&gt; 创建Foo&lt;int,main::TagA&gt;

或者,如果这无法实现,我们能否以某种方式使错误消息成为一个额外的模板参数,以使其可以通过?

【问题讨论】:

  • 我希望看到编译器在这里变得更好。必须可以显示失败的条件。它可以说note: in static_assert check for fake_dependency&lt;T&gt;::value [with T = ...](在括号中,它枚举了表达式中使用的所有模板参数)。让我们充满希望!
  • @Johannes:我认为这可以用constexpr 表达式模板来完成,你不觉得吗?就像您已经可以像 Check 单元测试框架那样反汇编运行时表达式/条件。
  • 太糟糕的概念没有融入 C++0x,这将大大减少需求,因为它代表 typeiddyanamic_cast 是确定类型的唯一方法,并且都需要一个实例没有模板参数。
  • @AJ:实际上,typeid 运算符仅对类型完全有效:例如 typeid(int)。但问题是,static_assert 想要一个文字字符串。 :(
  • 你提前知道你所有的类型吗?如果是这样,您可以为每种类型创建一个带有特化的错误模板。

标签: c++ templates c++11 custom-errors static-assert


【解决方案1】:

我的黑客

代码:

template <typename Assertion>
struct AssertValue : AssertionChecker<Assertion::value, Assertion>
{
    static_assert(AssertionValue, "Assertion failed <see below for more information>");
    static bool const value = Assertion::value;
};

它允许您检查任何::value 断言并在失败时转储类型。

用法:

// Bad indentation used to show parts
static_assert(
    AssertValue<
        std::my_check<
            T0, decltype(*somethingComplicated), T7::value_type
        >
    >, 
    "something horrible happened"
);

其中std::my_check&lt;...&gt;::value 是检查的布尔结果

示例

如需完整的SSCCE 示例,请参阅:IDEOne Example

示例的错误信息:

prog.cpp: In instantiation of 'AssertValue<std::is_base_of<IMyInterface, MyBadType> >':
prog.cpp:37:69:   instantiated from 'void MyFunction(IteratorType, IteratorType) [with IteratorType = __gnu_cxx::__normal_iterator<MyBadType*, std::vector<MyBadType> >]'
prog.cpp:60:38:   instantiated from here
prog.cpp:9:5: error: static assertion failed: "Assertion failed <see below for more information>"
prog.cpp: In function 'void MyFunction(IteratorType, IteratorType) [with IteratorType = __gnu_cxx::__normal_iterator<MyBadType*, std::vector<MyBadType> >]':
prog.cpp:60:38:   instantiated from here
prog.cpp:39:5: error: static assertion failed: "iterator passed does not reference IMyInterface items"

说明

如果断言失败,它将打印 AssertValue 的模板参数,从而打印检查的完整模板扩展。例如,如果您正在检查std::is_base_of,它将打印检查的完整类型,例如:std::is_base_of&lt;IMyInterface, MyBadType&gt;。然后你就知道在失败的断言中使用了哪些类型。

唯一的问题是这仅适用于将其结果放入::value 的模板。但是type_traits 主要使用这个并且是 goto 标准。

【讨论】:

  • 没有我想要的那么好,但也不错。从 GCC 和 MSVC 错误来看,我认为您的意思是 &lt;see above ...
  • @Xeo 我确实在下面看到了,因为我认为 MSVC 在转储类型之前将失败的断言放在首位...... GCC 则相反
  • Ideone 示例不再存在...ideone.com/AcwsCA 响应代码不完整,缺少 AssertionChecker
  • AssertionValue 定义在哪里?
【解决方案2】:

可以获得作为模板非类型参数传入的字符串文字,带有一点hoop-jumping。但由于static_assert 的第二个参数被限制为字符串文字,而不是地址常量表达式,不幸的是,这并没有多大用处。

遗憾的是,我怀疑您最好的选择是游说委员会或编译器编写者来扩展该功能。

【讨论】:

    【解决方案3】:

    如果您的编译器提供了 __FUNCTION__ 宏,您可以使用它和文字连接进行非常简单的替换。但是 gcc 和 clang 的实现不是作为宏来完成的,所以这个解决方案对它们不起作用。

    #include "stdafx.h"
    #include <type_traits>
    
    template <class T>
    class must_be_pod
    {
        static void test() { static_assert (std::is_pod<T>::value, __FUNCTION__ ": not a POD"); }
    public:
        must_be_pod() { test(); }
    };
    
    class not_a_pod
    {
    public:
        not_a_pod() {}
        virtual ~not_a_pod() {}
    };
    
    int main()
    {
        must_be_pod<not_a_pod> should_fail; // and it does
        return 0;
    }
    

    这在 VS2015 编译时会产生以下输出:

    static_assert_test.cpp(10): error C2338: must_be_pod<class not_a_pod>::test: not a POD
    

    【讨论】:

    • 这不适用于 GCC 或 Clang,因为它们没有 __FUNCTION__ 作为宏。
    • 另外,虽然 Embarcadero C++ 有 __FUNCTION__ 宏,但它不会让您 (10.1) 将它用作 static_assert 中的消息。
    • 其实根据gcc.gnu.org/onlinedocs/gcc/Function-Names.htmlgcc确实支持__FUNCTION__以及__PRETTY_FUNCTION__
    • 但它们不是宏,__FUNCTION___ 是 MSVC 中的一个宏——它在编译时被字符串文字替换,这就是它的工作原理。
    【解决方案4】:

    我看到这个问题已经回答了一段时间,但完整的答案丢失了,我找到了一种非常简单的方法来达到预期的结果。

    template <typename T, bool value>
    static typename std::enable_if<value, void>::type FunctionWithReadableErrorMessage()
    {
    }
    
    
    int  main()
    {
        FunctionWithReadableErrorMessage<int, false>();
        return 0;
    }
    

    如果value=true,这个函数将编译并且没有效果,否则我们会得到这个错误信息:

    main.cpp:在函数“int main()”中:main.cpp:16:50:错误:没有匹配 调用“FunctionWithReadableErrorMessage()”的函数 FunctionWithReadableErrorMessage(); ^

    如果我们想要更通用一点,我们可以把它放在一个宏中

    【讨论】:

      【解决方案5】:

      std::type_info 有一个成员const char* name()

      #include <typeinfo>
      using namespace std;
      
      //...
      
      const char* name = type_info(T).name();
      

      【讨论】:

      • 不是字符串文字,因此不能在static_assert 中使用。此外,“名称”取决于编译器和/或平台。 ://
      猜你喜欢
      • 1970-01-01
      • 2019-05-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多