48

Is it possible in C++ to stringify template arguments? I tried this:

#include <iostream>
#define STRINGIFY(x) #x
 
template <typename T>
struct Stringify
{
     Stringify()
     {
          std::cout << STRINGIFY(T) << endl;
     }
};
 
int main() 
{
     Stringify<int> s;
}

But what I get is a T, and not an int. Seems that the preprocessor macros are evaluated before template instantiation.

Is there any other way to do this?

Is there any way for the preprocessing to take place after template instantiation? (Compiler is VC++).

OverShifted
  • 457
  • 1
  • 7
  • 17
sold
  • 2,041
  • 5
  • 25
  • 32
  • 3
    Template resolution kicks in *long* after the preprocessor does its job. Anyway, templates are much more than text substitution (well, it's not even text substitution), so changing the order of operations it would not solve your problem. – Raphaël Saint-Pierre Sep 28 '09 at 17:18
  • 5
    The preprocessor kicks in before pretty much *everything*. Hence the name **pre**-processor. – Laurence Gonsalves Sep 28 '09 at 17:26
  • 3
    I've seen people do `template char const* get_type_name() { return __PRETTY_FUNCTION__; }` and then extract the `T = ...` out of the string. – Johannes Schaub - litb Sep 28 '09 at 17:40
  • Macro expansion happens long before the compiler gets involved. – Martin York Sep 28 '09 at 18:30
  • @litb: a nice hack, but why don't you post it as answer, so I can upvote that? :) – Pavel Minaev Sep 28 '09 at 19:29
  • I'm afraid i wasn't sure whether it works with VC++ xD – Johannes Schaub - litb Sep 28 '09 at 19:39
  • I would presume that the `T` extracted from `__PRETTY_FUNCTION__` will look very much like `typeid(T).name()`. Why would compiler writers implement different algorithms for representing types? – sbi Sep 29 '09 at 12:01
  • 1
    Because `__PRETTY_FUNCTION__` is intended to be human readable (in debug messages - it's got the same format as in diagnostics afaics) while `typeid(T).name()` isn't. – Johannes Schaub - litb Sep 30 '09 at 00:53
  • 2
    @litb: Well, `typeid(T).name()` isn't required to return anything meaningful, but I get your point. Working mostly with VC and using `typeid(T).name()` mostly for small test programs, I keep forgetting that it doesn't have to return a nicely formatted type. – sbi Sep 30 '09 at 16:19
  • @LaurenceGonsalves: pretty much but not really everything. before preprocess there is backslash style end of lines and triggraphs. – v.oddou Apr 18 '14 at 01:39
  • @v.oddou trigraph replacement and line-splicing are the first two phases of the preprocessor. – Laurence Gonsalves Apr 18 '14 at 18:44
  • @litb Does that mean that under the auspices of VC, `typeid(T).name()` returns a demangled type name – versus under the clang or gcc aegis, in which case you need to use e.g. `abi::__cxa_demangle()` to get something “nicely formatted” ?… I have never used VC, and that piques my curiosity. – fish2000 Aug 05 '16 at 16:05

8 Answers8

39

You could try

 typeid(T).name()

Edit: Fixed based on comments.

eduffy
  • 39,140
  • 13
  • 95
  • 92
  • 7
    Just keep in mind compilers don't necessarily have to give a meaning return value for `name()`, but most do. – GManNickG Sep 28 '09 at 17:27
  • 5
    This should be `typeid()`, not `typeinfo()` - the latter is the name of the header ``, and also `std::type_info` is the class type of object returned by `typeid()`. – Pavel Minaev Sep 28 '09 at 19:27
  • 2
    Holy smokes the output from `typeid(T).name()` is _ugly!_ I just tried it. It's name-mangled! I'm using the clang compiler. That won't work for my needs. I need it to be a most beautiful C-string, not a name-mangled hacked up piece of name. – Gabriel Staples Apr 28 '20 at 06:14
  • Note that the types I'm passing as `T` are structs. – Gabriel Staples Apr 28 '20 at 06:20
  • My solution to this ugly name-mangled output problem is the same as this person's, so this is what I'm doing instead: https://stackoverflow.com/questions/1488186/stringifying-template-arguments/49005288#49005288 – Gabriel Staples Apr 29 '20 at 03:26
27

You could use some template magic.

#include <iostream>

template <typename T>
struct TypeName { static const char *name; };

template <typename T>
const char *TypeName<T>::name = "unknown";

template <>
const char *TypeName<int>::name = "int";

template <typename T>
struct Stringify
{
     Stringify()
     {
          std::cout << TypeName<T>::name << std::endl;
     }
};

int main() 
{
     Stringify<int> s;
}

This has an advantage over RTTI (i.e. typeinfo) - it is resolved during compilation; and disadvantage - you need to provide type information yourself (unless there is some library that does that already that I'm not aware of; maybe something in Boost even).

Or, as Martin York suggested in comments, use inline function templates instead:

template <typename T>
inline const char* typeName(void) { return "unknown"; }

template <>
inline const char* typeName<int>(void) { return "int"; }

// ...
std::cout << typeName<T>() << std::endl;

But, if you'll ever need to store more information about that particular type, then class templates will probably be better.

Andreas Magnusson
  • 7,321
  • 3
  • 31
  • 36
Cat Plus Plus
  • 125,936
  • 27
  • 200
  • 224
  • 3
    Rather than building variables. Build inline functions that return the appropriate string. Then you will not have the problem of multi-definintions that may crop up with this method. – Martin York Sep 28 '09 at 18:32
  • 1
    Interestingly, the template specializations for each type of interest can be most easily and DRY-ly implemented with a macro: `#define TYPE_STRING(T) template<> const char* TypeName::name = STRINGIFY(T)` – Phil Miller Apr 07 '12 at 05:43
  • @Novelocrat: yes I have already made this exact system in my company. But frankly, I am longing for compiler support of typename stringification. This will help serilization frameworks so much. – v.oddou Apr 18 '14 at 01:45
18

Your code doesn't work because the preprocessor, responsible for searching and expanding the macros you use in your code, is not aware of the language itself. It is just a text parser. It finds that STRINGIFY(T) in the very function template and expand it, much before you give a type to that template. As it turns out, you will always get "T" instead of the typename you expected, unfortunately.

As litb suggested, I've (badly) implemented this `getTypeName' function template that returns the typename you pass it:

#include <iostream>

template <typename _Get_TypeName>
const std::string &getTypeName()
{
    static std::string name;

    if (name.empty())
    {
        const char *beginStr = "_Get_TypeName =";
        const size_t beginStrLen = 15; // Yes, I know...
                                       // But isn't it better than strlen()?

        size_t begin,length;
        name = __PRETTY_FUNCTION__;

        begin = name.find(beginStr) + beginStrLen + 1;
        length = name.find("]",begin) - begin;
        name = name.substr(begin,length);
    }

    return name;
}

int main()
{
    typedef void (*T)(int,int);

    // Using getTypeName()
    std::cout << getTypeName<float>() << '\n';
    std::cout << getTypeName<T>() << '\n'; // You don't actually need the
                                           // typedef in this case, but
                                           // for it to work with the
                                           // typeid below, you'll need it

    // Using typeid().name()
    std::cout << typeid(float).name() << '\n';
    std::cout << typeid(T).name() << '\n';

    return 0;
}

The code above results in the following output with GCC flag -s ("strip all symbols from binary") enabled:

float
void (*)(int, int)
f
PFviiE

So, you see, getTypename() does a fairly better job, at the cost of that fugly string parsing hack (I KNOW, it's damn ugly).

A few points to take into account:

  • The code is GCC only. I don't know how to port it to another compiler. Probably only a few others have such a facility to produce so pretty function names, and from what I searched, MSVC++ doesn't have one, if you're asking yourself that.
  • If, in a new version, GCC formats __PRETTY_FUNCTION__'s differently, the string matching can break and you'll have to fix it. For this same reason I also warn that getTypeName() might be good for debugging (and, still, maybe not even good for that), but it is surely bad, bad, and bad for other purposes such as comparing two types in a template or something like that (I don't know, just guessing what someone might think of..). Use it solely for debugging, and preferentially don't call it in release builds (use macros to disable), so that you don't use __PRETTY_FUNCTION__ and thus the compiler doesn't produce the string for it.
  • I'm definitely no expert, and I'm not sure whether some odd type could cause the string matching to fail. I'd like to ask for people who read this post to comment if they know of such a case.
  • The code uses a static std::string. It means that, if some exception is thrown from its constructor or destructor, there is no way that it will reach a catch block and you'll get an unhandled exception. I don't know whether std::strings can do that, but beware that, if they do, you're potentially in trouble. I used it because it needs a destructor to free the memory. You could implement your own class for that, though, ensuring no exception is thrown besides allocation failure (that's pretty much fatal, isn't it? So...), and return a simple C-string.
  • With typedefs you can get some weird results, like this (for some reason, the site breaks the formatting of this snippet, so I'm using this paste link): http://pastebin.com/f51b888ad

Despite those disadvantages, I'd like to say that it sure is fast. For the second time you lookup for one same type name, it will cost picking a reference to a global std::string containing the name. And, comparatively to the template specialiazation methods suggested before, there is nothing else you have to declare besides the very template itself, so it is really much easier to use.

Community
  • 1
  • 1
Gui Prá
  • 5,559
  • 4
  • 34
  • 59
  • 2
    w.r.t. your comment about `strlen`, why not use `const char beginStr[] = "_Get_TypeName =";` which would allow you to use `sizeof` unless it decays to a pointer. – Tim Seguine Jan 20 '15 at 09:48
  • 1
    This the best solution so far, but you don't get clean round-trip echos of the source code symbol when you use standard templated classes, like string. `getTypeName()` prints out `std::basic_string, std::allocator>`. – Mark Lakata Apr 30 '15 at 19:40
  • @MarkLakata Nonetheless still usable for information as the common cpp coder will know this original form of std::string. I think it is good to know this tricky bit, and even better that you pointed it out.! – MABVT Jul 14 '17 at 18:25
12

No, you cannot work on types as if they were variables. You could write code that extracted the typeid() of an element and printed the name, but the resulting value will probably not be what you expect (type names are not standarized).

You can also work with template specializations (and some macro magic) to achieve a more interesting version if the number of types you want to work with is limited:

template <typename T> const char* printtype(); // not implemented

// implement specializations for given types
#define DEFINE_PRINT_TYPE( type ) \
template<>\
const char* printtype<type>() {\
   return #type;\
}
DEFINE_PRINT_TYPE( int );
DEFINE_PRINT_TYPE( double );
// ... and so on
#undef DEFINE_PRINT_TYPE
template <typename T> void test()
{
   std::cout << printtype<T>() << std::endl;
}
int main() {
   test<int>();
   test<double>();
   test<float>(); // compilation error, printtype undefined for float
}

Or you could even combine both versions: implement the printtype generic template using typeinfo and then provide specializations for the types you want to have fancier names.

template <typename T>
const char* printtype()
{
   return typeid(T).name();
}
David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • You want to return "T" from type_constructor i think, or write "typeid(T).name()". If it returns a reference, typeid will evaluate the function call and look into the vtable/etc of the "object". – Johannes Schaub - litb Sep 28 '09 at 19:14
  • I thought of that, but it worked with the naïve tests I made up (none of them where references into a base of a hierarchy with rtti), thanks. – David Rodríguez - dribeas Sep 28 '09 at 19:29
  • Good point about the polymorphism. After looking up, i found the standard says that for lvalues with non-polymorphic types, the operand is unevaluated too. – Johannes Schaub - litb Sep 28 '09 at 19:37
  • I have reworked the solution to provide the const char* value instead of directly printing it. Also changed typeid to use the type instead of a fake instance of it. – David Rodríguez - dribeas Sep 29 '09 at 09:09
4

This breaks one of my primary tenets of C++ code writing: Avoid using tricks in both the template features and the preprocessor at the same time.

Part of the reason for templates and the nastiness they introduce into the language was an attempt to wean developers away from using the preprocessor. If you use both, then the terrorists win.

T.E.D.
  • 44,016
  • 10
  • 73
  • 134
  • 2
    I disagree. Macros can indeed be very bad, but they can also be very powerful. dribeas shows this very nicely (http://stackoverflow.com/questions/1488186/1488216#1488216), combining the pre-processor wit templates. Compare that with PiotrLegnica's idea (http://stackoverflow.com/questions/1488186/1488250#1488250), which is, basically, the same, but without the macros. I'd take the macro solution over typing any day. – sbi Sep 28 '09 at 18:24
  • No need to be terrified of it on this basis alone. Macros and templates can create powerful constructs. – Steven Lu Jan 09 '14 at 06:15
  • 2
    "If you use both, then the terrorists win." –> You are playing too much Counter-Strike. Macros and templates combined together and used properly can really, heally help you overcome what types do. Don't forget that fake functions accepting types as arguments can only be achieved this way. :) – Петър Петров Jul 07 '16 at 14:30
  • @ПетърПетров - Completely disagree. To start with, I'm not playing nearly **enough** Counter-Strike... – T.E.D. Jul 07 '16 at 15:08
4

If you use boost/core/demangle.hpp, you can get a reliable human-readable string.

char const * name = typeid(T).name();
boost::core::scoped_demangled_name demangled( name );

std::cout << (demangled.get() ? demangled.get() : "Failed to demangle") << std::endl;
Hayden
  • 111
  • 1
  • 2
3

in my code I use the "awful" double-declaration of the "Class-Name"

MqFactoryC<MyServer>::Add("MyServer").Default();

because c++ is NOT able to extract the string "MyServer" from the template… the only "way" to get "rid" of this… using a cpp "wrapper"

#define MQ_CPPSTR(s) #s
#define MqFactoryCAdd(T) MqFactoryC<T>::Add(MQ_CPPSTR(T)).Default()
Andreas Otto
  • 311
  • 1
  • 10
  • Upvoted because this is what I resorted to too. The [accepted answer](https://stackoverflow.com/a/1488204/4561887) does work, but it does name-mangling and generates ugly names for my structs. See my comments under that answer. Therefore, the proper solution would be to do what you did, then wrap that in a macro so that you pass the `MyServer` type in only **once** to the macro, and the macro passes it in **twice** to the template, once as a type and once as a C-string, as you have done. – Gabriel Staples Apr 28 '20 at 07:03
1

Here’s what I do: I have a demangle() function (implemented on top of abi::__cxa_demangle() which I call with a couple of convenience template function overloads, nameof(), with either the type I want stringified or an instance of same.

It’s fairly compact, so I’ll reproduce it here in all its glory. In demangle.hh we have:

#pragma once
#include <typeinfo>

namespace terminator {

    /// actual function to demangle an allegedly mangled thing
    char const* demangle(char const* const symbol) noexcept;

    /// convenience function template to stringify a name of a type,
    /// either per an explicit specialization:
    ///     char const* mytypename = terminator::nameof<SomeType>();
    template <typename NameType>
    char const* nameof() {
        try {
            return demangle(typeid(NameType).name());
        } catch (std::bad_typeid const&) {
            return "<unknown>";
        }
    }

    ///  … or as implied by an instance argument:
    ///     char const* myinstancetypename = terminator::nameof(someinstance);
    template <typename ArgType>
    char const* nameof(ArgType argument) {
        try {
            return demangle(typeid(argument).name());
        } catch (std::bad_typeid const&) {
            return "<unknown>";
        }
    }

} /* namespace terminator */

… And then in demangle.cpp:

#include "demangle.hh"

#include <cstdlib>
#include <cxxabi.h>
#include <mutex>
#include <memory>

namespace terminator {

    namespace {

        /// define one singular, private, static std::mutex,
        /// to keep the demangler from reentering itself
        static std::mutex mangle_barrier;

        /// define a corresponding private and static std::unique_ptr,
        /// using a delete-expression to reclaim the memory malloc()'ed by
        /// abi::__cxa_demangle() upon its return.
        /// … we use clang pragmas to add flags locally for this to work:
        #pragma clang diagnostic push
        #pragma clang diagnostic ignored "-Wglobal-constructors"
        #pragma clang diagnostic ignored "-Wexit-time-destructors"
        std::unique_ptr<char, decltype(std::free)&> demangled_name{ nullptr, std::free };
        #pragma clang diagnostic pop

    }

    char const* demangle(char const* const symbol) noexcept {
        if (!symbol) { return "<null>"; }
        std::lock_guard<std::mutex> lock(mangle_barrier);
        int status = -4;
        demangled_name.reset(
            abi::__cxa_demangle(symbol,
                                demangled_name.get(),
                                nullptr, &status));
        return ((status == 0) ? demangled_name.release() : symbol);
    }

} /* namespace terminator */

To use this, I think you’ll have to link to libc++ (or whatever your local equivalent is) to use abi::__cxa_demangle(). What may be suboptimal for the OP is the fact that this does the demangling and stringification at runtime. I’d personally love something constexpr-friendly in leu of this, but since I suffer from a severe macro-abuse allergy, I find this to be the least generally-unreasonable solution to this problem.

(the terminator namespace is inconsequential – I use this code in a libunwind-based stacktracer called from termination handler – feel free to s///g that token)

fish2000
  • 4,289
  • 2
  • 37
  • 76