简体   繁体   中英

Compile time typeid without RTTI with GCC

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)); 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.

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
}

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).

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].

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.

GCC 支持带有typeof 的编译时类型运算符。

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM