Take the 2-minute tour ×

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.

share|improve this question
C++ would have nice error messages, if TMP was planned in any way. You could say it slipped into the language by 'mistake'. –  Xeo Apr 3 '11 at 11:28
STL Error Message Decryptor. Makes the pain mostly go away. –  Mat Apr 3 '11 at 11:36
@Mat: If only the active development wouldn't have stopped. :( Ain't going back to VC9, y'know. –  Xeo Apr 3 '11 at 11:39
@Serge Dundich if one compiler is giving such messages, surely, but if all compilers are giving such error messages, it means, that something is wrong in language. One of the main reasons to add concepts to C++ is to help compiler to produce more clear error messages. –  UmmaGumma Apr 3 '11 at 14:23
@Serge Dundich: Comeau C++ is designed by Greg Comeau. The standard authors are a diverse bunch from many companies. –  MSalters Apr 4 '11 at 8:49
show 10 more comments

6 Answers

up vote 12 down vote accepted

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.

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

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:

  • C considers it normal to declare a variable in stride after defining a struct
  • C considers it normal not to specify a return type for a function (in which case it defaults to int)

If we reason about, we could see that:

  • if foo names a type, then it belongs to the function declaration
  • if not, it probably denotes a variable... unless of course we made a typo and it was meant to be written fool, 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:

  • Terse but complete error messages, which include the source ranges to expose exactly where the error emanated from
  • Fix-It notes when it's obvious what was meant
  • In which case the compiler parses the rest of the file as if the fix had been there already, instead of spewing lines upon lines of gibberish
  • (recent) avoid including the include stack for notes, to cut out on the cruft
  • (recent) trying only to expose the template parameter types that the developper actually wrote, and preserving typedefs (thus talking about std::vector<Name> instead of std::vector<std::basic_string<char, std::allocator<char>>, std::allocator<std::basic_string<char, std::allocator<char>> > which makes all the difference)
  • (recent) recovering correctly in case of a missing template in case it's missing in a call to a template method from within another template method

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

share|improve this answer
"C considers it normal not to specify a return type for a function (in which case it defaults to int)". C++ however most definitely does not allow this grammar, and as the question concerns C++, I'd definitely edit this out. –  DeadMG Apr 3 '11 at 15:31
@DeadMG: I could not think of a good example of ambiguity in C++ (outside of templates) so I'll leave it like it for now :) If you have one yourself, then do edit the answer, I don't mind. –  Matthieu M. Apr 3 '11 at 15:48
There is much worse C++ specific ambiguities. E.g. what is declared by SomeType Foo(abcd); cannot be deduced from the syntax. Only environment gives the right answer: if abcd is type name then Foo is a function taking abcd-typed argument that return SomeType and if abcd is object name then Foo is object of type SomeType initialized with abcd value. This was a major problem for G++ developers for a long period. And lots of other type-name vs object-name problems. Anyway your message is great. +1 –  Serge Dundich Apr 4 '11 at 2:09
A*B = D+E;: If A is a type, that is a deceleration. If A is a variable that over loads operator* to return something that over loads assignment, that is a expression evaluation. –  BCS Apr 4 '11 at 5:08
@Serge, @BCS: Yes, those are ambiguous, but I was looking for ambiguity in the presence of an error (here a forgotten semi-colon) to illustrate why compiler would sometimes provide rather unhelpful messages. Thanks for the examples anyway, they nicely illustrate the issue with the grammar itself. –  Matthieu M. Apr 4 '11 at 6:21
add comment

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.

share|improve this answer
add comment

D has two features to improve the quality of template error messages: Constraints and 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
share|improve this answer
look interesting. So D doesn't suffer from such problem? And D's errors are always clear? –  UmmaGumma Apr 3 '11 at 14:35
C++0x has static_assert, so the 2nd part alone no longer really make D better. But combined with compile-time string processing, D's static assert's error reporting capability is far more superior (e.g. you can't show the q{a**b} and int in ideone.com/TQ6wY in C++0x). –  KennyTM Apr 3 '11 at 15:29
@Ashot: D doesn't suffer from this problem given well-written templates. It still does given quick and dirty templates. –  dsimcha Apr 3 '11 at 16:36
add comment

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.

share|improve this answer
Additionally, Ada source code is easier to read. This leads to more readable error messages. –  oenone Apr 4 '11 at 10:55
add comment

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

share|improve this answer
C++ templates are Turing complete, so they're more than just a macro preprocessor. –  naasking Dec 27 '11 at 15:55
add comment

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.

share|improve this answer
add comment

Your Answer


