调试模板实例化

问题描述 投票:0回答:4

使用 C++ 模板进行元编程时,是否可以使用一种类似于调试器的方法来逐步完成模板的实例化和编译?现在看来,在创建复杂的模板网络时,除了查看编译器错误消息以了解模板如何实例化(如果有任何编译器错误)之外,确实没有很好的调试它们的方法,如果产生意外情况,则尝试从错误消息中逆向工作。我不确定我正在寻找的东西是否存在,因为它必须是在编译时完成的事情,但基本上它是一种方法,有点像单步执行代码并检查堆栈帧

gdb
在运行时,可以停止编译器并检查环境以查找实例化模板或一组嵌套模板的顺序。

例如,假设我创建了一些简单的代码,如下所示:

template<typename T, typename R = void>
struct int_return_type {};

template<typename R>
struct int_return_type<int, R>
{
    typedef R type;
};

template<typename T, typename R = void>
struct float_return_type {};

template<typename R>
struct float_return_type<float, R> 
{
    typedef R type;
};

template<typename T>
typename int_return_type<T>::type test()
{
    cout << "T type is int" << endl;
}

template<typename T>
typename float_return_type<T>::type test()
{
    cout << "T type is float" << endl;
}

int main()
{
    test<int>();
    test<float>();
    return 0;
}

我知道这是相对容易遵循的代码,但模板可能会涉及更多,特别是在进行元编程、递归等时。我知道编译器会发出错误消息,可用于推断模板是如何实例化的,但我也想知道当实际的模板代码在语法意义上是正确的,但运行时结果仍然不正确时,可以做什么。例如,如果有一种方法来停止编译器并查看正在实例化什么

test
,以及
int_return_type
float_return_type
,或者哪些实例化失败,那就太好了。

目前用于调试具有这种粒度级别的模板的唯一可用选项是:1)代码不正确时的编译器错误消息,2)反汇编器和调试器的组合,以查看如果运行时结果生成了哪些实例化代码是不正确的?或者是否有其他一些实用程序可以帮助“观察”模板的实例化方式,并查看/检查编译器生成的代码以调查和调试模板错误?

c++ c++11
4个回答
37
投票

这些都是非常基本的,但在大多数情况下它们对我有用。我也有兴趣看看其他人怎么说。

对人为的例子表示歉意。

使用沙箱

一旦模板代码开始表现奇怪或者您正在做一些复杂的事情,就从小沙箱开始测试模板代码。我对模板非常满意,而且我仍然几乎立即这样做。简而言之,它可以更快地发现错误。你已经在这里为我们做了这件事,所以我认为这是没有意义的。

指定临时类型

临时工具可能会混淆你的意图没有得到满足的地方。我见过很多代码都执行如下所示的操作。

template<typename T>
  T calc(const T &val) {
    return some_other_calc(val) / 100.0;
  }

告诉编译器你期望什么类型会失败得更快,并且可能会给你一个更好的消息来处理。

template<typename T>
  T calc(const T &val) {
    T val_ = some_other_calc(val);
    return val_ / 100.0;
  }

使用typeid

使用

typeid(T).name()
在调试语句中打印模板名称。这将为您提供一个字符串,您可以使用它来查看编译器如何决定实现该类型。

template<typename T>
  typename void test() {
    std::cout << "testing type " << typeid(T).name() << std::endl;
    // ...
  }

避免不必要的默认实现

以这样的方式编写模板,使其具有默认实现。

template<typename T, bool is_integral = boost::is_numeric<T>::value >
  struct my_traits;

template<typename T>
  struct my_traits<T, true> {
    typedef uint32_t cast_type;
  };

template<typename T>
  void print_whole_number(T &val) {
    std::cout << static_cast<my_traits<T>::cast_type>(val) << std::endl;
  }

这强制

print_whole_number
的用户拥有自己的
my_traits
专业化。他们会得到一个编译器错误,而不是 half 工作,因为你无法为所有类型提供良好的实现。诚然,如果在代码库的不同部分中使用,编译器错误不会立即有帮助。


11
投票

是的,有一个模板元编程调试器。 圣光


4
投票

我喜欢使用优秀的基于 Web 的 Comeau 编译器进行调试。它可以注意到标准兼容性方面的错误,而其他编译器则无法...

Comeau 的一大优势是提供比 GCC 或 MSVC 更多可读的错误消息。

此外,请记住尽可能使用

static_assert
- 即使您确定答案是正确的。


4
投票

2018 年我们有 cppinsights.io。但不确定它对于非常复杂的模板有多大用处。

© www.soinside.com 2019 - 2024. All rights reserved.