Интегрировать имя типа в вывод static_assert? - PullRequest
53 голосов
/ 20 июня 2011

Мне нравится давать полезные сообщения об ошибках, и я также хочу сделать это для моих static_assert s.Проблема в том, что они зависят от параметров шаблона.Обычно эти параметры отображаются в пути или по другому из-за возникшей ошибки, но они либо неясны, либо не сгруппированы, поэтому имеют смысл.Пример:

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.А теперь представьте еще несколько параметров, или больше шаблонов, или оба. shivers

Один из способов обойти это - использовать промежуточную структуру, которая принимает оба тега в качестве параметров шаблона:

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>.

Или, если это невозможно,можем ли мы как-то сделать сообщение об ошибке дополнительным параметром шаблона, чтобы сделать его проходимым?

Ответы [ 5 ]

11 голосов
/ 13 ноября 2012

Мой взлом

Код:

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 см. * Пример 101E * 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.

3 голосов
/ 02 октября 2011

Возможно получить строковый литерал, переданный как шаблонный тип параметра, с небольшим количеством скачкообразных скачков .Но поскольку второй аргумент static_assert ограничен строковым литералом, а не, скажем, выражением константы адреса, это, к сожалению, не очень полезно.

К сожалению, я подозреваю, что вам лучше всего лоббировать комитетили создатели компилятора для расширения возможностей.

2 голосов
/ 27 июля 2016

Если ваш компилятор предоставляет макрос __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
0 голосов
/ 01 июля 2018

Я вижу, что на этот вопрос уже отвечали некоторое время назад, но полный ответ был потерян, и я нашел очень простой способ достижения желаемого результата.

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


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

Эта функция компилируется и не имеет никакого эффектаесли значение = true, в противном случае мы получим это сообщение об ошибке:

main.cpp: в функции 'int main ()': main.cpp: 16: 50: ошибка: нет соответствующей функции для вызова'FunctionWithReadableErrorMessage ()' FunctionWithReadableErrorMessage ();^

Если мы хотим быть более общими, мы можем поместить это в макрос

0 голосов
/ 18 августа 2011

std::type_info имеет члена const char* name():

#include <typeinfo>
using namespace std;

//...

const char* name = type_info(T).name();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...