23

Is there anyway to get compile-time typeid information from GCC with RTTI disabled? Under Visual Studio, a simple command like const char* typeName = typeid(int).name(); will appropriately return "int", even if RTTI is disabled. Unfortunately, GCC can't do the same. When I try to call typeid without RTTI, my program crashes. I know disabling RTTI is not part of the standard, but is there anyway I can force GCC to do compile time resolution of known types?

RTTI is disabled for performance reasons. I have no need for runtime RTTI.

Edit:

Here's what I ended up going with:

template<typename T> const char* TypeName(void);
template<typename T> const char* TypeName(T type) { return TypeName<T>(); }

#define REFLECTION_REGISTER_TYPE(type) \
    template <> const char* TypeName<type>(void) { return #type; } 

It requires that REFLECTION_REGISTER_TYPE be called for every type that needs reflection info. But as long as it's called for every required type, calling TypeName<int> works perfectly. I also added the function TypeName(T type) which means you can do things like this: int x = 0; printf(TypeName(x)); and it will print out "int". GCC should really be able to do this at compile time like VC++ can.

Kyle
  • 1,111
  • 1
  • 12
  • 27

6 Answers6

11

There is another solution with its pros and cons:

typedef void* TypeId;
template<class T>
TypeId TypeIdNoRTTI() //this function is instantiated for every different type
{
    //WARNING: works only inside one module: same type coming from different module will have different value!
    static T* TypeUniqueMarker = NULL; //thus this static variable will be created for each TypeIdNoRTTI<T> separately
    return &TypeUniqueMarker; //it's address is unique identifier of TypeIdNoRTTI<T> type
}
mmohab
  • 2,303
  • 4
  • 27
  • 43
araud
  • 171
  • 1
  • 6
  • 1
    Could you clarify what you meant by "module" in the comment? A translation unit? – Leedehai Jun 04 '21 at 17:14
  • 1
    This looks insanely elegant! Even despite the possibility of having different values for the same type typeided from different TUs. Thanks! – Arty Feb 22 '22 at 01:20
9

First of all, turn back on RTTI.

Failing that, if you really *really* need to get a string representation of a type without it, with a little string manipulation, and careful consideration of the fact that you're writing non-standard code that might break if you upgrade GCC, or change platforms, or use a different set of options, you might be able to fake it.

#include <iostream>
#include <string>

std::string extract_type_name(const char* s) {
  //add logic her
  return s;
}

template<typename T>
std::string type_name() {
  static std::string s = extract_type_name(__PRETTY_FUNCTION__);
  return s;
}

int main() {
  std::cout << type_name<int>() << " " << type_name<std::string>() << std::endl;
}

The output of that function on ideone is

std::string type_name() [with T = int]
std::string type_name() [with T = std::basic_string<char, std::char_traits<char>, std::allocator<char> >]

Assuming that __PRETTY_FUNCTION__ behaves the same with RTTI turned off, yanking out the T = blah bit shouldn't be overly difficult.

Also, keep in mind that typeid(blah).name() offers very few guarantees... I remember using it on one platform where the result for any user defined type was simply struct. Not overly useful. Relying on it is flimsy even with RTTI turned on [which you should do anyhow].

Dennis Zickefoose
  • 10,791
  • 3
  • 29
  • 38
7

No. RTTI is RunTime Type Information (and disabling it is silly, but hey), and that's the purpose of typeid. If you want to stringise type names at compile time, you have to do it yourself (via template or macros).

Cat Plus Plus
  • 125,936
  • 27
  • 200
  • 224
  • 4
    I'd like to do something like this: `struct S { int a, b; } const char* aType = typeid(S::a).name();` which is all compile time information. While I can simply specify that `a` is an `int`, I'd rather have the compiler do it. It will make my code much less verbose. VC compiler can do it, I wasn't sure if there was a way to get GCC to do it as well. – Kyle Nov 03 '11 at 20:22
  • 15
    I will never understand why C++ doesn't have this feature. Much more useful than RTTI. – Pubby Nov 03 '11 at 20:25
  • 5
    I'd be careful with calling it "silly". Especially if you have heavily templatized code and 256 kB of flash on MCU available, you don't want to waste half of it for strings with template names you'd never use. There's also world outside of x86_64! – Marcin Tarsier Dec 02 '20 at 08:24
2

It seems at least one other developer, who acknowledges the reality that RTTI is often not available, thinks that having "compile time type information" would be a good thing.

There are also more search results for 'ctti C++' so I expect there are options that cover most of what is useful and possible.

Note: I've never used that library so use at your own risk.

BCS
  • 75,627
  • 68
  • 187
  • 294
2

GCC supports compile time type operator with typeof.

Antti
  • 11,944
  • 2
  • 24
  • 29
  • 3
    What I actually need is the name of the type. If there was someway to turn `typeof` into a string, that'd be perfect. – Kyle Nov 03 '11 at 20:23
  • @KennyTM: Thanks for that link. That ended up being like the solution I came up with – Kyle Nov 03 '11 at 23:06
0

Based on Edit of Kyle a variation of his solution. This solution is useful if you have an atmel avr 8bit microcontroller like arduino uno, nano, mega (or similar). Such microcontrollers have very small cpu and ram footprint. Defining and comparing strings is not efficient. To make it efficient an enum is required.

Sadly this solution has a dependency (You need only the preprocessor part of boost). Types can be compared at run time.

Content of file get_type.h:

#pragma once
#include <boost/preprocessor.hpp>

template<typename rtt_enum, typename T> rtt_enum getType(void);
template<typename rtt_enum, typename T> rtt_enum getType(T type) { return getType< rtt_enum, T>(); }

#define ENUM_ENTRY(r, data, elem) elem,
#define REFLECTION_REGISTER_TYPE(r, rtt_enum, rtt_type)\
    template <> rtt_enum getType<rtt_enum, rtt_type>(void) { return rtt_enum::rtt_type; }

#define REFLECTION_REGISTER(rtt_enum, rtt_list)\
    enum class rtt_enum {\
        BOOST_PP_SEQ_FOR_EACH(ENUM_ENTRY, ~, BOOST_PP_VARIADIC_TO_SEQ rtt_list)\
    };\
    BOOST_PP_SEQ_FOR_EACH(REFLECTION_REGISTER_TYPE, rtt_enum, BOOST_PP_VARIADIC_TO_SEQ rtt_list)

Usage is very simple:

#include <Arduino.h>
#include "get_type.h"

class MyClass1 {};
class MyClass2 {};
REFLECTION_REGISTER(MyTypes1, (MyClass1, MyClass2))

class MyClass3 {};
class MyClass4 {};
REFLECTION_REGISTER(MyTypes2, (MyClass3, MyClass4))

void setup(){
    MyClass1 obj1;
    MyClass2 obj2;

    MyTypes1 t = getType<MyTypes1>(obj2);

    Serial.println((int)t); // will print "1", because MyClass2 is second elem in enum MyTypes1 

    switch(t){
    case MyTypes1::MyClass1:
        //do fancy stuff with MyClass1
    break;

    case MyTypes1::MyClass2:
        //do fancy stuff with MyClass2
    break;
    }
}

void loop(){}

As you see you can even register on multiple sets of types like MyTypes1 or MyTypes2.

ptiza_v_nebe
  • 31
  • 1
  • 5