Pregunta ¿Herramientas para generar mensajes de error de mayor calidad para el código basado en plantillas?


Conceptos, que haría innecesarias estas herramientas, son no parte de C ++ 11.

¿Qué herramientas de calidad de producción están disponibles para descifrar los mensajes de error derivados del código basado en plantilla? El soporte de Eclipse-CDT sería bueno también. :)

Si renuncio a C ++ 11, ¿qué opciones tengo para C ++ 98?


Preguntas relacionadas:


32
2018-01-08 17:18


origen


Respuestas:


Atentos a una respuesta (marqué esta wiki de la comunidad para obtener una buena respuesta juntos) ...

Estoy trabajando desde hace mucho tiempo con las plantillas y los mensajes de error han mejorado en general de una manera u otra:

  • Escribir una pila de errores crea mucho más texto, pero generalmente también incluye el nivel que el usuario está mirando y esto generalmente incluye una pista sobre cuál es el problema real. Dado que el compilador solo ve una unidad de traducción lanzada, no hay mucho que pueda hacerse para determinar qué error en la pila es el más adecuado para el usuario.
  • Usar inspectores conceptuales, es decir, clases o funciones que ejercitan todos los miembros requeridos de los argumentos de la plantilla y posiblemente generen mensajes de error usando static_assert() proporcione al autor de la plantilla una forma de informar a los usuarios sobre las suposiciones que aparentemente no se cumplen.
  • También ayuda el hecho de decirle al usuario sobre los tipos que escribe en lugar de expandir todos los typedefs, como le gusta ver al compilador en el nivel más bajo. clang es bastante bueno en esto y en realidad te da mensajes de error, p. hablando sobre std::string en lugar de expandir las cosas, escribe lo que sea que termine siendo.

Una combinación de la técnica en realidad causa, p. clang para crear un mensaje de error bastante decente (incluso si no implementa C ++ 2011, sin embargo, no hay compilador y, por lo que puedo decir, gcc y clang lideran el paquete). Sé que otros desarrolladores de compiladores trabajan activamente para mejorar los mensajes de error de la plantilla, ya que muchos programadores han descubierto que las plantillas en realidad representan un gran avance, aunque los mensajes de error son algo que nos lleva un poco de tiempo acostumbrarnos.

Un problema con las herramientas como stlfilt es que los compiladores y las bibliotecas de C ++ están en desarrollo activo. Esto da como resultado mensajes de error que cambian todo el tiempo, haciendo que la herramienta reciba salidas diferentes. Si bien es bueno que los escritores de compiladores trabajen en la mejora de los mensajes de error, ciertamente hace la vida más difícil para las personas que intentan trabajar a partir de los mensajes de error que recibieron. También hay otro lado de esto: una vez que se detecta que un determinado patrón de error es común y se recoge, p. por stlfilt (bueno, no se mantiene activamente hasta donde yo sé) los escritores de compiladores probablemente quieran informar los errores siguiendo estos patrones directamente, posiblemente también proporcionando información adicional disponible para el compilador pero no emitida antes. Dicho de otra manera, esperaría que los escritores de compiladores sean bastante receptivos a los informes de los usuarios que describen situaciones de error comunes y cómo se informan mejor. Los escritores del compilador pueden no encontrar los errores porque el código en el que están trabajando es realmente C (por ejemplo, gcc se implementa en C) o porque están tan acostumbrados a ciertas técnicas de plantilla que evitan ciertos errores (por ejemplo, omisión de typenamepara tipos dependientes).

Finalmente, para abordar la pregunta sobre las herramientas concretas: la principal "herramienta" que estoy usando cuando me quedo atrapado con un compilador quejándose de la instanciación de una plantilla es usar compiladores diferentes. Aunque no siempre es el caso, a menudo un compilador informa un mensaje de error completamente incomprensible que solo tiene sentido después de ver el informe bastante conciso de otro compilador (en caso de que esté interesado, regularmente uso la versión más reciente de gcc, clang y EDG). para esto). Sin embargo, no estoy al tanto de un paquete fácil como stlfilt.


16
2018-01-15 12:01



Sé que esto puede no ser tan útil como querías, pero he encontrado que la mejor herramienta contra los mensajes de error de la plantilla es conocimiento.

Una buena comprensión del STL y cómo usarlo lo ayudará a evitar muchos errores en primer lugar. En segundo lugar, a menudo los mensajes de error se refieren a las funciones en la fuente STL: si tiene una idea aproximada de cómo se implementa el STL, esto puede ser extremadamente útil para descifrar de qué se trata el mensaje de error. Finalmente, los fabricantes de compiladores son conscientes de este problema y están mejorando gradualmente la salida de mensajes de error, por lo que haría bien en apegarse a la última versión de su compilador.

Aquí hay un buen ejemplo de un error de plantilla oscuro:

std::vector<std::unique_ptr<int>> foo;
std::vector<std::unique_ptr<int>> bar = foo;

unique_ptr no se puede copiar, solo se puede mover. Así que tratar de asignar un vector de unique_ptr a otro vector significará que en algún lugar del vector el código fuente intentará copiar un puntero único. Por lo tanto, el error se originará en un código que no es suyo y arrojará un mensaje de error bastante opaco como resultado. El mensaje de error ideal sería

main.cpp (20): no se puede construir 'bar' desde 'foo': el tipo de plantilla de foo no se puede copiar

En cambio, VS2010 produce el siguiente error:

1>C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include\xmemory(48): error C2248: 'std::unique_ptr<_Ty>::unique_ptr' : cannot access private member declared in class 'std::unique_ptr<_Ty>'
1>          with
1>          [
1>              _Ty=int
1>          ]
1>          C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include\memory(2347) : see declaration of 'std::unique_ptr<_Ty>::unique_ptr'
1>          with
1>          [
1>              _Ty=int
1>          ]
1>          C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include\xmemory(197) : see reference to function template instantiation 'void std::_Construct<std::unique_ptr<_Ty>,const std::unique_ptr<_Ty>&>(_Ty1 *,_Ty2)' being compiled
1>          with
1>          [
1>              _Ty=int,
1>              _Ty1=std::unique_ptr<int>,
1>              _Ty2=const std::unique_ptr<int> &
1>          ]
1>          C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include\xmemory(196) : while compiling class template member function 'void std::allocator<_Ty>::construct(std::unique_ptr<int> *,const _Ty &)'
1>          with
1>          [
1>              _Ty=std::unique_ptr<int>
1>          ]
1>          C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include\vector(421) : see reference to class template instantiation 'std::allocator<_Ty>' being compiled
1>          with
1>          [
1>              _Ty=std::unique_ptr<int>
1>          ]
1>          C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include\vector(481) : see reference to class template instantiation 'std::_Vector_val<_Ty,_Alloc>' being compiled
1>          with
1>          [
1>              _Ty=std::unique_ptr<int>,
1>              _Alloc=std::allocator<std::unique_ptr<int>>
1>          ]
1>         main.cpp(19) : see reference to class template instantiation 'std::vector<_Ty>' being compiled
1>          with
1>          [
1>              _Ty=std::unique_ptr<int>
1>          ]

Examinando esto hay pistas. La primera sección hace referencia a un acceso de miembro privado de std::unique_ptr<int>. La segunda sección, si hace clic a través de la línea fuente, apunta al constructor de copia de unique_ptr, que se declara debajo de un private: especificador. Entonces ahora sabemos que intentamos copiar un unique_ptr que no está permitido. Las secciones 3, 4 y 5 solo apuntan al código repetitivo, es solo ruido. La Sección 6 dice "ver compilación de la instanciación de la plantilla de clase 'std :: _ Vector_val <_Ty, _Alloc>'. En otras palabras, este error ocurrió en el código de la plantilla del vector. La última sección es la más interesante: apunta directamente a la línea que declara foo en su propio código fuente - ¡se descifró en qué parte de su código fuente se originó el error!

Así que sumando las pistas:

  • Se origina en foo,
  • Se origina en el código vectorial,
  • Intenta copiar un unique_ptr que no está permitido.
  • Conclusión: el vector intentó copiar uno de sus elementos, lo cual no está permitido. Revisar el código para foo y revise si hay algo que cause una copia.

Dado que el compilador solo apuntó a la declaración de foo, si la asignación está muy alejada en el código fuente, habrá algo de caza involucrado. Obviamente, esto no es ideal, pero creo que este enfoque finalmente te brinda más posibilidades de corregir errores en general. Comenzarás a reconocer que ese tipo de volcado de error significa "has copiado un unique_ptr". Una vez más, no lo defiendo, definitivamente necesita mejorar, pero creo que en estos días hay sólo suficiente información en la salida que combinada con un buen conocimiento de la STL le permite solucionar el problema.


9
2018-01-16 00:28



Encontré Clang para generar los mejores mensajes de error para el código con muchas plantillas. Por supuesto, la verbosidad es inevitable en la mayoría de los casos, pero sigue siendo mejor que GCC o MSVC la mayor parte del tiempo. Aquí está el mensaje de error de Clang para el código de ejemplo publicado por AshleysBrain:

$ clang++ -std=c++11 -stdlib=libc++ -o dummy dummy.cpp 
In file included from dummy.cpp:1:
In file included from /usr/include/c++/v1/vector:243:
In file included from /usr/include/c++/v1/__bit_reference:15:
In file included from /usr/include/c++/v1/algorithm:594:
/usr/include/c++/v1/memory:1425:36: error: calling a private constructor of class 'std::__1::unique_ptr<int,
      std::__1::default_delete<int> >'
                ::new ((void*)__p) _Tp(_STD::forward<_Args>(__args)...);
                                   ^
/usr/include/c++/v1/memory:1358:14: note: in instantiation of function template specialization
      'std::__1::allocator_traits<std::__1::allocator<std::__1::unique_ptr<int, std::__1::default_delete<int> > >
      >::__construct<std::__1::unique_ptr<int, std::__1::default_delete<int> >, std::__1::unique_ptr<int,
      std::__1::default_delete<int> > &>' requested here
            {__construct(__has_construct<allocator_type, pointer, _Args...>(),
             ^
/usr/include/c++/v1/vector:781:25: note: in instantiation of function template specialization
      'std::__1::allocator_traits<std::__1::allocator<std::__1::unique_ptr<int, std::__1::default_delete<int> > >
      >::construct<std::__1::unique_ptr<int, std::__1::default_delete<int> >, std::__1::unique_ptr<int,
      std::__1::default_delete<int> > &>' requested here
        __alloc_traits::construct(__a, _STD::__to_raw_pointer(this->__end_), *__first);
                        ^
/usr/include/c++/v1/vector:924:9: note: in instantiation of function template specialization
      'std::__1::vector<std::__1::unique_ptr<int, std::__1::default_delete<int> >,
      std::__1::allocator<std::__1::unique_ptr<int, std::__1::default_delete<int> > >
      >::__construct_at_end<std::__1::unique_ptr<int, std::__1::default_delete<int> > *>' requested here
        __construct_at_end(__x.__begin_, __x.__end_);
        ^
dummy.cpp:7:37: note: in instantiation of member function 'std::__1::vector<std::__1::unique_ptr<int,
      std::__1::default_delete<int> >, std::__1::allocator<std::__1::unique_ptr<int, std::__1::default_delete<int> > >
      >::vector' requested here
        std::vector<unique_ptr<int>> bar = foo;
                                           ^
/usr/include/c++/v1/memory:1997:5: note: declared private here
    unique_ptr(const unique_ptr&);
    ^
1 error generated.

Todavía es largo y feo, pero en mi opinión está mucho más claro sobre qué / dónde está el problema.


4