Me gusta dar útil errores /mensajes, y también quiero hacerlo para mi static_asserts. El problema es, que dependen de los parámetros de la plantilla. Normalmente, estos parámetros se muestran en forma o de otra, debido a que el error generado, pero son ya sea oscuro o no se agrupan de forma que tengan 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);
}

De 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 abajo con la plantilla de clase. No es tan agradable. Vamos a ver lo que GCC salidas:

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 todavía no se realmente donde el static_assert es. Y ahora imaginen algunos parámetros más, o más plantillas, o ambos. escalofríos

Una forma de evitar esto es utilizar un intermedio struct, que tiene tanto las Etiquetas como los parámetros de la 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 vamos a ver 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! Aquí es lo que GCC dice:

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

Parece que no está mal. El problema: necesito crear una estructura para cada plantilla, ya que el mensaje de error en static_assert debe ser una cadena literal…

Ahora, para mi pregunta: ¿se Puede de alguna manera incluyen los nombres de tipo directamente en el static_assert? Como

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

Ejemplo de salida:

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

O, si ello no es factible, podemos hacer de alguna manera el mensaje de error con un extra de parámetro de plantilla, como para hacerla transitable?

  • Me gustaría ver a los compiladores a ser mejores aquí. Debe ser posible demostrar la condición de que no se pudo. Se podría decir note: in static_assert check for fake_dependency<T>::value [with T = ...] (en los soportes, que enumera todos los parámetros de la plantilla utilizada en la expresión). Esperemos!
  • Creo que se podría hacer con constexpr expresión de las plantillas, no crees? Como ya se puede desmontaje de tiempo de ejecución de expresiones o condiciones como la unidad de Verificación de marco de pruebas no.
  • Lástima que los conceptos no hacer en C++0x, que reduciría en gran medida la necesidad, tal y como está typeid o dyanamic_cast son la única manera para determinar el tipo y ambos requieren de una instancia que no tiene con una plantilla de arg.
  • En realidad, la typeid operador es perfectamente válido en un solo tipo: typeid(int) por ejemplo. Pero el problema es, que static_assert quiere un literal de cadena. 🙁
  • ¿Conoces todos los tipos de antemano? Si es así, puede crear un error de la plantilla con especializaciones para cada tipo.
  • Puedo artesanal invalid_use_of_<> plantillas, por cada mensaje que yo quiero, como abusado aquí, pero me preguntó específicamente para static_assert. :/
  • Mi respuesta: stackoverflow.com/questions/13837668/…

InformationsquelleAutor Xeo | 2011-06-20

5 Comentarios

  1. 11

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

    Permite para que usted compruebe cualquier ::value afirmación y el volcado de los tipos si no.

    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 de la verificación

    Ejemplo

    Para una completa SSCCE ejemplo ver: IDEOne Ejemplo

    El Ejemplo del mensaje de error:

    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 aserción falla, se va a imprimir la plantilla de los argumentos de AssertValue y, por tanto, imprimir la plantilla completa de expansión de su cheque. Por ejemplo, si estaban revisando un std::is_base_of se imprimirá el tipo completo de la verificación, por ejemplo: std::is_base_of<IMyInterface, MyBadType>. Entonces usted sabe exactamente lo que los tipos se han utilizado en el error de aserción.

    El único problema es que esto sólo funciona en las plantillas que poner su resultado en el ::value. Sin embargo type_traits utiliza sobre todo esto y es el de goto estándar.

    • No es tan bonito como me gustaría, pero no está mal tampoco. Creo que te refieres <see above ... aunque, a juzgar por GCC y MSVC errores.
    • Me hizo ver abajo porque creo que MSVC pone el error de afirmar primero antes de volcar los tipos… GCC hace lo contrario
    • A la derecha, la miré mal la salida de error en mi pregunta.
    • Ideone ejemplo ya no hay… ideone.com/AcwsCA Y el código de respuesta no está completa, falta AssertionChecker
    • Donde se AssertionValue definido?
  2. 3

    Es posible obtener un literal de cadena pasada como una plantilla de no-tipo de parámetro, con un poco de aro de salto. Pero desde el segundo argumento static_assert se limita a ser un literal de cadena en lugar de, digamos, una dirección constante de la expresión, esto por desgracia no es de mucho uso.

    Lamentablemente sospecho que su mejor apuesta es en el lobby ante el comité o el compilador de escritores para ampliar las instalaciones.

  3. 2

    Si su compilador proporciona la __FUNCTION__ macro, usted puede tener una muy simple sustitución de usarlo y literal de la concatenación. Sin embargo, gcc y el sonido metálico de la aplicación 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 se compila por VS2015:

    static_assert_test.cpp(10): error C2338: must_be_pod<class not_a_pod>::test: not a POD
    • Esto no funcionará con GCC o Clang, ya que no tienen __FUNCIÓN__ como una macro.
    • También, aunque Embarcadero C++ tiene la __FUNCTION__ macro, no se puede (10.1) uso como el mensaje en un static_assert.
    • En realidad, según gcc.gnu.org/onlinedocs/gcc/Function-Names.html gcc en efecto, el apoyo __FUNCTION__ así como __PRETTY_FUNCTION__.
    • Pero no son las macros, __FUNCTION___ es una macro en MSVC que se sustituye con un literal de cadena en tiempo de compilación, que es lo que hace a este trabajo.
  4. 0

    Veo esto ha sido contestado hace un tiempo, pero la respuesta completa se perdió, y me encontré con una muy sencilla forma 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 se encargará de compilar y no tienen ningún efecto si el valor=true, de lo contrario nos aparece este mensaje de error:

    main.cpp: En la función ‘int main()’: main (principal).cpp:16:50: error: no coincidentes
    la función de llamada a ‘FunctionWithReadableErrorMessage()’
    FunctionWithReadableErrorMessage();
    ^

    Si queremos ser un poco más general, se puede poner en una macro

  5. -4

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

    #include <typeinfo>
    using namespace std;
    
    //...
    
    const char* name = type_info(T).name();
    • No es una cadena literal, y como tal no puede ser utilizado en static_assert. También, el «nombre» del compilador y/o dependiente de la plataforma. :/

Dejar respuesta

Please enter your comment!
Please enter your name here