Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I see the type deduced for a template type parameter?

Is there an easy way to force compilers to show me the type deduced for a template parameter? For example, given

template<typename T>
void f(T&& parameter);

const volatile int * const pInt = nullptr;
f(pInt);

I might want to see what type is deduced for T in the call to f. (I think it's const volatile int *&, but I'm not sure.) Or given

template<typename T>
void f(T parameter);

int numbers[] = { 5, 4, 3, 2, 1 };
f(numbers);

I might want to find out if my guess that T is deduced to be int* in the call to f is correct.

If there's a third-party library solution (e.g., from Boost), I'd be interested to know about it, but I'd also like to know if there's an easy way to force a compilation diagnostic that would include the deduced type.

like image 300
KnowItAllWannabe Avatar asked Aug 21 '13 23:08

KnowItAllWannabe


1 Answers

Link time solution:

On my platform (OS X), I can get the linker to give me this information by simply making a short program that is complete, minus the definition of the function I'm curious about:

template<typename T>
void f(T&& parameter);  // purposefully not defined

int
main()
{
    const volatile int * const pInt = nullptr;
    f(pInt);
}

Undefined symbols for architecture x86_64:
  "void f<int const volatile* const&>(int const volatile* const&&&)", referenced from:
      _main in test-9ncEvm.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Admittedly I get the "triple reference", which should be interpreted as an lvalue reference (due to reference collapsing), and is a demangling bug (perhaps I can get that fixed).


Run time solution:

I keep a type_name<T>() function handy for this type of thing. A completely portable one is possible, but sub-optimal for me. Here it is:

#include <type_traits>
#include <typeinfo>
#include <string>

template <typename T>
std::string
type_name()
{
    typedef typename std::remove_reference<T>::type TR;
    std::string r = typeid(TR).name();
    if (std::is_const<TR>::value)
        r += " const";
    if (std::is_volatile<TR>::value)
        r += " volatile";
    if (std::is_lvalue_reference<T>::value)
        r += "&";
    else if (std::is_rvalue_reference<T>::value)
        r += "&&";
    return r;
}

I can use it like:

#include <iostream>

template<typename T>
void f(T&& parameter)
{
    std::cout << type_name<T>() << '\n';
}

int
main()
{
    const volatile int * const pInt = nullptr;
    f(pInt);
}

which for me prints out:

PVKi const&

That's not terribly friendly output. Your experience may be better. My platform ABI is based on the Itanium ABI. And this ABI includes this function:

namespace abi
{
    extern "C"
    char*
    __cxa_demangle(const char* mangled_name, char* buf, size_t* n, int* status);
}

I can use this to demangle C++ symbols into a human readable form. An updated type_name<T>() to take advantage of this is:

#include <type_traits>
#include <typeinfo>
#include <string>
#include <memory>
#include <cstdlib>
#include <cxxabi.h>

template <typename T>
std::string
type_name()
{
    typedef typename std::remove_reference<T>::type TR;
    std::unique_ptr<char, void(*)(void*)> own
        (
            abi::__cxa_demangle(typeid(TR).name(), nullptr, nullptr, nullptr),
            std::free
        );
    std::string r = own != nullptr ? own.get() : typeid(TR).name();
    if (std::is_const<TR>::value)
        r += " const";
    if (std::is_volatile<TR>::value)
        r += " volatile";
    if (std::is_lvalue_reference<T>::value)
        r += "&";
    else if (std::is_rvalue_reference<T>::value)
        r += "&&";
    return r;
}

And now the previous main() prints out:

int const volatile* const&
like image 141
Howard Hinnant Avatar answered Oct 31 '22 16:10

Howard Hinnant