c++ templates c++11 custom-errors static-assert

c++ - ¿Integre el nombre del tipo en la salida static_assert?



templates c++11 (5)

Mi Hack

Código:

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

Le permite verificar cualquier aserción ::value y volcar los tipos si falla.

Uso:

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

donde std::my_check<...>::value es el resultado booleano del cheque

Ejemplo

Para ver un ejemplo completo de SSCCE, vea: IDEOne Example

El mensaje de error del Ejemplo:

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"

Explicación

Si la afirmación falla, imprimirá los argumentos de la plantilla de AssertValue y, por lo tanto, imprimirá la expansión completa de la plantilla de su cheque. Por ejemplo, si estaba comprobando una std::is_base_of , imprimirá el tipo completo de la verificación, por ejemplo: std::is_base_of<IMyInterface, MyBadType> . Entonces sabrá exactamente qué tipos se usaron en la afirmación fallida.

El único problema es que esto solo funciona en plantillas que ponen su resultado en ::value . Sin embargo, type_traits usa principalmente esto y es el estándar goto.

Me gusta dar errores / mensajes útiles, y también quiero hacerlo para mi static_assert s. El problema es que dependen de los parámetros de la plantilla. Normalmente, esos parámetros se mostrarán de forma u otra debido al error planteado, pero son oscuros o no están agrupados, por lo que tienen sentido. Ejemplo:

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

Salida en 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 ]

Una etiqueta se menciona en la plantilla de función en sí, la otra a continuación con la plantilla de clase. No tan agradable. Veamos qué resultados de 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>."

Mucho mejor, pero aún no es donde realmente está el static_assert . Y ahora imagine algunos parámetros más, o más plantillas, o ambos. escalofríos

Una forma de evitar esto es usar una estructura intermedia, que toma ambas etiquetas como parámetros de plantilla:

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

Ahora veamos la salida de nuevo:

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 ]

¡Mucho mejor! Esto es lo que dice 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>."

No está mal. El problema: necesito crear una estructura así para cada plantilla, ya que el mensaje de error en static_assert necesita ser una cadena literal ...

Ahora, para mi pregunta: ¿Podemos de alguna manera incluir los nombres de tipo directamente en static_assert ? Me gusta

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

Ejemplo de salida:

No se puede crear Foo<int,main::TagA> desde Foo<int,main::TagB> .

O, si eso no se puede lograr, ¿podemos de alguna manera hacer que el mensaje de error sea un parámetro de plantilla adicional, para hacerlo pasable?


Es posible obtener un literal de cadena pasado como parámetro de plantilla sin tipo, con un poco de hoop-jumping de hoop-jumping . Pero dado que el segundo argumento para static_assert está restringido a ser un literal de cadena en lugar de, por ejemplo, una expresión de constante de dirección, desafortunadamente no es de mucha utilidad.

Lamentablemente, sospecho que su mejor opción es presionar al comité o a los escritores del compilador para que amplíen las instalaciones.


Si su compilador proporciona la macro __FUNCTION__ , puede tener una sustitución realmente simple usando it y concatenación literal. Sin embargo, la implementación de gcc y clang no se realiza como una macro, por lo que esta solución no funcionará para ellos.

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

Esto produce la siguiente salida cuando compila VS2015:

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


Veo que esto fue respondido hace un tiempo, pero la respuesta completa se perdió, y encontré una manera muy simple de lograr el resultado deseado.

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

Esta función compilará y no tendrá efecto si value = true, de lo contrario aparece este mensaje de error:

main.cpp: en la función ''int main ()'': main.cpp: 16: 50: error: sin función de coincidencia para la llamada a ''FunctionWithReadableErrorMessage ()'' FunctionWithReadableErrorMessage (); ^

Si queremos ser un poco más generales, podemos ponerlo en una macro


std::type_info tiene un miembro const char* name() :

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