27

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.

UmmaGumma
  • 5,633
  • 1
  • 31
  • 45
  • 6
    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 03 '11 at 11:28
  • 1
    [STL Error Message Decryptor](http://www.bdsoft.com/tools/stlfilt.html). Makes the pain mostly go away. – Mat Apr 03 '11 at 11:36
  • 1
    @Mat: If only the active development wouldn't have stopped. :( Ain't going back to VC9, y'know. – Xeo Apr 03 '11 at 11:39
  • I'm using scala, a language on top of the JVM, which is functional and more elaborated than Java. But I would need to know which ugly errors you're talking about, because I didn't do much with C++, especially not TMP. – user unknown Apr 03 '11 at 11:54
  • 1
    @user if you try to compile C++ code, which intensively uses templates, boost etc.. You can get very long (several pages) and ugly errors. – UmmaGumma Apr 03 '11 at 12:00
  • Are you talking about errors or error messages? – user unknown Apr 03 '11 at 12:19
  • It is not C++ or its template support. It is compilers. – Serge Dundich Apr 03 '11 at 14:10
  • 6
    @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 03 '11 at 14:23
  • 1
    @Ashot Martirosyan: "but if all compilers are giving such error messages, it means, that something is wrong in language" Not necessarily. As for C++ for what it matters I agree. It is possible to write a good compiler that produces easy-readable error messages but it is obviously of less priority then just implement C++ features to work according to the standard. And it turns out that just producing compatible implementation is a big problem (AFAIK solved in one implementation only - Comeau C++ designed by the standard authors). – Serge Dundich Apr 04 '11 at 01:45
  • 1
    @Ashot Martirosyan: nevertheless most of the ugly multi-line error messages come from full template specialization expansion instead of just writing type name as it appears in the code. Obviously it is totally compiler design problem - not C++. – Serge Dundich Apr 04 '11 at 02:17
  • 1
    @Serge Dundich: Comeau C++ is designed by Greg Comeau. The standard authors are a diverse bunch from many companies. – MSalters Apr 04 '11 at 08:49
  • 1
    @MSalters: True. AFAIK Greg Comeau (personally and his company) is the second major contributor (after Stroustrup) to C++ language development and to its standardization process. So I usually consider their compiler as reference implementation. – Serge Dundich Apr 04 '11 at 11:33
  • 1
    @Serge Dundich: It may have changed since I left, but I doubt it. Just look at the papers: I can't find any with Greg's name on it (and I checked all since the original 1998 standard) OTOH, the EDG folks did write a stack of papers (and still do). – MSalters Apr 04 '11 at 13:29
  • 1
    @MSalters: Maybe they didn't participate in committees directly. But contributed to C++ language from the early days and supported all the latest standard (and drafts') features all the time. Look [here](http://www.comeaucomputing.com/faqs/genfaq.html#history). – Serge Dundich Apr 05 '11 at 09:01
  • 1
    C♯ and Eiffel generics are different to C++ templates: With Eiffel and C♯ you say this method takes a G that implements X. In C++ templates you just say it takes a class T, and then latter when it is used, it tries to see if the type has what ever methods are needed (compile time / static “duck typing” http://en.wikipedia.org/wiki/Duck_typing). This static duck typing approach is why the errors messages are not clear. – ctrl-alt-delor Mar 26 '14 at 09:40

6 Answers6

18

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

Al.G.
  • 4,327
  • 6
  • 31
  • 56
T.E.D.
  • 44,016
  • 10
  • 73
  • 134
17

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

Arlen
  • 6,641
  • 4
  • 29
  • 61
Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • 6
    "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. – Puppy Apr 03 '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 03 '11 at 15:48
  • 3
    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 04 '11 at 02:09
  • 2
    `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 04 '11 at 05: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 04 '11 at 06:21
11

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.

trashgod
  • 203,806
  • 29
  • 246
  • 1,045
9

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
}
dsimcha
  • 67,514
  • 53
  • 213
  • 334
  • look interesting. So D doesn't suffer from such problem? And D's errors are always clear? – UmmaGumma Apr 03 '11 at 14:35
  • 3
    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 http://ideone.com/TQ6wY in C++0x). – kennytm Apr 03 '11 at 15:29
  • 1
    @Ashot: D doesn't suffer from this problem given well-written templates. It still does given quick and dirty templates. – dsimcha Apr 03 '11 at 16:36
5

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.

Lothar
  • 12,537
  • 6
  • 72
  • 121
2

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.

Flexo
  • 87,323
  • 22
  • 191
  • 272