May 28th, 2020

Template metaprogramming trick: Get the compiler to tell you what type you have

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:

  1. It fails to compile.
  2. It compiles and gives you what you want.
  3. 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.

Topics
Code

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

7 comments

Discussion is closed. Login to edit/delete existing comments.

  • Alex Alabuzhev

    There’s a better technique (by STL, popularised by Scott Meyers):

    template<typename...>
    class TD; // TD == "Type Displayer"
    ...
    TD<T> Type;

    The advantage is that you don’t have to wait for the linking phase – the compiler will complain straight away.

    • Raymond ChenMicrosoft employee Author

      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.

      • Erkin Alp Güney

        I thought you were not allowed to mention anything non-Microsoft by mentioning brand names in this blog.

  • Letao Wang

    Holding my breath for someone to implement a meta-debugger for template metaprogramming, using template metaprogramming.

  • Leif Strand

    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...

    Read more
  • Brian MacKay

    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)`

    • Raymond ChenMicrosoft employee Author

      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.