C++ Get name of type in template

I'm writing some template classes for parseing some text data files, and as such it is likly the great majority of parse errors will be due to errors in the data file, which are for the most part not written by programmers, and so need a nice message about why the app failed to load e.g. something like:

Error parsing example.txt. Value ("notaninteger")of [MySectiom]Key is not a valid int

I can work out the file, section and key names from the arguments passed to the template function and member vars in the class, however I'm not sure how to get the name of the type the template function is trying to convert to.

My current code looks like, with specialisations for just plain strings and such:

template<typename T> T GetValue(const std::wstring &section, const std::wstring &key)
{
    std::map<std::wstring, std::wstring>::iterator it = map[section].find(key);
    if(it == map[section].end())
        throw ItemDoesNotExist(file, section, key)
    else
    {
        try{return boost::lexical_cast<T>(it->second);}
        //needs to get the name from T somehow
        catch(...)throw ParseError(file, section, key, it->second, TypeName(T));
    }
}

Id rather not have to make specific overloads for every type that the data files might use, since there are loads of them...

Also I need a solution that does not incur any runtime overhead unless an exception occurs, i.e. a completely compile time solution is what I want since this code is called tons of times and load times are already getting somewhat long.

EDIT: Ok this is the solution I came up with:

I have a types.h containg the following

#pragma once
template<typename T> const wchar_t *GetTypeName();

#define DEFINE_TYPE_NAME(type, name) \
    template<>const wchar_t *GetTypeName<type>(){return name;}

Then I can use the DEFINE_TYPE_NAME macro to in cpp files for each type I need to deal with (eg in the cpp file that defined the type to start with).

The linker is then able to find the appropirate template specialisation as long as it was defined somewhere, or throw a linker error otherwise so that I can add the type.

Jesse Beder's solution is likely the best, but if you don't like the names typeid gives you (I think gcc gives you mangled names for instance), you can do something like:

template<typename T>
struct TypeParseTraits;

#define REGISTER_PARSE_TYPE(X) template <> struct TypeParseTraits<X> \
    { static const char* name; } ; const char* TypeParseTraits<X>::name = #X


REGISTER_PARSE_TYPE(int);
REGISTER_PARSE_TYPE(double);
REGISTER_PARSE_TYPE(FooClass);
// etc...

And then use it like

throw ParseError(TypeParseTraits<T>::name);

EDIT:

You could also combine the two, change name to be a function that by default calls typeid(T).name() and then only specialize for those cases where that's not acceptable.

C, "C" is one of the most widely used programming languages of all time. Prof Brian Kernighan Duration: 8:26 Posted: 18 Aug 2015 This is a list of operators in the C and C++ programming languages.All the operators listed exist in C++; the fourth column "Included in C", states whether an operator is also present in C. Note that C does not support operator overloading.

The solution is

typeid(T).name()

which returns std::type_info.

Why C is so Influential - Computerphile, This course will give you a full introduction into all of the core concepts in the C programming Duration: 3:46:13 Posted: 15 Aug 2018 C programming is a general-purpose, procedural, imperative computer programming language developed in 1972 by Dennis M. Ritchie at the Bell Telephone Laboratories to develop the UNIX operating system. C is the most widely used computer language. It keeps fluctuating at number one scale of popularity

typeid(T).name() is implementation defined and doesn't guarantee human readable string.

Reading cppreference.com :

Returns an implementation defined null-terminated character string containing the name of the type. No guarantees are given, in particular, the returned string can be identical for several types and change between invocations of the same program.

...

With compilers such as gcc and clang, the returned string can be piped through c++filt -t to be converted to human-readable form.

But in some cases gcc doesn't return right string. For example on my machine I have gcc whith -std=c++11 and inside template function typeid(T).name() returns "j" for "unsigned int". It's so called mangled name. To get real type name, use abi::__cxa_demangle() function (gcc only):

#include <string>
#include <cstdlib>
#include <cxxabi.h>

template<typename T>
std::string type_name()
{
    int status;
    std::string tname = typeid(T).name();
    char *demangled_name = abi::__cxa_demangle(tname.c_str(), NULL, NULL, &status);
    if(status == 0) {
        tname = demangled_name;
        std::free(demangled_name);
    }   
    return tname;
}

"C" Programming Language: Brian Kernighan, C programming is a general-purpose, procedural, imperative computer programming language developed in 1972 by Dennis M. Ritchie at the Bell Telephone� C provides a compound assignment operator for each binary arithmetic and bitwise operation (i.e. each operation which accepts two operands). Each of the compound bitwise assignment operators perform the appropriate binary operation and store the result in the left operand.

As mentioned by Bunkar typeid(T).name is implementation defined.

To avoid this issue you can use Boost.TypeIndex library.

For example:

boost::typeindex::type_id<T>().pretty_name() // human readable

C Programming Tutorial for Beginners, C is a powerful general-purpose programming language. Our C tutorials will guide you to learn C programming one step at a time with the help of examples. C or Do is the first note of the C major scale, the third note of the A minor scale (the relative minor of C major), and the fourth note (F, A, B, C) of the Guidonian hand, commonly pitched around 261.63 Hz.

The answer of Logan Capaldo is correct but can be marginally simplified because it is unnecessary to specialize the class every time. One can write:

// in header
template<typename T>
struct TypeParseTraits
{ static const char* name; };

// in c-file
#define REGISTER_PARSE_TYPE(X) \
    template <> const char* TypeParseTraits<X>::name = #X

REGISTER_PARSE_TYPE(int);
REGISTER_PARSE_TYPE(double);
REGISTER_PARSE_TYPE(FooClass);
// etc...

This also allows you to put the REGISTER_PARSE_TYPE instructions in a C++ file...

C Tutorial, C is a computer programming language. That means that you can use C to create lists of instructions for a computer to follow. C is one of thousands of� Learn C# programming - for beginning developers, developers new to C#, and experienced C# / .NET developers

Learn C Programming, learn-c.org is a free interactive C tutorial for people who want to learn C, fast. Programming Languages Development - C++ has been used extensively in developing new programming languages like C#, Java, JavaScript, Perl, UNIX’s C Shell, PHP and Python, and Verilog etc. Computation Programming - C++ is the best friends of scientists because of fast speed and computational efficiencies.

What is C?, C-SPAN.org gives you access to C-SPAN's daily coverage of Washington and more than 200,000 hours of extensively indexed and archived C-SPAN video.

C Programming: Getting Started, Microsoft C++, C, and Assembler documentation. Learn how to use C++, C, and assembly language to develop applications, services, and tools for your platforms and devices.

Comments
  • not really relevant to your question, but you might want to use map.find(section) when accessing the section aswell, unless you intentionally want to create an empty section.
  • Note: This code will not compile if you forget to define REGISTER_PARSE_TYPE for a type that you use. I have used a similar trick before (in code without RTTI) and it has worked very well.
  • I had to move name outside of the struct in g++ 4.3.0 due to "error: invalid in-class initialization of static data member of non-integral type 'const char *'"; and, of course, the keyword 'struct' is needed between <> and TypeParseTraits and the definition should be terminated with a semicolon.
  • Well the leaving the semicolon out was intentional, to force you to use it at the end of the macro invocation, but thanks for the corrections.
  • I obtain the folllowing error: error: '#' is not followed by a macro parameter
  • @kratsg - that's because at the end '#x' should be '#X' (uppercase to match macro parameter) - I'll fix the answer.
  • Keep in mind that it's compliant to return the same string for every type (though I don't think any compiler would do that).
  • Or to return a different string for the same type on different executions... (again not that I think that any sane compiler would do that).
  • I'd just like to point out how ugly the given name can be: typeid(simd::double3x4).name() = "N4simd9double3x4E". typeid(simd::float4).name() = "Dv4_f" C++17, Xcode 10.1.
  • Indeed. typeid(T).name() is the canonical way to do this, but very few compilers return unmangled names; the only one I'm personally familiar with that does so is MSVC. Depending on the compiler used, there's also a chance that it may lose some type information on function types, but that's probably irrelevant in this case.
  • Isn't it memory leak to have free in if?
  • No, because the pointer points to nullptr if the status is not 0.
  • I'd like to add that it's probably best to check for gcc or clang's existence and if not default to not doing the demangling as shown here.