编译时类型没有RTTI与GCC

Compile time typeid without RTTI with GCC

本文关键字:RTTI GCC 类型 编译      更新时间:2023-10-16

是否有办法从禁用RTTI的GCC获得编译时typeid信息?在Visual Studio中,即使RTTI被禁用,像const char* typeName = typeid(int).name();这样的简单命令也会适当地返回"int"。不幸的是,GCC做不到这一点。当我试图调用typeid没有RTTI,我的程序崩溃。我知道禁用RTTI不是标准的一部分,但是我是否可以强迫GCC对已知类型进行编译时解析?

由于性能原因,

RTTI被禁用。我不需要运行时RTTI。

编辑:

这是我最后的结果:

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; } 

它要求对每个需要反射信息的类型调用REFLECTION_REGISTER_TYPE。但是,只要为每个所需的类型调用它,调用TypeName<int>就可以完美地工作。我还添加了函数TypeName(T type),这意味着你可以做这样的事情:int x = 0; printf(TypeName(x));,它将打印出"int"。GCC应该真的能够像vc++那样在编译时做到这一点。

还有另一种解决方案,它有利有弊:

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
}

首先,打开RTTI。

如果做不到这一点,如果你真的真的*需要在没有它的情况下获得一个类型的字符串表示,加上一点字符串操作,并且仔细考虑你正在编写的非标准代码可能会在升级GCC、改变平台或使用不同的选项集时中断,那么你可能能够做到。

#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;
}

该函数在ideone上的输出为

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

假设__PRETTY_FUNCTION__的行为与RTTI关闭时相同,拔出T = blah位应该不会太困难。

另外,请记住typeid(blah).name()提供了很少的保证…我记得在一个平台上使用它,任何用户定义的类型的结果都是简单的struct。不是很有用。依靠它是脆弱的,即使打开了RTTI[无论如何你都应该这样做]。

No。RTTI是RunTime类型信息(禁用它是愚蠢的,但是嘿),这就是typeid的目的。如果你想在编译时对类型名进行字符串化,你必须自己做(通过模板或宏)。

GCC支持typeof的编译时类型操作符

似乎至少有另一个承认RTTI经常不可用的现实的开发人员认为拥有"编译时类型信息"将是一件好事。

还有更多关于"ctti c++"的搜索结果,所以我希望有涵盖大多数有用和可能的选项。

基于编辑的凯尔,他的解决方案的变化。如果您有atmel avr 8位微控制器(如arduino uno, nano, mega(或类似)),则此解决方案非常有用。这种微控制器具有非常小的cpu和ram占用空间。定义和比较字符串的效率不高。要使其高效,需要使用enum。

遗憾的是,这个解决方案有一个依赖性(你只需要boost的预处理器部分)。类型可以在运行时进行比较。

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)

用法很简单:

#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(){}

正如你所看到的,你甚至可以注册多个类型集,如MyTypes1或MyTypes2。