C++ vs D, Ada y Eiffel (horrible mensajes de error con plantillas)


Uno de los problemas de C++ son los horribles mensajes de error que estamos recibiendo de código que utiliza intensivamente plantillas y metaprogramación de plantillas. Los conceptos están diseñados para resolver este problema, pero desafortunadamente no estarán en el siguiente estándar.

Me pregunto, ¿es este problema común para todos los lenguajes, que son compatibles con la programación genérica? O algo está mal con las plantillas de C++?

Desafortunadamente no conozco ningún otro lenguaje que soporte genéricos programación (los genéricos de Java y C# son demasiado simplificados y no tan potentes como las plantillas de C++).

Así que les pregunto: ¿Las plantillas D,Ada,Eiffel (genéricos) también producen mensajes de error tan feos? ¿Y es posible tener un lenguaje con un poderoso paradigma de programación genérica, pero sin mensajes de error feos? Y si es así, ¿cómo estos idiomas están resolviendo este problema ?

Editar: para downvoters. Me encanta C++ y las plantillas. No estoy diciendo que las plantillas sean malas. En realidad soy un gran fan de la programación genérica y la metaprogramación de plantillas. Solo estoy preguntando por qué estoy recibiendo mensajes de error tan feos de los compiladores.

Author: UmmaGumma, 2011-04-03

6 answers

El problema, en el fondo, es que la recuperación de errores es difícil, sea cual sea el contexto.

Y cuando se tienen en cuenta gramáticas horribles de C y C++, solo se puede preguntar que los mensajes de error no son peores que eso! Me temo que la gramática C ha sido diseñada por personas que no tenían ni idea de las propiedades esenciales de una gramática, una de ellas es que cuanto menos confianza en el contexto, mejor y la otra es que debe esforzarse para que sea tan inequívoca como posible.

Ilustremos un error común: olvidar un punto y coma.

struct CType {
  int a;
  char b;
}
foo
bar() { /**/ }

Bien, esto está mal, ¿dónde debería ir el punto y coma que falta ? Bueno, desafortunadamente es ambiguo, puede ir antes o después foo porque:

  • C considera normal declarar una variable en zancada después de definir un struct
  • C considera normal no especificar un tipo de retorno para una función (en cuyo caso el valor predeterminado es int)

Si razonamos acerca de, podríamos ver que:

  • si foo nombra un tipo, entonces pertenece a la declaración de función
  • si no, probablemente denota una variable... a menos que, por supuesto, hayamos hecho un error tipográfico y estuviera destinado a ser escrito fool, que resulta ser un tipo: /

Como puede ver, la recuperación de errores es francamente difícil, porque necesitamos inferir lo que el escritor quiso decir, y la gramática está lejos de ser receptiva. Sin embargo, no es imposible, y la mayoría de los errores pueden ser diagnosticado más o menos correctamente, e incluso recuperado de... solo toma considerable esfuerzo.

Parece que las personas que trabajan en gcc están más interesadas en producir código rápido (y quiero decir rápido, buscar los últimos puntos de referencia en gcc 4.6) y agregar características interesantes (gcc ya implementa la mayoría, si no todo, de C++0x) que producir mensajes de error fáciles de leer. Puede culparlos ? No puedo.

Afortunadamente hay gente que piensa que un informe de errores preciso y una buena recuperación de errores son un objetivo muy valioso, y algunos de ellos han estado trabajando en CLang durante bastante tiempo, y continúan haciéndolo.

Algunas características agradables, de la parte superior de mi cabeza:

  • Mensajes de error concisos pero completos, que incluyen los rangos de origen para exponer exactamente de dónde emanó el error
  • Fix-It señala cuando es obvio lo que se quería decir
  • , En cuyo caso el compilador analiza el resto del archivo como si la solución ya hubiera estado allí, en lugar de arrojar líneas sobre líneas de galimatías
  • (reciente) evite incluir la pila include para notas, para cortar en el cruft
  • (reciente) tratando solo de exponer los tipos de parámetros de plantilla que el desarrollador realmente escribió, y preservando typedefs (hablando así de std::vector<Name> en lugar de std::vector<std::basic_string<char, std::allocator<char>>, std::allocator<std::basic_string<char, std::allocator<char>> > lo que hace toda la diferencia)
  • (reciente) recuperándose correctamente en caso de que falte template en caso de que falte en una llamada a una plantilla método desde dentro de otro método de plantilla

Pero cada uno de ellos ha requerido varias horas a días de trabajo.

Ciertamente no vinieron gratis.

Ahora, los conceptos deberían (normalmente) haber hecho nuestras vidas más fáciles. Sin embargo, en su mayoría no se habían ensayado, por lo que se consideró preferible eliminarlas del proyecto. Debo decir que me alegro de ello. Dada la relativa inercia de C++, es mejor no incluir características que no hayan sido completamente revisadas, y los mapas conceptuales no me emocionaron. Tampoco entusiasmaron a Bjarne o Herb, al parecer, ya que dijeron que reconsiderarían los conceptos desde cero para el siguiente estándar.

 15
Author: Matthieu M.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-08 07:28:06

En general, encontré que los mensajes de error del compilador Ada para genéricos realmente no son significativamente más difíciles de leer que cualquier otro mensaje de error del compilador Ada.

Los mensajes de error de la plantilla C++, por otro lado, son notorios por ser novelas de error. La principal diferencia creo que es la forma en que C++ hace la instanciación de plantillas. La cosa es que las plantillas de C++ son mucho más flexibles que los genéricos de Ada. Es tan flexible, es casi como un preprocesador macro. Las personas inteligentes en Boost tienen usó esto para implementar cosas como lambdas e incluso otros lenguajes.

Debido a esa flexibilidad, toda la jerarquía de plantillas básicamente tiene que ser compilada de nuevo cada vez que su permutación particular de parámetros de plantilla se encuentra por primera vez. Por lo tanto, los problemas que se resuelven hasta incompatibilidades varias capas de una API terminan presentándose al cliente API pobre para descifrar.

En Ada, los genéricos son en realidad fuertemente mecanografiados, y proporcionan información completa ocultando a la cliente, al igual que lo hacen los paquetes y subrutinas normales. Por lo tanto, si recibe un mensaje de error, normalmente solo hace referencia al genérico que está tratando de instatizar, no a toda la jerarquía utilizada para implementarlo.

Así que sí, los mensajes de error de la plantilla C++ son mucho peores que los de Ada.

Ahora depurar es una historia completamente diferente...

 14
Author: T.E.D.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-04 16:05:25

D tiene dos características para mejorar la calidad de los mensajes de error de plantilla: Restricciones y static assert.

// Use constraints to only allow a function to operate on random access 
// ranges as defined in std.range.  If something that doesn't satisfy this
// is passed, the compiler will error before even trying to instantiate
// fun().
void fun(R)(R range) if(isRandomAccessRange!(R)) {
    // Do stuff.
}


// Use static assert to check a high level invariant.  If 
// the predicate is false, the error message will be 
// printed and compilation will stop before a screen 
// worth of more confusing errors are encountered.
// This function takes any number of ranges to merge sort
// and the same number of temporary buffers to merge into.
void mergeSort(R...)(R ranges) {
    static assert(R.length % 2 == 0, 
        "Must have equal number of ranges to be sorted and temporary buffers.");

    static assert(allSatisfy!(isRandomAccessRange, R), 
        "All arguments to mergeSort must be random access ranges.");

    // Implementation
}
 9
Author: dsimcha,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-03 14:26:55

El artículo Programación Genérica describe muchos de los pros y contras de los genéricos en varios idiomas, incluyendo Ada en particular . Aunque carece de especialización en plantillas, todos Ada generic las instancias son "equivalentes a la declaración de instancia immediately seguida inmediatamente por el cuerpo de la instancia". Como cuestión práctica, los mensajes de error tienden a ocurrir en tiempo de compilación, y típicamente representan violaciones familiares de seguridad de tipo.

 8
Author: trashgod,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-03 20:27:05

Eiffel tiene el mejor de todos los mensajes de error porque tiene el mejor de todos los sistemas de plantillas. Está totalmente integrado en el lenguaje y funciona bien porque es el único lenguaje que está usando covarianz en los argumentos.

Por lo tanto, es mucho más que un simple compilador copiar y pegar. Desafortunadamente explicar la diferencia en unas pocas líneas es imposible. Solo tienes que ir y echar un vistazo a EiffelStudio.

 4
Author: Lothar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-20 00:52:29

Hay algunos esfuerzos para mejorar los mensajes de error. Clang, por ejemplo, ha puesto mucho énfasis en generar mensajes de error de compilador más fácilmente legibles. Solo lo he estado usando por un corto tiempo, pero mi experiencia hasta ahora ha sido bastante positiva en comparación con los errores equivalentes de GCC.

 2
Author: Flexo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-03 11:56:22