web-dev-qa-db-ja.com

static_assert出力に型名を統合しますか?

役立つエラーやメッセージを提供したいのですが、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
          ]

1つのタグは関数テンプレート自体で言及され、もう1つのタグはクラステンプレートで言及されます。いいね。 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がどこにあるかはわかりません。そして、さらにいくつかのパラメーター、またはより多くのテンプレート、あるいはその両方を想像してみてください。 shivers

これを回避する1つの方法は、中間構造体を使用することです。これは、両方のタグをテンプレートパラメータとして使用します。

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<int,main::TagA>からFoo<int,main::TagB>を作成できません。

または、それが達成できない場合は、エラーメッセージを渡せるようにするために、なんらかの理由でエラーメッセージを追加のテンプレートパラメータにできますか?

59
Xeo

私のハック

コード:

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 [〜#〜] の例については、以下を参照してください。 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"

説明

アサーションが失敗した場合、AssertValueのテンプレート引数が出力されるため、チェックの完全なテンプレート展開が出力されます。たとえば、std::is_base_ofチェックの完全なタイプを出力します。例:std::is_base_of<IMyInterface, MyBadType>。次に、失敗したアサーションで使用されたタイプを正確に把握します。

唯一の問題は、これが結果を::value。しかしながら type_traits主にこれを使用し、goto標準です。

11
Bob Fincheimer

hoop-jumping を少し使用して、文字列リテラルをテンプレートの非型パラメーターとして渡すことができます。ただし、static_assertの2番目の引数は、たとえばアドレス定数式ではなく文字列リテラルに制限されているため、残念ながらこれはあまり役に立ちません。

悲しいことに、私はあなたの最善の策は委員会やコンパイラーの作者にロビーを拡張して施設を拡張することだと思います。

3
Alan Stokes

コンパイラが__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
2
Spencer

私はこれが少し前に回答されたことを確認しますが、完全な回答は失われ、望ましい結果を達成するための非常に簡単な方法が見つかりました。

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

もう少し一般的にしたい場合は、マクロに入れることができます

0