【问题标题】:Compile time typeid without RTTI with GCC使用 GCC 编译没有 RTTI 的时间 typeid
【发布时间】:2011-11-03 20:11:25
【问题描述】:

在禁用 RTTI 的情况下,是否可以从 GCC 获取编译时 typeid 信息?在 Visual Studio 下,像 const char* typeName = typeid(int).name(); 这样的简单命令将适当地返回“int”,即使 RTTI 被禁用。不幸的是,GCC 不能这样做。当我尝试在没有 RTTI 的情况下调用 typeid 时,我的程序崩溃了。我知道禁用 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&lt;int&gt; 就可以完美地工作。我还添加了函数TypeName(T type),这意味着你可以这样做:int x = 0; printf(TypeName(x));,它会打印出“int”。 GCC 应该能够像 VC++ 一样在编译时做到这一点。

【问题讨论】:

  • 您关注哪些性能原因? RTTI 本质上是免费的,节省了一些二进制大小的开销。
  • @Kyle:在启用 RTTI 的 g++ 中,typeid(int).name() 将返回“i”而不是“int”。这相关吗?
  • @R. Martinho Fernandes:GCC 有一些解构函数,可以将“i”转换为“int”。

标签: c++ reflection


【解决方案1】:

还有另一种解决方案,各有优缺点:

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
}

【讨论】:

  • 您能否澄清评论中“模块”的含义?翻译单位?
  • 这看起来非常优雅!即使从不同的 TU 键入的相同类型可能具有不同的值。谢谢!
【解决方案2】:

首先,重新开启 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 [无论如何你都应该这样做],依赖它也是脆弱的。

【讨论】:

    【解决方案3】:

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

    【讨论】:

    • 我想做这样的事情:struct S { int a, b; } const char* aType = typeid(S::a).name(); 这是所有编译时间信息。虽然我可以简单地指定aint,但我宁愿让编译器这样做。这将使我的代码不那么冗长。 VC 编译器可以做到,我不确定是否有办法让 GCC 也能做到。
    • 我永远不会明白为什么 C++ 没有这个特性。比 RTTI 有用得多。
    • 我会小心称其为“愚蠢”。特别是如果您在 MCU 上有大量模板化代码和 256 kB 的闪存可用,您不希望将其中的一半浪费在您从未使用过的模板名称的字符串上。 x86_64 之外还有世界!
    【解决方案4】:

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

    【讨论】:

    • 我真正需要的是类型的名称。如果有办法将typeof 变成字符串,那就完美了。
    • @KennyTM:感谢您提供的链接。这最终就像我想出的解决方案
    【解决方案5】:

    似乎至少有其他开发人员承认 RTTI 通常不可用这一现实,认为拥有"compile time type information" 将是一件好事。

    还有更多关于“ctti C++”的搜索结果,所以我希望有一些选项可以涵盖大部分有用和可能的内容。

    注意:我从未使用过该库,因此使用风险自负。

    【讨论】:

      【解决方案6】:

      基于凯尔的编辑他的解决方案的变体。如果您拥有像 arduino uno、nano、mega(或类似)这样的 atmel avr 8 位微控制器,此解决方案将非常有用。这种微控制器的 CPU 和内存占用空间非常小。定义和比较字符串效率不高。为了提高效率,需要枚举。

      遗憾的是,这个解决方案有一个dependency(您只需要 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。

      【讨论】:

        猜你喜欢
        • 2012-09-07
        • 2013-04-22
        • 1970-01-01
        • 2011-09-17
        • 2012-02-02
        • 2013-03-11
        • 1970-01-01
        • 2015-07-16
        • 2011-01-26
        相关资源
        最近更新 更多