Sign in to follow this  
Hodgman

[C++] Get type name without RTTI

Recommended Posts

For a while, I've been looking for a way to get the name ([i]as a c-string[/i]) of a template parameter, without using RTTI. Speaking with [url="http://www.gamedev.net/user/35663-digitalfragment/"]digital[/url], he mentioned the idea of using the __FUNCTION__ macro and some string parsing, so I had a go and it seems to work quite easily.

This is pretty hacky, so I'm hoping some of you fellow C++ users can tear apart it's weaknesses and expose the down-sides for me.

My first concern is that this relies on MSVC's implementation of the __FUNCTION__ macro, which includes the class name. On GCC I'd have to use __PRETTY_FUNCTION__ instead. I'm assuming any decent compiler will have some kind of macro that I can use for this purpose...

[code]template<class T>
struct TypeName
{
static void Get(const char*& begin, const char*& end)
{
begin = __FUNCTION__;
for(++begin; *begin && *(begin-1) != '<'; ++ begin);
for(end = begin; *end; ++ end);
for(; end > begin && *end != '>'; -- end);
}
};[/code]

Example usage:
[code] const char* begin=0, *end=0;
TypeName<Foo>::Get(begin, end);
assert(end>begin);
assert(end-begin < 128);
char buf[128];
memcpy(buf, begin, end-begin);
buf[end-begin] = 0;
printf(buf);//prints Foo[/code]

Share this post


Link to post
Share on other sites
[quote name='JarkkoL' timestamp='1312965810' post='4847075']You can use typeid(T).name() without RTTI.[/quote]Hmm, I'd assumed that disabling RTTI (with /GR-) would kill [font="Lucida Console"]typeid[/font], but it seems in MSVC2008, it still works fine on compile-time expressions like this!
This was my previous method, but I'd assumed I was relying on RTTI.

I wonder if other compilers (e.g. GCC with fno-rtti) will act the same way though? Also, GCC's implementation of type_info::name requires unmangling, but I guess I'm bound to end up with lots of "[font="Lucida Console"]#ifdef compilerABC[/font]" when doing this kind of stuff... [img]http://public.gamedev.net/public/style_emoticons/default/wink.gif[/img]

[edit]GCC gives: "[i]error: cannot use typeid with -fno-rtti[/i]"

Share this post


Link to post
Share on other sites
Right, the behavior of typeid() is compiler specific when you disable RTTI (disabling RTTI isn't part of C++ standard). Regarding your original code, it looks good to me. Thanks for sharing the trick (:


Cheers, Jarkko

Share this post


Link to post
Share on other sites
What do I get for:
[code]typedef std::basic_string<int> MyCustomString;
typedef MyCustomString SomethingStrange;

TypeName<SomethingStrange>::get()[/code]
How about:[code]
TypeName<std::pair<std::vector<int>, std::string>>::get();[/code]

Or:[code]TypeName<printf>::get();[/code]


There is a reason why C++ error messages are the mess they are.

A minor nitpick as well - RTTI means Run-time type information, FUNCTION and templates are both compile-time.

The best that can be had reliably is resolving the type down to basic types and printing that. Which is also what the compiler reports for errors. Going from that to what was actually used in code can be difficult to impossible.

Another gotcha is that long symbol names may cause problems with compiler and buffer sizes, causing the name to be incomplete or perhaps even crash.

Share this post


Link to post
Share on other sites
[quote name='Antheus' timestamp='1312978370' post='4847140']What do I get for:
[font="Courier New"]typedef std::basic_string<int> MyCustomString;
typedef MyCustomString SomethingStrange;
TypeName<SomethingStrange>::get()[/font][/quote]Both of the above methods ([i]using [font="Courier New"]__FUNCTION__[/font] or [font="Courier New"]typeid[/font][/i]) return "[font="Lucida Console"]class std::basic_string<int,struct std::char_traits<int>,class std::allocator<int> >[/font]"[quote]How about: [font="Courier New"]TypeName<std::pair<std::vector<int>, std::string>>::get();[/font][/quote]Both return "[font="Lucida Console"]struct std::pair<class std::vector<int,class std::allocator<int> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >[/font]"[quote]Or: [font="Courier New"]TypeName<printf>::get();[/font][/quote]As expected: [font="Lucida Console"]'printf' is not a valid template type argument for parameter 'T'[/font].
I don't want to know the types of types that aren't types.

I'm not sure what you're getting at, but these are the results I'm looking for. I want to know the actual type of a template argument, regardless of typedefs, etc.


[quote]A minor nitpick as well - RTTI means Run-time type information, FUNCTION and templates are both compile-time.[/quote]The point of these shenanigans is to be able to disable the standard RTTI and instead use a home-made lightweight implementation.
If I can determine the type of a parameter at compile time, then I can make a simple RTTI system from that - hence the title, [i]Get type name without RTTI[/i].
Basically, if you create a perfect hash from these strings, then you get a bare bones RTTI system that lets you do fast type comparisons ([i]but nothing else[/i]).

Share this post


Link to post
Share on other sites
Light weight in what sense? If you're interested in reducing the number of compiler generated structures added to your binary, on MSVC you may also have to disable exception handling because MSVC uses many of the same internal structures to support both.

Share this post


Link to post
Share on other sites
There is a compiler option in Visual Studio (sorry name of it escapes me at the moment) that can remove duplicate functions, ie functions which have similar or same functionality even with different types. I would be wary (or at least check) that the template function name is not changed by this and gives incorrect information about the typename T.

Share this post


Link to post
Share on other sites
I just discovered that instead of __FUNCTION__ giving, e.g.:
[font="Courier New"]TypeName<std::pair<std::vector<int>, std::string>>::get[/font]
Some compilers give you:
[font="Courier New"]TypeName<T>::get [with T = std::pair<std::vector<int>, std::string>][/font]
which is still parsable, but it seems the parsing routing is going to be quite different depending on the compiler.

[quote name='SiCrane' timestamp='1312985261' post='4847161']Light weight in what sense? If you're interested in reducing the number of compiler generated structures added to your binary, on MSVC you may also have to disable exception handling because MSVC uses many of the same internal structures to support both.[/quote]Lightweight in the sense that I can check that a type is what I expect it to be with a predictable/low amount of storage and a predictable/low amount of work. I also only need to check for exact type matches, not inheritance/interface matches.
The standard RTTI implementation is unpredictable, depending on the compiler's implementation, and MSVC's implementation of type-comparison is extremely slow.

Also, type_info objects are non-copyable, which is usually worked around by wrapping them in a PIMPL class. However, predictable memory access patterns are important for this library ([i]e.g. for NUMA support[/i]), which makes the default RTTI implementation quite expensive/unusable from a memory-access perspective.
e.g. when I invoke [font="Courier New"]operator==[/font] on two [font="Courier New"]type_info[/font]s, which memory locations are read from? If I can't answer/control that, then I can't use them.

I'm using the assumption that many users of a game-oriented library will likely have RTTI and exceptions disabled, so I need to support that.
Most of the game projects I've been involved with professionally have had both exceptions and RTTI disabled, so I would like to design this system in a way where people can use it without being forced to change their compiler settings.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this