Debugging template instantiations
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. 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. 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.
How do you debug heavily templated code in c++?
For the STL at least there are tools available that will output more human-friendly error messages. See http://www.bdsoft.com/tools/stlfilt.html
For non-STL templates you'll just have to learn what the errors mean. After you've seen them a dozen times it becomes easier to guess what the problem is. If you post them here maybe somebody can help you figure it out.
inspect C++ template instantiation
With templates we simply don't have clean output facilities and there are no compilers i know of that allow you to directly view template instantiations. The closest i found regarding metaprogram debugging was a paper on Templight.
For now the best utilities seem to be:
- static asserts & concept checks (clearly assert your assumptions)
- the mentioned instantiation backtraces (e.g. by using static asserts)
- letting instantiations generate warnings (
boost::mpl::print
might do it) - a tracer, a custom class that gets passed as a template argument and is used to emit runtime output (introduced by C++ Templates - The Complete Guide)
How to debug template arguments at compile-time?
I believe you're using MSVC++, if so, then see the output window, it might have more info printed, especially the line number along with the filename. Once you know the file and line number, you can start from there.
Output window usually prints everything, like how and with what template argument(s), a template is instantiated. Everything step by step. Those messages are very useful when debugging.
As you found yourself, enabling /WL prints more detail messages in the output window.
How to debug and print a template alias type c++
A quick and dirty way:
template <typename T>
void print_type()
{
#ifndef _MSC_VER
std::cout << __PRETTY_FUNCTION__ << '\n';
#else
std::cout << __FUNCSIG__ << '\n';
#endif
}
What exactly is printed depends on the compiler. For print_type<int>();
, my Clang prints void print_type() [T = int]
.
See this thread for removing anything other than the type name from such strings.
Can I add a breakpoint only for a specific template instantiation?
I found it.
Just put a breakpoint in the line you want (I'll show an example with std::shared_ptr<>).
Then go to the Breakpoints window and notice that when it breaks, there's a little +
next to the breakpoint that will open all the different instantiations.
The line in bold is the breakpoint that is currently active.
Now, unfortunately, the Breakpoints window doesn't show you the actual template instantiation.
But, you can use the call stack to see which instantiation is currently used.
Or, you can right click on each of the breakpoints, and choose "Go To Disassembly".
This may give you a hint as to the actual template instantiation.
Then you can choose which breakpoints and for which type you want to keep active.
Edit:
You could also add the Function column to the Breakpoints window and see the actual template function.
Related Topics
Making a Template Parameter a Friend
What Does It Mean for a C++ Function to Be Inline
Complete C++ I18N Gettext() "Hello World" Example
Why Do Lambda Functions in C++11 Not Have Function<> Types
How Does Overloading of Const and Non-Const Functions Work
What's the Point of G++ -Wreorder
Passing Const Char* as Template Argument
Must the Int Main() Function Return a Value in All Compilers
How to Have Static Data Members in a Header-Only Library
"If" Argument Evaluation Order
Is It Counter-Productive to Pass Primitive Types by Reference
How to Alloc a Executable Memory Buffer
Is It Legal to Declare a Constexpr Initializer_List Object
Image Scaling and Rotating in C/C++
C++ String Literal Data Type Storage