在 static_assert 输出中集成类型名称?

发布于 11-16 02:30 字数 3303 浏览 4 评论 0原文

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

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 输出

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 所说的

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 创建 Foo

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

I like to give helpful errors / messages, and I also want to do so for my static_asserts. The problem is, that they depend on template parameters. Normally, those parameters will get displayed on way or an other due to the error raised, but they are either obscure or not grouped so they make sense. Example:

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);
}

Output on 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
          ]

One tag is mentioned in the function template itself, the other below with the class template. Not so nice. Lets see what 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>."

Much better, but still not really where the static_assert is. And now imagine some more parameters, or more templates, or both. shivers

One way to work around that is to use an intermediate struct, which takes both Tags as template parameters:

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;
  }
};

Now lets see the output again:

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
          ]

Much better! Here's what 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>."

Looks not bad. The problem: I need to create such a struct for every template, since the error message in static_assert needs to be a string literal...

Now, for my question: Can we somehow include the type names directly into the static_assert? Like

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

Example output:

Cannot create Foo<int,main::TagA> from Foo<int,main::TagB>.

Or, if that isn't achievable, can we somehow make the error message an extra template parameter, as to make it passable?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

把梦留给海 2024-11-23 02:30:07

我的黑客

代码:

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<...>::value 是检查的布尔结果

示例

对于完整的 SSCCE< /a> 示例参见: IDEOne 示例

示例的错误消息:

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"

Explanation

如果断言失败,它将打印 AssertValue 的模板参数,因此打印完整的模板扩展你的支票。例如,如果您正在检查 std::is_base_of ,它将打印检查的完整类型,例如:std::is_base_of。然后您就可以确切地知道失败的断言中使用了哪些类型。

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

My Hack

Code:

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;
};

It allows for you to check any ::value assertion and dump the types if it failed.

Usage:

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

where std::my_check<...>::value is the boolean result of the check

Example

For a full SSCCE example see: IDEOne Example

The Example's error message:

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"

Explanation

If the assertion fails, it will print the template arguments of AssertValue and therefore print the full template expansion of your check. For example, if you were checking a std::is_base_of it will print the full type of the check, e.g.: std::is_base_of<IMyInterface, MyBadType>. Then you know exactly what types were used in the failed assertion.

The only problem is that this only works on templates that put their result in ::value. However type_traits mostly uses this and is the goto standard.

锦上情书 2024-11-23 02:30:07

如果您的编译器提供了 __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

If your compiler provides the __FUNCTION__ macro, you can have a really simple substitution using it and literal concatenation. However, gcc's and clang's implementation is not done as a macro, so this solution will not work for them.

#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;
}

This produces the following output when compiled by VS2015:

static_assert_test.cpp(10): error C2338: must_be_pod<class not_a_pod>::test: not a POD
旧时光的容颜 2024-11-23 02:30:07

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

可悲的是,我怀疑您最好的选择是游说委员会或编译器编写者扩展该设施。

It's possible to get a string literal passed in as a template non-type parameter, with a little bit of hoop-jumping. But since the second argument to static_assert is constrained to be a string literal rather than, say, an address constant expression, this unfortunately is not much use.

Sadly I suspect your best bet is to lobby the committee or the compiler writers to extend the facility.

第几種人 2024-11-23 02:30:07

__FUNCSIG__ 包括 MSVC 上的模板参数,以及 GCC 上的 __PRETTY_FUNCTION__

#if defined(_MSC_VER)
  #define FUNSIG __FUNCSIG__
#elif defined(__GNUC__)
  #define FUNSIG __PRETTY_FUNCTION__
#else
  #define FUNSIG "Unknown"
#endif

...

static_assert(..., FUNSIG "failed");

请注意,__func____FUNCTION__ 并不总是包含模板参数。

__FUNCSIG__ includes the template arguments on MSVC, and __PRETTY_FUNCTION__ for GCC.

#if defined(_MSC_VER)
  #define FUNSIG __FUNCSIG__
#elif defined(__GNUC__)
  #define FUNSIG __PRETTY_FUNCTION__
#else
  #define FUNSIG "Unknown"
#endif

...

static_assert(..., FUNSIG "failed");

Note that __func__ and __FUNCTION__ do not always include template arguments.

路还长,别太狂 2024-11-23 02:30:07

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

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();
^

如果我们想要更通用一点,我们可以将其放入宏中

I see this has been answered a while ago, but the full answer was lost, and I found a very simply way to achieve the desired result.

template <typename T, bool value>
static typename std::enable_if<value, void>::type FunctionWithReadableErrorMessage()
{
}


int  main()
{
    FunctionWithReadableErrorMessage<int, false>();
    return 0;
}

This function will compile and have no effect if value=true, otherwise we get this error message:

main.cpp: In function 'int main()': main.cpp:16:50: error: no matching
function for call to 'FunctionWithReadableErrorMessage()'
FunctionWithReadableErrorMessage();
^

If we wanna be a bit more general we can put it in a macro

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文