C++ 的问题之一是我们从大量使用模板和模板元编程的代码中收到可怕的错误消息。这些概念旨在解决这个问题,但不幸的是它们不会出现在下一个标准中。
我想知道,这个问题对于所有支持泛型编程的语言来说都是常见的吗?或者 C++ 模板有问题?
不幸的是,我不知道任何其他语言支持泛型编程(Java 和 C# 泛型过于简化,而且不如 C++ 模板强大)。
所以我问你们:D、Ada、Eiffel 模板(泛型)也会产生如此丑陋的错误消息吗?是否有可能有一种语言具有强大的通用编程范式,但没有丑陋的错误消息?如果是,这些语言是如何解决这个问题的?
编辑:针对投反对票的人。我真的很喜欢 C++ 和模板。我并不是说模板不好。事实上,我非常喜欢泛型编程和模板元编程。我只是问为什么我会从编译器收到如此难看的错误消息。
One of the problems of C++ are horrible error messages that we are getting from code which intensively uses templates and template metaprogramming. The concepts are designed to solve this problem, but unfortunately they will not be in the next standard.
I'm wondering, is this problem common for all languages, which are supporting generic programming? Or something is wrong with C++ templates?
Unfortunately I don't know any other language, that supports generic programming (Java and C# generics are too simplified and not as powerful as C++ templates).
So I'm asking you guys: are D,Ada,Eiffel templates (generics) producing such ugly error messages too? And Is it possible to have language with powerful generic programming paradigm, but without ugly error messages? And if yes, how these languages are solving this problem ?
Edit: for downvoters. I really love C++ and templates. I'm not saying that templates are bad. Actually I'm a big fan of generic programming and template metaprogramming. I'm just asking why I'm getting such ugly error messages from compilers.
发布评论
评论(6)
Eiffel 拥有所有错误消息中最好的,因为它拥有所有模板系统中最好的。它完全集成到语言中并且运行良好,因为它是唯一在参数中使用 covarianz 的语言。
因此,它不仅仅是简单的编译器复制和粘贴。不幸的是,用几行代码来解释差异是不可能的。去 EiffelStudio 看看吧。
Eiffel has the best of all error messages because it is has the best of all template systems. It is fully integrated into the language and works well because it is the only language which is using covarianz in arguments.
Therefore it is much more then a simple compiler copy and paste. Unfortunately explaining the difference in a few lines is impossible. Just go and have a look at EiffelStudio.
有一些努力来改进错误消息。例如,Clang 非常重视生成更易于阅读的编译器错误消息。我只使用了很短一段时间,但到目前为止,与 GCC 的同等错误相比,我对它的体验非常积极。
There are some efforts to improve the error messages. Clang, for example, has put quite a lot of emphasis on generating more easily readable compiler error messages. I've only been using it for a short while, but my experience of it so far has been quite positive compared to GCC's equivalent errors.
问题的核心在于,无论在什么情况下,错误恢复都很困难。
当您考虑到 C 和 C++ 可怕的语法时,您只能想知道错误消息并不比这更糟糕!恐怕 C 语法是由那些对语法的基本属性一无所知的人设计的,其中之一是对上下文的依赖越少越好,另一个是您应该努力使它尽可能明确。
让我们举例说明一个常见错误:忘记分号。
好吧,这是错误的,缺少的分号应该去哪里?不幸的是,它是不明确的,它可以在 foo 之前或之后,因为:
int
)如果我们推理,我们可以看到:
foo
命名了一个类型,那么它属于函数声明fool
,它恰好是一个类型:/如你所见,错误恢复是非常困难的,因为我们需要推断作者的意思,而语法远不能被接受。但这并非不可能,而且大多数错误确实可以或多或少正确地诊断出来,甚至可以从中恢复......这只需要相当大的努力。
似乎从事
gcc
工作的人更感兴趣的是生成快速代码(我的意思是快速,搜索 gcc 4.6 上的最新基准)和添加有趣的功能(gcc 已经实现大部分(如果不是全部)C++0x)而不是生成易于阅读的错误消息。你能责怪他们吗?我不能。幸运的是,有人认为准确的错误报告和良好的错误恢复是一个非常有价值的目标,其中一些人已经在 CLang 上工作了相当长的时间,并且他们将继续这样做。
一些不错的功能,在我的脑海中浮现出来:
std::vector
而不是std::vector>、std::allocator> >
使一切变得不同)template
以防在从另一个模板方法调用模板方法时丢失该模板但是每个模板方法都需要几个小时到几天的工作。
他们当然不是免费来的。
现在,概念(通常)应该让我们的生活变得更轻松。但他们大多未经测试,因此最好将他们从选秀中删除。我必须说我对此感到高兴。考虑到 C++ 的相对惯性,最好不要包含尚未彻底修改的功能,而且概念图并没有真正让我兴奋。他们似乎也没有让 Bjarne 或 Herb 感到兴奋,因为他们表示将重新考虑下一个标准的概念。
The problem, at heart, is that error recovery is difficult, whatever the context.
And when you factor in C and C++ horrid grammars, you can only wonder that error messages are not worse than that! I am afraid that the C grammar has been designed by people who didn't have a clue about the essential properties of a grammar, one of them being that the less reliance on the context the better and the other being that you should strive to make it as unambiguous as possible.
Let us illustrate a common error: forgetting a semi-colon.
Okay so this is wrong, where should the missing semi-colon go ? Well unfortunately it's ambiguous, it can go either before or after
foo
because:struct
int
)If we reason about, we could see that:
foo
names a type, then it belongs to the function declarationfool
, which happens to be a type :/As you can see, error recovery is downright difficult, because we need to infer what the writer meant, and the grammar is far from being receptive. It is not impossible though, and most errors can indeed be diagnosed more or less correctly, and even recovered from... it just takes considerable effort.
It seems that people working on
gcc
are more interested in producing fast code (and I mean fast, search for the latest benchmarks on gcc 4.6) and adding interesting features (gcc already implement most - if not all - of C++0x) than producing easy to read error messages. Can you blame them ? I can't.Fortunately there are people who think that accurate error reporting and good error recovery are a very worthy goal, and some of those have been working on CLang for quite a bit, and they are continuing to do so.
Some nice features, off the top of my head:
std::vector<Name>
instead ofstd::vector<std::basic_string<char, std::allocator<char>>, std::allocator<std::basic_string<char, std::allocator<char>> >
which makes all the difference)template
in case it's missing in a call to a template method from within another template methodBut each of those has required several hours to days of work.
They certainly didn't come for free.
Now, concepts should have (normally) made our lives easier. But they were mostly untested and so it was deemed preferable to remove them from the draft. I must say I am glad for this. Given C++ relative inertia, it's better not to include features that haven't been thoroughly revised, and the concept maps didn't really thrilled me. Neither did they thrilled Bjarne or Herb it seems, as they said that they would be rethinking Concepts from scratch for the next standard.
一般来说,我发现泛型的 Ada 编译器错误消息实际上并不比任何其他 Ada 编译器错误消息更难阅读。
另一方面,C++ 模板错误消息因 错误小说。我认为主要区别在于 C++ 进行模板实例化的方式。事实是,C++ 模板比 Ada 泛型灵活得多。它非常灵活,几乎就像一个宏预处理器。 Boost 中的聪明人已经使用它来实现诸如 lambda 之类的东西,甚至整个其他语言。
由于这种灵活性,每次第一次遇到模板参数的特定排列时,基本上都必须重新编译整个模板层次结构。因此,解决 API 下几层不兼容性的问题最终会呈现给糟糕的 API 客户端来解密。
在 Ada 中,泛型实际上是强类型的,并向客户端提供完整的信息隐藏,就像普通的包和子例程一样。因此,如果您确实收到一条错误消息,它通常只是引用您尝试实例化的一个泛型,而不是用于实现它的整个层次结构。
所以,是的,C++ 模板错误消息比 Ada 的错误消息要糟糕得多。
现在调试完全是一个不同的故事......
In general I found Ada compiler error messages for generics really not significantly more difficult to read than any other Ada compiler error messages.
C++ template error messages, on the other hand, are notorious for being error novels. The main difference I think is the way C++ does template instantiation. The thing is, C++ templates are much more flexible than Ada generics. It is so flexible, it is almost like a macro preprocessor. The clever folks in Boost have used this to implement things like lambdas and even whole other languages.
Because of that flexibility, the entire template hierarchy basically has to be compiled anew every time its particular permutation of template parameters is first encountered. Thus issues that resolve down to incompatibilities several layers down a API end up being presented to the poor API client to decipher.
In Ada, Generics are actually strongly typed, and provide full information hiding to the client, just like normal packages and subroutines do. So if you do get an error message, it is typically just referencing the one generic you are trying to instatiate, not the entire hierarchy used to implement it.
So yes, C++ template error messages are way worse than Ada's.
Now debugging is a different story entirely...
文章泛型编程概述了泛型的许多优点和缺点多种语言,包括特别是 Ada。尽管缺乏模板专业化,但所有 Ada 通用 < a href="http://www.adaic.org/resources/add_content/standards/05rm/html/RM-12-3.html" rel="noreferrer">实例“相当于实例声明…紧接着是实例主体”。实际上,错误消息往往发生在编译时,它们通常代表常见的类型安全违规行为。
The article Generic Programming outlines many of the pros and cons of generics in several languages, including Ada in particular. Although lacking template specialization, all Ada generic instances are "equivalent to the instance declaration…immediately followed by the instance body". As a practical matter, error messages tend to occur at compile-time, and they typically represent familiar violations of type-safety.
D 有两个功能可以提高模板错误消息的质量:约束和静态断言。
D has two features to improve the quality of template error messages: Constraints and
static assert
.