36

会使这些工具变得不必要的概念不是 C++11 的一部分

有哪些生产质量工具可用于破译源自基于模板的代码的错误消息?Eclipse-CDT 支持也会很好。:)

如果我放弃 C++11,我对 C++98 有什么选择?


相关问题:

4

3 回答 3

19

让我们来回答一下(我标记了这个社区维基,所以我们一起得到了很好的回应)......

我很长时间以来一直在使用模板,并且错误消息通常以某种方式得到了改进:

  • 编写一堆错误会创建更多文本,但通常还包括用户正在查看的级别,这通常包括对实际问题的提示。鉴于编译器只看到一个翻译单元被扔给它,因此无法确定堆栈中的哪个错误最适合用户。
  • 使用概念检查器,即执行模板参数的所有必需成员并可能生成错误消息的类或函数,使用static_assert()为模板作者提供了一种告诉用户显然不成立的假设的方法。
  • 告诉用户他编写的类型而不是像编译器希望在最低级别看到的那样扩展所有类型定义也有帮助。clang 非常擅长这一点,并且实际上会给您错误消息,例如谈论std::string而不是将事物类型扩展到最终的结果。

该技术的组合实际上会导致例如 clang 创建相当不错的错误消息(即使它还没有实现 C++2011;但是,没有编译器可以做到,据我所知 gcc 和 clang 处于领先地位) . 我知道其他编译器开发人员积极致力于改进模板错误消息,因为许多程序员发现模板实际上是一个巨大的飞跃,即使错误消息需要一些时间来适应。

像 stlfilt 这样的一个问题工具是 C++ 编译器和库正在积极开发中。这会导致错误消息一直在移动,从而导致工具接收不同的输出。虽然编译器编写者致力于改进错误消息是件好事,但对于那些试图从他们得到的错误消息中工作的人来说,这肯定会让他们的生活变得更加困难。这还有另一面:一旦检测到某个错误模式很常见并被 stlfilt 拾取(好吧,据我所知,它没有被积极维护)编译器编写者可能热衷于报告错误直接遵循这些模式,可能还提供编译器可用但之前未发出的附加信息。换个说法,我希望编译器编写者很容易接受用户描述常见错误情况以及如何最好地报告它们的报告。编译器编写者自己可能不会遇到错误,因为他们正在处理的代码实际上是 C(例如 gcc 是用 C 实现的),或者因为他们习惯于某些模板技术以避免某些错误(例如省略typename对于依赖类型)。

最后,要解决有关具体工具的问题:当我遇到编译器抱怨某些模板实例化时,我使用的主要“工具”是使用不同的编译器!尽管情况并非总是如此,但通常一个编译器会报告一个完全难以理解的错误消息,这只有在看到另一个编译器的相当简洁的报告后才有意义(如果您有兴趣,我经常使用最新版本的 gcc、clang 和 EDG为了这)。但是,我不知道像 stlfilt 这样的易于打包的工具。

于 2012-01-08T20:00:49.130 回答
9

我知道这可能没有您想要的那么有用,但我发现针对模板错误消息的最佳工具是知识

对 STL 以及如何使用它的良好理解将帮助您从一开始就避免很多错误。其次,错误消息通常指的是 STL 源代码中的函数——如果您大致了解 STL 是如何实现的,那么这对于破译错误消息的内容非常有帮助。最后,编译器制造商已经意识到了这个问题,并且正在逐步改进错误消息输出,因此您最好坚持使用最新版本的编译器。

这是一个不起眼的模板错误的好例子:

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

unique_ptr不可复制,只能移动。因此,尝试将 unique_ptr 的向量分配给另一个向量将意味着向量源代码中的某处将尝试复制唯一指针。因此,错误将源自不属于您的代码,并因此引发相当不透明的错误消息。理想的错误信息是

main.cpp(20):无法从 'foo' 构造 'bar':foo 的模板类型不可复制

相反,VS2010 给出了以下错误:

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

从中筛选出一些线索。第一部分引用 的私有成员访问std::unique_ptr<int>。第二部分,如果您单击源代码行,则指向 的复制构造函数unique_ptr,该构造函数在说明符下方声明private:。所以现在我们知道我们试图复制一个不允许的 unique_ptr。第 3、4 和 5 节只是指向样板代码 - 这只是噪音。第 6 节说“请参阅正在编译的类模板实例化 'std::_Vector_val<_Ty,_Alloc>' 的引用”。换句话说,这个错误发生在vector的模板代码中。最后一部分是最有趣的:它直接指向在foo您自己的源代码中声明的行 - 它找出了您自己的源代码中错误的来源!

所以把线索加起来:

  • 它起源于foo,
  • 它起源于矢量代码,
  • 它试图复制一个不允许的 unique_ptr。
  • 结论:向量试图复制其中一个元素,这是不允许的。查看代码foo并检查是否有任何导致复制的内容。

由于编译器只指向 foo 的声明,如果赋值在源代码中很远,就会涉及一些搜索。这显然并不理想,但我认为这种方法最终会给你更多的机会来纠正错误。您将开始认识到这种错误转储意味着“您复制了 unique_ptr”。再说一次,我不是在为它辩护,它肯定需要改进——但我认为现在输出中只有足够的信息,再加上对 STL 的深入了解,您可以解决问题。

于 2012-01-15T12:01:27.097 回答
4

我发现 Clang 可以为大量模板化的代码生成最好的错误消息。当然,在大多数情况下,冗长是不可避免的,但在大多数情况下,它仍然比 GCC 或 MSVC 好。这是 AshleysBrain 发布的示例代码的 Clang 错误消息:

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

它仍然很长而且很丑陋,但在我看来,问题出在哪里/在哪里要清楚得多。

于 2012-01-16T00:28:26.080 回答