简体   繁体   English

调试模板实例化

[英]Debugging template instantiations

When doing metaprogramming using C++ templates, is there a method that can be used, sort of like a debugger, to step through how the templates are being instantiated and complied?在使用 C++ 模板进行元编程时,是否可以使用一种方法(有点像调试器)来逐步了解模板的实例化和编译方式? It seems right now, when creating a complicated network of templates, there really isn't a very good way of debugging them other than looking at the complier error messages to see how the templates are being instantiated (if there are any compiler errors), and the attempt to work backwards from the error messages if something unexpected is being generated.现在看来,在创建复杂的模板网络时,除了查看编译器错误消息以查看模板是如何实例化的(如果有任何编译器错误)之外,确实没有很好的调试方法,如果正在生成意外的东西,则尝试从错误消息向后工作。 I'm not really sure if what I'm looking for even exists, as it would have to be something that is done at compile time, but basically it would be a method, sort of like stepping through code and examining the stack frame in gdb at runtime, where the compiler could be stopped and the environment examined for the sequence by which a template or set of nested templates is being instantiated.我不确定我要找的东西是否存在,因为它必须是在编译时完成的,但基本上它是一种方法,有点像单步执行代码并检查堆栈帧gdb在运行时,编译器可以在其中停止,并检查环境以查看正在实例化模板或嵌套模板集的顺序。

For instance, let's say I created some simple code like the following:例如,假设我创建了一些简单的代码,如下所示:

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

I know this is relatively easy code to follow, but templates can get quite a bit more involved, especially when doing metaprogramming, recursion, etc. I understand that the complier will issue error messages that can be used to deduce how templates are being instantiated, but I'm also wondering what can be done when the actual template code is correct in a syntactic sense, but the runtime results are still incorrect.我知道这是相对容易遵循的代码,但是模板可能会涉及更多内容,尤其是在进行元编程、递归等时。我知道编译器会发出错误消息,可用于推断模板是如何实例化的,但我也想知道当实际模板代码在语法意义上是正确的,但运行时结果仍然不正确时,可以做什么。 It would be nice for instance to have a method to stop the compiler and see what test , as well as int_return_type and float_return_type , was being instantiated with, or what instantiations were failing.例如,如果有一种方法可以停止编译器并查看正在实例化的test以及int_return_typefloat_return_type ,或者哪些实例化失败, float_return_type了。

Are the only options available right now for debugging templates with this level of granularity 1) the compiler error messages when the code is incorrect, and 2) a combination of disassemblers and debuggers to see what instantiated code was generated if the run-time results are incorrect?是目前用于调试具有这种粒度级别的模板的唯一选项吗 1) 代码不正确时的编译器错误消息,以及 2) 反汇编器和调试器的组合,以查看在运行时结果是什么时生成的实例化代码不正确? Or are there some other utilities out there that help with "watching" how templates are instantiated, and see/inspect what code is generated by the compiler to investigate and debug template errors?或者是否有其他一些实用程序可以帮助“观察”模板的实例化方式,并查看/检查编译器生成的代码以调查和调试模板错误?

These are pretty basic, but they have worked for me in most cases.这些是非常基本的,但在大多数情况下它们对我有用。 I'm interested to see what others have to say too.我也有兴趣看看其他人怎么说。

Apologies for the contrived examples.为人为的例子道歉。

Use sandboxes使用沙箱

Starting with small sandboxes to test template code as soon as it starts behaving weird or you are doing something complicated.一旦模板代码开始出现异常或您正在做一些复杂的事情,就从小型沙箱开始测试模板代码。 I am pretty comfortable with templates and I still do this almost immediately.我对模板非常满意,而且我仍然几乎立即这样做。 Simply, it uncovers errors faster.简而言之,它可以更快地发现错误。 You have done it for us here, so I presume that this is moot.你已经在这里为我们做了,所以我认为这是没有实际意义的。

Specify temporary types指定临时类型

Temporaries can obfuscate where your intentions are not met.临时文件可能会混淆您的意图未得到满足的地方。 I have seen a lot of code that does something like the below.我已经看到很多代码做类似下面的事情。

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

Telling the compiler what type you expect will fail faster and potentially will give you a better message to deal with.告诉编译器你期望的类型会更快地失败,并且可能会给你一个更好的消息来处理。

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

Use typeid使用类型标识

Using typeid(T).name() to print template names in debug statements.使用typeid(T).name()在调试语句中打印模板名称。 This will give you a string that you can use to see how the compiler decided to fulfill the type.这将为您提供一个字符串,您可以使用它来查看编译器如何决定实现该类型。

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

Avoid unnecessary default implementations避免不必要的默认实现

Write templates in such a way that they don't have default implementations.没有默认实现的方式编写模板。

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

This enforces users of print_whole_number have their own my_traits specialization.这强制print_whole_number用户拥有自己的my_traits专业化。 They will get an compiler error instead of half working because you couldn't supply a good implementation for all types.他们会得到一个编译器错误而不是一半的工作,因为你不能为所有类型提供一个好的实现。 The compiler error won't be immediately helpful if used in a disparate part of a code base, admittedly.诚然,如果在代码库的不同部分使用,编译器错误不会立即有帮助。

Yes, there is a template metaprogramming debugger.是的,有一个模板元编程调试器。 Templight时间之光

In 2018 we have cppinsights.io . 2018 年,我们有cppinsights.io Not sure how useful it is for really complicated templates though.不过不确定它对于非常复杂的模板有多大用处。

I love using the excellent web-based Comeau compiler for debugging.我喜欢使用出色的基于 Web 的 Comeau 编译器进行调试。 It can notice errors in terms of standard compilance where the other compilers can't...它可以注意到其他编译器不能...

Comeau has the big advantage of giving a lot more readable error messages than GCC or MSVC. Comeau 的一大优势是提供比 GCC 或 MSVC 更具可读性的错误消息。

Also, remember to use static_assert 's everywhere where possible -- even if you're sure the answer is true.另外,请记住尽可能在任何地方使用static_assert - 即使您确定答案是正确的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM