Print template typename at compile time
Asked Answered
O

7

48

When creating a template function in C++ is there a simple way to have the typename of the template represented as a string? I have a simple test case to show what I'm trying to do (note the code shown does not compile):

#include <stdio.h>
template <typename type>
type print(type *addr) 
{
  printf("type is: %s",type);
}

int main()
{
  int a;
  print(&a);
}

// Would like to print something like:
// type is: int

I think that the typename should be available at compile time when the function is instantiated, but I'm not that familiar with templates and I haven't seen a way to get the typename as a string.

The reason that I want to do this is for some printf type debugging. I have multiple threads running and stepping through with gdb changes the program behavior. So for some things I want to dump information about which functions were executing. It's not too important so if the solution is overly complex I would skip adding this information to my logging function. But if there was a simple way to do this it would be useful information to have.

Ovaritis answered 21/3, 2012 at 21:42 Comment(5)
Try typeid (type).name() after including <typeinfo>Instill
do you strictly need it at compile time? otherwise, typeid(type).name() might help.Upchurch
Never mind, didn't see the compile-time thing, but if you're printing it, I'm sure you can figure it out at run-time.Instill
I once had a question to see if a template argument was void so it wouldn't return anything. If it helps, it's here: #9626026Instill
I don't think there's a way to get the name at compile time, since you couldn't do much of anything useful with it. typeid::name() is the right answer there.Koffler
P
24

__PRETTY_FUNCTION__ should solve your problem (at run time at least)

The output to the program below is:

asfdasdfasdf test<type>::test() [with type = int]
asfdasdfasdf test<type>::test() [with type = int]
asfdasdfasdf test<type>::test() [with type = int]
asfdasdfasdf test<type>::test() [with type = int]
asfdasdfasdf test<type>::test() [with type = int]
asfdasdfasdf test<type>::test() [with type = int]
asfdasdfasdf void tempFunction() [with type = bool]
asfdasdfasdf void tempFunction() [with type = bool]
asfdasdfasdf void tempFunction() [with type = bool]
asfdasdfasdf void tempFunction() [with type = bool]
asfdasdfasdf void tempFunction() [with type = bool]
asfdasdfasdf void tempFunction() [with type = bool]
!!!Hello World!!!

If you really, really, need the typename as a string, you could hack this (using snprintf instead of printf) and pull the substring after '=' and before ']'.

#include <iostream>
using namespace std;

template<typename type>
class test
{
public:
test()
{
    printf("asfdasdfasdf %s\n", __PRETTY_FUNCTION__);
    printf("asfdasdfasdf %s\n", __PRETTY_FUNCTION__);
    printf("asfdasdfasdf %s\n", __PRETTY_FUNCTION__);
    printf("asfdasdfasdf %s\n", __PRETTY_FUNCTION__);
    printf("asfdasdfasdf %s\n", __PRETTY_FUNCTION__);
    printf("asfdasdfasdf %s\n", __PRETTY_FUNCTION__);
}
};

template<typename type>
void tempFunction()
{
    printf("asfdasdfasdf %s\n", __PRETTY_FUNCTION__);
    printf("asfdasdfasdf %s\n", __PRETTY_FUNCTION__);
    printf("asfdasdfasdf %s\n", __PRETTY_FUNCTION__);
    printf("asfdasdfasdf %s\n", __PRETTY_FUNCTION__);
    printf("asfdasdfasdf %s\n", __PRETTY_FUNCTION__);
    printf("asfdasdfasdf %s\n", __PRETTY_FUNCTION__);
}

int main() {
    test<int> test;

    tempFunction<bool>();
    cout << "!!!Hello World!!!" << endl; // prints !!!Hello World!!!
    return 0;
}
Pneumatophore answered 21/3, 2012 at 22:27 Comment(5)
Doesn't compile on VS15.Syndic
@kuhaku It will only compile if you're using a compiler that supports PRETTY_FUNCTION. Review this answer to see details about PRETTY_FUNCTION. Also this link will show you what windows macros can be used to do the same thing. I suspect func will work, but have not tried or attempted to confirm. I exclusively use GCC, so I don't have an environment to confirm.Pneumatophore
Note: PRETTY_FUNCTION should be __PRETTY_FUNCTION__ and func should be __func__Pneumatophore
Not compile timeThermidor
This is my example to print a typename with PRETTY_FUNCTION: paste.ubuntu.com/p/QZ35GMR5P9Position
A
100

To get a useful compile time name:

Supposing you have some unknown type named 'T'. You can get the compiler to print its type by using it horribly. For example:

typedef typename T::something_made_up X;

The error message will be like:

error: no type named 'something_made_up' in 'Wt::Dbo::ptr<trader::model::Candle>'

The bit after 'in' shows the type. (Only tested with clang).

Other ways of triggering it:

bool x = T::nothing;   // error: no member named 'nothing' in 'Wt::Dbo::ptr<trader::model::Candle>'
using X = typename T::nothing;  // error: no type named 'nothing' in 'Wt::Dbo::ptr<trader::model::Candle>'

With C++11, you may already have an object and use decltype to get its type, so you can also run:

auto obj = creatSomeObject();
bool x = decltype(obj)::nothing; // (Where nothing is not a real member). 
Ashti answered 16/5, 2015 at 14:20 Comment(4)
this really helps to print typenames at compile time when you have code which is not compiling!Vincents
The only response to actually answer the question!Merrow
Works fine with gcc too.Otherwhere
Breaks when decltype(obj) is a pointer or a primitive typeRathskeller
P
24

__PRETTY_FUNCTION__ should solve your problem (at run time at least)

The output to the program below is:

asfdasdfasdf test<type>::test() [with type = int]
asfdasdfasdf test<type>::test() [with type = int]
asfdasdfasdf test<type>::test() [with type = int]
asfdasdfasdf test<type>::test() [with type = int]
asfdasdfasdf test<type>::test() [with type = int]
asfdasdfasdf test<type>::test() [with type = int]
asfdasdfasdf void tempFunction() [with type = bool]
asfdasdfasdf void tempFunction() [with type = bool]
asfdasdfasdf void tempFunction() [with type = bool]
asfdasdfasdf void tempFunction() [with type = bool]
asfdasdfasdf void tempFunction() [with type = bool]
asfdasdfasdf void tempFunction() [with type = bool]
!!!Hello World!!!

If you really, really, need the typename as a string, you could hack this (using snprintf instead of printf) and pull the substring after '=' and before ']'.

#include <iostream>
using namespace std;

template<typename type>
class test
{
public:
test()
{
    printf("asfdasdfasdf %s\n", __PRETTY_FUNCTION__);
    printf("asfdasdfasdf %s\n", __PRETTY_FUNCTION__);
    printf("asfdasdfasdf %s\n", __PRETTY_FUNCTION__);
    printf("asfdasdfasdf %s\n", __PRETTY_FUNCTION__);
    printf("asfdasdfasdf %s\n", __PRETTY_FUNCTION__);
    printf("asfdasdfasdf %s\n", __PRETTY_FUNCTION__);
}
};

template<typename type>
void tempFunction()
{
    printf("asfdasdfasdf %s\n", __PRETTY_FUNCTION__);
    printf("asfdasdfasdf %s\n", __PRETTY_FUNCTION__);
    printf("asfdasdfasdf %s\n", __PRETTY_FUNCTION__);
    printf("asfdasdfasdf %s\n", __PRETTY_FUNCTION__);
    printf("asfdasdfasdf %s\n", __PRETTY_FUNCTION__);
    printf("asfdasdfasdf %s\n", __PRETTY_FUNCTION__);
}

int main() {
    test<int> test;

    tempFunction<bool>();
    cout << "!!!Hello World!!!" << endl; // prints !!!Hello World!!!
    return 0;
}
Pneumatophore answered 21/3, 2012 at 22:27 Comment(5)
Doesn't compile on VS15.Syndic
@kuhaku It will only compile if you're using a compiler that supports PRETTY_FUNCTION. Review this answer to see details about PRETTY_FUNCTION. Also this link will show you what windows macros can be used to do the same thing. I suspect func will work, but have not tried or attempted to confirm. I exclusively use GCC, so I don't have an environment to confirm.Pneumatophore
Note: PRETTY_FUNCTION should be __PRETTY_FUNCTION__ and func should be __func__Pneumatophore
Not compile timeThermidor
This is my example to print a typename with PRETTY_FUNCTION: paste.ubuntu.com/p/QZ35GMR5P9Position
A
13

Another compile time solution, similar to the one provided by matiu, but perhaps a little more descriptive would be to use a static_assert wrapped in a little helper function:

#include <utility>

template <typename T>
struct always_false : std::false_type { };

template <typename T>
constexpr bool always_false_v = always_false<T>::value;

template <typename T>
void print_type_in_compilation_error(T&&)
{
    static_assert(always_false_v<T>,
                  "Compilation failed because you wanted to know the type; see below:");
}
// usage:
int I;
print_type_in_compilation_error(I);

The above will give you a nice error message (tested in MSVC and Clang), as in the other answer, but the code is easier to understand in my opinion.

N.B. the code in the original answer was ill formed, so I changed the code a bit. Credit for the always_false goes to this answer.

Altarpiece answered 21/9, 2017 at 8:51 Comment(2)
This was quite useful to me today in a situation where I was unable to get other approaches listed here to work. I also adapted this approach to use a template struct rather than a template function.Temptation
@Jarod42 You're right, thanks for pointing this out. I've adjusted the answer, hopefully it is correct now.Altarpiece
S
11

Since you have said you would need this for debugging purpose, maybe a runtime solution is also acceptable. And you have tagged this as g++ so you don't want to be standard conform.

Here is what that means:

#include <cxxabi.h> // the libstdc++ used by g++ does contain this header

template <typename type>
void print(const type *addr) // you wanted a pointer
{
  char * name = abi::__cxa_demangle(typeid(*addr).name(), 0, 0, NULL);
  printf("type is: %s\n", name);
  free(name);
}
     
print(new unsigned long);    // prints "type is: unsigned long"
print(new std::vector<int>); // prints "type is: std::vector<int, std::allocator<int> >"

EDIT: corrected the memory leak. Thx to Jesse.

Scammony answered 21/3, 2012 at 22:2 Comment(5)
This doesn't work at compile time it just prints out the demangled nameIndictable
Just keep in mind that you are leaking memory as malloc is called with __cxa_demangle.Wreak
I changed my accepted answer because for the problem I'm debugging doing the function call ended up changing the program behavior and so I was not reproducing the bug that I'm trying to fix. I thought the runtime overhead would not matter but I think the function call changed something with the stack and that did matter. The __PRETTY_FUNCTION__ method seems to work better for the problem I'm debugging, but both solutions look useful so thanks again.Ovaritis
@sbi, your edit directly conflicts with the documentation for this function. The pointer returned by abi::__cxa_demangle must be deallocated using free, and you cannot interchange delete and free. abi::__cxa_demangle is designed to be usable from C, so it uses C memory management functions.Rupert
@cgmb: You're right, of course. I dunno what I was thinking at that moment. I apologize.Southerner
F
8

There is Boost.TypeIndex library.

See boost::typeindex::type_id for details.

It is very-easy-to-use, cross-platform and is real compile-type solution. Also it works as well even if no RTTI available. Also most of compilers are supported from the box.

Farrel answered 26/2, 2015 at 9:17 Comment(3)
Note that this is only available for Boost >= 1.56.0.Ern
You are right but still is very-easy-to-use and it is real compile-type solution. And it works as well even if no RTTI info available. Also most of compilers are supported from the box.Farrel
Absolutely, hence my +1. But the next information I needed was the minimum Boost version required, since distributions shipping older versions of Boost may not have the library available (e.g. Ubuntu 12.04), which means that you'd need to fall back to other solutions if Boost < 1.56.0 is detected. Still, the extra information you added deserves to be in your answer.Ern
M
1

You can use C++20's concepts to define a constraint no type can satisfy. Using this constraint on a template argument and instantiating the template will cause the compiler to fail and print out the deduced typename.

void print_typename (auto) requires false {}
// Equivalent to
//   template<typename T>
//   concept unsatisfiable_c = false;
//   
//   template<unsatisfiable_c T>
//   void print_typename (T) {}

int main (int, char**) {
    int foo = 73;
    print_typename(foo);
    // error: no matching function for call to 'print_typename'
    // note: candidate template ignored: constraints not satisfied [with auto:1 = int]
    //                                                                            ^^^

    return 0;
}

If your T is more complex, e.g. T=std::vector<std::pair<key_t, value_t>> or T=void (*)(U, V const&), and you want to specifically find out key_t or V, you can do that as follows:

template<typename T>
concept unsatisfiable_c = false;

template<unsatisfiable_c key_t, typename value_t>
void print_key_t (std::vector<std::pair<key_t, value_t>>) {}

template<typename U, unsatisfiable_c V>
void print_V (void (*)(U, V const&)) {}

void foo (double, long double const&) {}

int main (int, char**) {
    std::vector<std::pair<long, char>> bar;

    print_key_t(bar);
    // error: no matching function for call to 'print_key_t'
    // note: candidate template ignored: constraints not satisfied [with key_t = long, value_t = char]

    print_V(&foo);
    // error: no matching function for call to 'print_V'
    // note: candidate template ignored: constraints not satisfied [with U = double, V = long double]

    return 0;
}
Morin answered 18/12, 2021 at 3:25 Comment(0)
K
0

if you don't care the statement will break the compilation, the simplest way is :

static_assert( 1== T::not_existed_member_name, "print the T type when compile" )

no way to print the type without breaking compilation. ( certainly you can trigger a warning, instead of a error here )

Kings answered 9/11, 2023 at 9:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.