C++ template metaprogramming is like writing a program in Prolog without a debugger. The compiler executes your metaprogram by running through a bunch of pattern-matching rules. But unlike Prolog, the C++ template metaprogramming language doesn’t have a debugger. You just feed your code to the compiler, and you get a few possible results:
- It fails to compile.
- It compiles and gives you what you want.
- It compiles and gives you something that wasn’t what you want.
Only if you’re lucky do you get case 2 on the first try.
There’s no way to single-step through your metaprogram, and there’s no print-debugging either. All you can do is see what the compiler says.
Here’s a trick I use to get something. It’s not great, but it’s still handy.
template<typename... Args> void whatis();
This is a forward declaration of a function that takes an arbitrary number of type arguments.
I can drop a call to this function at various points in my template metaprogram to see how the compiler deduced a type:
template<typename T> void f(T&& t) { whatis<T>(); ... other stuff ... }
When I instantiate f
, a call to whatis<T>
is made, among all the other stuff. I can look at the compiler output or the linker’s “unresolved external” error message to see what T
ended up being.
double v = 3.0; f(v); // msvc ??$f@AEAN@@YAXAEAN@Z PROC ; f<double &>, COMDAT ... other stuff ... call ??$whatis@AEAN@@YAXXZ ; whatis<double &> ... other stuff ... ??$f@AEAN@@YAXAEAN@Z ENDP ; f<double &> unresolved external symbol "void __cdecl whatis<double &>()" (??$whatis@AEAN@@YAXXZ) // gcc _Z1fIRdEvOT_: ... other stuff ... call _Z6whatisIJRdEEvv ... other stuff ... undefined reference to `void whatis<double&>()'
Aha, in this instantiation of f
, the type T
was deduced to be double&
.
It’s not a super-awesome trick, but with template metaprogramming, every little bit helps.
There’s a better technique (by STL, popularised by Scott Meyers):
The advantage is that you don’t have to wait for the linking phase – the compiler will complain straight away.
I prefer to wait until the linking stage, because that lets me see two things at once: Whether the code would have compiled, and what the type is. I use this technique mostly on gcc.godbolt.com.
I thought you were not allowed to mention anything non-Microsoft by mentioning brand names in this blog.
Holding my breath for someone to implement a meta-debugger for template metaprogramming, using template metaprogramming.
This is the most profound blog entry you have ever written. I have bookmarked it. I think you have just taught me everything that I need to know about this metaprogramming thing. I might use this for inspiration in a future version of our product.
The other day, I decided to resurrect MS InfoViewer as a (Django?) web site, complete with "Dr. GUI's Espresso Stand" and everything. I'll include your blog somehow -- i.e., via scraping...
It might be handy to include a link to an MSVC demangler (like http://demangler.com/) so that folks can demangle `??$whatis@AEAN@@YAXXZ` into a more readable type `void __cdecl whatis<double & __ptr64>(void)`
The compiler undecorates it for you in the comment, as I noted. If you really want to undecorate it yourself, you can use the undname program that comes with Visual Studio.