你如何在c ++中调试大量的模板代码?

时间:2009-02-18 23:59:50

标签: c++ debugging templates metaprogramming

在使用C ++模板元编程时,我发现很难弄清楚我的代码有什么问题。可能我不是很擅长理解错误信息,但据我所知,我不能诉诸于打印语句或断点来弄清楚发生了什么。

在尝试弄清楚为什么某些东西不能编译时,您可以提供哪些提示或建议,而不仅仅是手动选择代码并希望它来找我?

11 个答案:

答案 0 :(得分:14)

对于STL,至少有一些工具可以输出更人性化的错误消息。见http://www.bdsoft.com/tools/stlfilt.html

对于非STL模板,您只需了解错误的含义。在你看了十几次后,你会更容易猜到问题是什么。如果你在这里发布它们,也许有人可以帮助你搞清楚。

答案 1 :(得分:7)

您可以尝试使用较新的编译器。如果您使用的是Visual C ++ 6.0,请切换到9.0,您会看到编译器错误的有用性大幅提升。

否则,我的技术通常是尽可能地测试一小段代码,直到我发现错误被隔离。这可能是模板系统最大的失败 - 没有合理的调试方法。

在您自己的代码中,自由使用compile-time asserts可以更容易地诊断使用问题。

答案 2 :(得分:5)

如果你正在使用gcc,我发现colorgcc可以提供一些帮助。通过颜色编码,可以更容易地在心理上解析警告与错误和上下文信息。

答案 3 :(得分:4)

当在元编程语言中复杂化时,我多次使用BOOST_MPL_ASSERT宏,检查元执行的每一步的结果。 Boost.MPL库对此非常有用。我建议你尽可能多地使用代码,因为它可能不会包含错误。

当我不确定正在使用类的正确特化时,我倾向于在命名空间中隔离正确的特化。一旦确定专业化有效,就必须确保选择它。如果不是,你必须找出哪个被选中。然后我建议使用Boost.EnableIf从选择过程中排除这个错误接受的专业化。

最后但并非最不重要的是,STLfilt非常有用,你可以自己修改它,以便它尽可能地满足你的需求。

但最重要的是尽量不要在任何地方使用元编程。它很复杂,所以只有在真正需要它时才使用它。

答案 4 :(得分:3)

这应该对你有帮助。

http://www.bdsoft.com/tools/stlfilt.html

我自己没有用过它;但是,它可能对你有帮助。我还可以告诉您,随着您获得更多模板和元程序的经验,您将习惯于错误消息。有时它们有点难以阅读;但是,他们的疯狂是有道理的。只需尽可能地使您的终端尽可能大,并在阅读时尝试翻译您脑海中的内容。

答案 5 :(得分:3)

您使用的是哪种编译器? VC8和9在输出可读错误消息方面实际上相当不错。它仍然需要一点耐心,但它可以完成,它们实际上显示了调用堆栈的编译时等效。从底部开始,哪个模板实例化导致错误,以及模板参数是什么?下一级别显示它实例化的模板,依此类推,一直到顶级。当然,这仅在“输出”选项卡中可见,而不是在编译失败后通常显示的“错误”。

GCC的原理类似,虽然最后我尝试过,但至少格式化的可读性稍差。但实际上,您只需要跟踪实例化堆栈,并在每个级别验证它是否已使用您期望的类型进行实例化,直到找到引入错误的类型为止。

这是一种痛苦,但它可以完成,它只需要耐心和阅读错误信息的意愿。 :)

此外,通过提供健全性检查,自由使用static_assert(或BOOST_STATIC_ASSERT)可以提供很多帮助

答案 6 :(得分:2)

随着时间的推移你已经习惯了,不幸的是,如果你计划使用C ++,你必须这样做。 因为,像VC9这样的库有很好的错误信息,但是一旦你转向说GCC或其他编译器,消息就会消失。甚至VC9也不会帮助你,当你有一些其他人写的库或者你自己在深夜写的错误时,甚至一些Boost库也不那么友好。仅仅因为并非每个作者都会在发生错误时把事情弄清楚,而这在新库中更常见(往往会出现最多的错误和帮助)。

此外,你必须记住,你可以在代码中找到的好的STATIC_ERRORS由作者放置,通常会破坏,并且总会有一些可怕的角落案例,作者没有考虑,会产生400行错误消息,因为你错过了某个地方的const。

使用工具会在一开始就帮助你,但从长远来看会伤到你。而且由于问题是C ++固有的问题,它不会很快消失。在C ++不再使用之前,这些错误可能会伴随我们。因此,当你需要它们来生存时,工具会切断你的牙齿。如果您计划很快离开C ++,请随时使用它们。如今我通常可以一目了然地理解这些400行错误信息,所以它们对我的眼睛来说非常清晰,但这不是因为任何工具。

与所有内容一样,特别是C ++,需要经验和培训。

答案 7 :(得分:2)

正如答案已经说明的那样,模板代码基本上有两种类型的困难: 1.让它编译,找出编译器错误的原因 2.让它在运行时做正确的事情

我通常尝试将编译时类型魔术与运行时逻辑分开,这有助于找到问题的原因(类型1或2)。 实现此目的的一种方法是使用一种模板类型用于魔术类型,并且尽可能少使用运行时功能,以及一种使用模板类型的运行时逻辑类型。

如果您按照其他回复中的建议,特别是有关compile_time断言的建议,将更容易找到麻烦的来源。

答案 8 :(得分:1)

Templight:C ++模板元程序调试器和分析器

答案 9 :(得分:0)

Metashell是模板调试的绝佳工具。在线模式!

答案 10 :(得分:0)

对于调试,通常仅在某个时候停止元程序并显示由某种类型计算得出的类型通常会有所帮助。可以这样实现:

template <class T>
struct mp_debug : T::MP_DEBUG_FORCE_COMPILE_FAILURE {};

using Foo = int; // type to be inspected

// usage at namespace scope
template struct mp_debug<Foo>;

// usage at function scope
int main() {
  mp_debug<Foo>{};
}

这将导致编译器打印一个编译时错误,该错误显示mp_debug的评估类型参数。