36

Concepts, that would render these tools unnecessary, are not part of C++11.

  • STLFilt would have been one option but it is no longer maintained.

  • Clang claims to give expressive diagnostics although important C++11 features are not available yet.

  • colorgcc seems to be abandoned since 1999.

What production quality tools are available to decipher error messages stemming from template-based code? Eclipse-CDT support would be nice too. :)

If I give up on C++11, what options do I have for C++98?


Related questions:

Community
  • 1
  • 1
Ali
  • 56,466
  • 29
  • 168
  • 265
  • 1
    Which C++11 features are you specifically requiring that clang doesn't have yet? – Ben Voigt Jan 08 '12 at 17:40
  • 1
    @BenVoigt [According to the Clang website](http://clang.llvm.org/cxx_status.html) Initializer lists, New wording for C++0x lambdas, Inheriting constructors, Universal character name literal, User-defined literals are not available. Does Clang indeed provide better error messages for templates? I have never tried it. – Ali Jan 08 '12 at 18:02
  • What do concepts have to do with anything? As you point out, they are not in the language. – Lightness Races in Orbit Jan 08 '12 at 18:25
  • @LightnessRacesinOrbit From the STLFilt website "Active Development on STLFilt has ended. The author sincerely hopes the C++ Standards Committee adopts "Concepts" sooner rather than later, rendering tools such as STLFilt unnecessary..." – Ali Jan 08 '12 at 18:37
  • @Ali: I don't see how concepts would really help. – Lightness Races in Orbit Jan 08 '12 at 18:38
  • 1
    @Lightness One of the main selling points of Concepts was always better error messages - eg "Foo is not CopyConstructible because ..." rather than an obscure error deep inside the library implementation. – Alan Stokes Jan 08 '12 at 19:05
  • @LightnessRacesinOrbit From [Bjarne Stroustrup's website, concepts](http://www2.research.att.com/~bs/C++0xFAQ.html#concepts): errors "are caught immediately at the point of use and that error messages are greatly improved". – Ali Jan 08 '12 at 19:14
  • @LightnessRacesinOrbit You made a valid point, I improved the question. – Ali Jan 08 '12 at 19:20
  • @Ali: Oh I think that's much clearer now :) – Lightness Races in Orbit Jan 08 '12 at 19:24
  • 1
    @AlanStokes: When playing with conceptgcc I couldn't find that it provides better error messages. It made implementing templates a lot harder, though. I can see some arguments for concepts but I haven't seen anything delivering them. ... but than, Larisse is working on [conceptclang](http://www.generic-programming.org/software/ConceptClang/) and I haven't looked at this, yet: maybe this works better. – Dietmar Kühl Jan 08 '12 at 19:35
  • @BenVoigt - My personal list also includes atomics, mutexes and initializer lists. – Omnifarious Jan 08 '12 at 19:45
  • @LightnessRacesinOrbit Well, thanks for pointing out the part that had to be improved. – Ali Jan 08 '12 at 19:58
  • @Omnifarious I started with the initializer lists :) – Ali Jan 08 '12 at 19:59
  • @DietmarKühl Regarding you experience with conceptgcc: wasn't it just the tools immaturity that caused the poor error messages? Or concepts don't really help in improving the error messages? I would happy to hear about your experience with conceptlang. – Ali Jan 08 '12 at 20:03
  • 1
    @Ali: So far I haven't tried using `conceptclang`. You are right in some ways about `conceptgcc`: it never got to a stage where it implemented things which would have made it useful. My main gripes were that it really made implementing very general templates, composed out of possibly many basic concepts which are somehow interrelated, a Royal Pain. – Dietmar Kühl Jan 09 '12 at 00:00

3 Answers3

21

Let's have a stab at an answer (I marked this community wiki so we get a good response together)...

I'm working since a long time with templates and error messages have generally improved in some way or another:

  • Writing a stack of errors creates a lot more text but also typically includes the level the user is looking at and this generally includes a hint at what the actual problem is. Given that the compiler only sees a translation unit tossed at it, there isn't a lot which can be done determining which error in the stack is the one most suitable for the user.
  • Using concept checkers, i.e. classes or functions which exercise all the required members of template arguments and possibly generating errors messages using static_assert() give the template author a way to tell users about assumptions which apparently don't hold.
  • Telling the user about types he writes rather than expanding all typedefs as the compiler like to see at the lowest level also helps. clang is rather good at this and actually gives you error messages e.g. talking about std::string rather than expanding things type to whatever it ends up to be.

A combination of the technique actually causes e.g. clang to create quite decent error message (even if it doesn't implement C++2011, yet; however, no compiler does and as far as I can tell gcc and clang are leading the pack). I know other compiler developers actively work on improving the template error messages as lots of programmers have discovered that templates actually are a huge leap forward even though the error messages are something which takes a bit of getting used to.

One problem tools like stlfilt face is that C++ compilers and libraries are under active development. This results in error messages shifting all the time, causing the tool to receive different outputs. While it is good that compiler writers work on improving error messages, it certainly makes life harder for people who try to work from the error messages they got. There is another side to this as well: once a certain error pattern is detected to be common and is picked up e.g. by stlfilt (well, it isn't actively maintained as far as I know) compiler writers are probably keen to report the errors following these patterns directly, possibly also providing additional information available to the compiler but not emitted before. Put differently, I would expect that compiler writers are quite receptive to reports from users describing common error situations and how they are best reported. The compiler writers may not encounter the errors themselves because the code they are working on is actually C (e.g. gcc is implemented in C) or because they are so used to certain template techniques that they avoid certain errors (e.g. omission of typename for dependent types).

Finally, to address the question about concrete tools: the main "tool" I'm using when I get kind of stuck with a compiler complaining about some template instantiation is to use different compilers! Although it isn't always the case but often one compiler reports an entirely incomprehensible error messages which only makes sense after seeing the fairly concise report from another compiler (in case you are interested, I regularly use recent version of gcc, clang, and EDG for this). I'm not aware of a readily packaged too like stlfilt, however.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
8

I know this may not be as helpful as you wanted, but I've found the best tool against template error messages is knowledge.

A good understanding of the STL and how to use it will help you avoid lots of errors in the first place. Secondly, often error messages refer to functions in the STL source - if you have a rough idea how the STL is implemented, this can be extremely helpful in deciphering what the error message is going on about. Finally, compiler makers are aware of this issue and are gradually improving error message output, so you would do well to stick to the latest version of your compiler.

Here's a good example of an obscure template error:

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

unique_ptr is not copyable, it can only be moved. So trying to assign a vector of unique_ptr to another vector will mean somewhere in the vector source code will try to copy a unique pointer. Therefore the error will originate from code which is not yours and throw a fairly opaque error message as a result. The ideal error message would be

main.cpp(20): cannot construct 'bar' from 'foo': foo's template type is non-copyable

Instead, VS2010 gives the following 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>          ]

Sifting through this there are clues. The first section references a private member access of std::unique_ptr<int>. The second section, if you click through to the source line, points at the copy constructor of unique_ptr, which is declared beneath a private: specifier. So now we know we tried to copy a unique_ptr which is not allowed. Sections 3, 4 and 5 just point to boilerplate code - it's just noise. Section 6 says "see reference to class template instantiation 'std::_Vector_val<_Ty,_Alloc>' being compiled". In other words, this error happened in vector's template code. The last section is most interesting: it directly points at the line declaring foo in your own source code - it's figured out where in your own source code the error originated from!

So adding up the clues:

  • It originates in foo,
  • It originates in vector code,
  • It tries to copy a unique_ptr which is not allowed.
  • Conclusion: the vector tried to copy one of its elements, which is not allowed. Review code for foo and check for anything causing a copy.

Since the compiler only pointed at foo's declaration, if the assignment is far away in the source code some hunting will be involved. This obviously is not ideal, but I think this approach ultimately gives you more chance of fixing mistakes in general. You'll start to recognise that kind of error dump means "you copied a unique_ptr". Again, I'm not defending it, it definitely needs improving - but I think these days there's just enough information in the output that combined with a good knowledge of the STL allows you to fix the problem.

AshleysBrain
  • 22,335
  • 15
  • 88
  • 124
  • 1
    +1. Unfortunately, you are right in your first paragraph. This makes C++ a very hard language to use properly (and this is only the tip of the iceberg: for instance, exception safety considerations in C++11 gives me painful headaches, and I consider myself to be an experienced C++ programmer). The most useful answer to the question, in view of the current state of the C++ language, is indeed *"deal with it, the price you pay by using C++ should be weighed against the advantages in term of control it provides"*. – Alexandre C. Jan 15 '12 at 12:12
  • Theoretically, those messages could easily be improved even without special tools or language-level concepts (*especially* in C++11) by implementing concepts in a round-about way with SFINAE and `static_assert` (or non-C++11 equivalent). I think you can test with [`is_copy_constructible`](http://en.cppreference.com/w/cpp/types/is_copy_constructible) in C++11 and throw that inside `operator=` (or any operator that needs the type to be copyable). – Xeo Jan 15 '12 at 12:53
  • @AshleysBrain +1 and thanks. Yes, I have acquired some knowledge of the STL internals over the years, and it is mainly due to the error messages :) Also, when I have no idea what gcc is complaining about, I try VS2010 and it usually helps (Dietmar Kühl also pointed that out). – Ali Jan 15 '12 at 16:46
  • Downvoted because OP wasn't asking for a lesson in reading the existing error messages, OP was asking for a tool to improve the messages. – Jeff Sep 28 '13 at 00:24
4

I have found Clang to generate the best error messages for heavily templated code. Of course verbosity is unavoidable in most cases, but it's still better than GCC or MSVC most of the time. Here's the Clang error message for the example code posted by 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.

It's still long and ugly, but in my opinion much clearer regarding what/where the problem is.

Reuben Morais
  • 1,013
  • 1
  • 8
  • 20