如何向C ++应用程序添加反射?

时间:2020-03-05 18:46:57  来源:igfitidea点击:

我希望能够对C ++类的名称,内容(即成员及其类型)等进行自省。我在这里说的是本机C ++,而不是具有反射的托管C ++。我意识到C ++使用RTTI提供了一些有限的信息。哪些其他库(或者其他技术)可以提供此信息?

解决方案

回答

我会爱小马,但小马不是免费的。 :-p

我们将获得http://en.wikibooks.org/wiki/C%2B%2B_Programming/RTTI。就像我们正在考虑的反射一样-运行时可用的完全描述性元数据-默认情况下对于C ++而言不存在。

回答

我们想用反射做什么?
我们可以将Boost类型特征和typeof库用作编译时反射的有限形式。也就是说,我们可以检查和修改传递给模板的类型的基本属性。

回答

从我的C ++时代开始,我知道两种类似于反射的解决方案:

1)使用RTTI,如果我们能够使所有类都从"对象"基类派生,则可以使用它来建立类似反射的行为。该类可以提供一些方法,例如GetMethod,GetBaseClass等。关于这些方法的工作方式,我们将需要手动添加一些宏来装饰类型,这些宏在幕后在该类型中创建元数据以提供对GetMethods等的答案。

2)如果我们有权访问编译器对象,则另一个选择是使用DIA SDK。如果我没记错的话,这可以让我们打开pdbs,其中应包含C ++类型的元数据。做我们需要的事情可能就足够了。例如,此页面显示如何获取类的所有基本类型。

这些解决方案都有点丑陋!没有什么比C ++能让我们欣赏C#的奢侈了。

祝你好运。

回答

我建议使用Qt。

有开源许可证和商业许可证。

回答

RTTI doesn't exist for C++.

这是完全错误的。实际上,术语RTTI是C ++标准创造的。另一方面,RTTI在实现反射方面进展不大。

回答

一种简单的方法是使用" dynamic_cast <>()"运算符,当将其分配给错误的类型时,该运算符将返回NULL,因此我们可以通过一种简便的方法将其转换为基础具体类,并检查指针的值(如果有)不是NULL,则转换已完成,我们得到了对象的类型。

但这只是一个简单的解决方案,它仅提供对象的类型,我们无法询问它具有什么方法,就像Java中一样。如果我们需要高级解决方案,则可以选择一些框架。

回答

我们需要查看我们要做什么,以及RTTI是否可以满足要求。我已经为一些非常特定的目的实现了自己的伪反射。例如,我曾经希望能够灵活地配置模拟输出。它需要将一些样板代码添加到要输出的类中:

namespace {
  static bool b2 = Filter::Filterable<const MyObj>::Register("MyObject");
} 

bool MyObj::BuildMap()
{
  Filterable<const OutputDisease>::AddAccess("time", &MyObj::time);
  Filterable<const OutputDisease>::AddAccess("person", &MyObj::id);
  return true;
}

第一次调用将这个对象添加到过滤系统,该过滤系统调用" BuildMap()"方法以找出可用的方法。

然后,在配置文件中,我们可以执行以下操作:

FILTER-OUTPUT-OBJECT   MyObject
FILTER-OUTPUT-FILENAME file.txt
FILTER-CLAUSE-1        person == 1773
FILTER-CLAUSE-2        time > 2000

通过一些涉及" boost"的模板魔术,它可以在运行时转换为一系列方法调用(读取配置文件时),因此它相当有效。除非我们确实需要,否则我不建议我们这样做,但是,当我们这样做时,我们可以做一些非常酷的事情。

回答

我认为我们可能会发现Dominic Filion的文章"在C ++中使用反射模板"很有趣。它在Game Programming Gems 5的1.4节中。不幸的是,我没有随身携带副本,但请寻找它,因为我认为它可以解释要求。

回答

游泳有两种"反射"。

  • 通过遍历类型的成员,枚举其方法等进行检查。对于C ++,这是不可能的。
  • 通过检查类类型(类,结构,联合)是否具有方法或者嵌套类型来进行检查,该方法是从另一个特定类型派生的。在C ++中,使用template-tricks可以实现这种事情。在很多事情上使用boost :: type_traits(例如检查一个类型是否是整数)。要检查成员函数的存在,请使用是否可以编写模板来检查函数的存在? 。要检查是否存在某种嵌套类型,请使用普通SFINAE。

如果我们宁愿寻找完成1)的方法,例如查看一个类有多少种方法,或者想要获取一个类id的字符串表示形式,那么恐怕没有标准的C ++方法可以做到这一点。我们必须使用

  • 像Qt Meta Object Compiler这样的Meta编译器,可翻译代码并添加其他元信息。
  • 由宏组成的框架,允许我们添加所需的元信息。我们将需要告诉框架所有方法,类名,基类及其所需的一切。

C ++是在考虑速度的前提下制作的。如果我们希望像Cor Java一样进行高级检查,那么恐怕我不得不告诉我们,没有任何努力就没有办法。

回答

我曾经做过类似我们想做的事情,虽然有可能获得一定程度的反思和访问更高级别的功能,但维护麻烦可能不值得。我的系统用于通过类似于Objective-C的消息传递和转发概念的委派来使UI类与业务逻辑完全分开。做到这一点的方法是创建一些能够将符号映射到函数指针的基类(我使用了一个字符串池,但是如果我们更喜欢速度和编译时错误处理而不是总的灵活性,则可以使用枚举来实现)到函数指针纯函数指针,但类似于Boost在Boost.Function中的功能(我当时无法访问)。只要我们有一些可以表示任何值的通用基类,就可以对成员变量执行相同的操作。整个系统毫不掩饰地窃取了键值编码和委派,其副作用可能值得花大量的时间来让使用该系统的每个类将其所有方法和成员与合法电话进行匹配:1)任何类都可以在任何其他类上调用任何方法,而不必包含标头或者编写伪造的基类,因此可以为编译器预定义接口;和2)成员变量的getter和setter易于使用线程安全,因为更改或者访问它们的值始终是通过所有对象的基类中的2个方法完成的。

这也导致可能做一些真正奇怪的事情,而这些事情在C ++中是不容易的。例如,我可以创建一个包含任何类型的任意项(包括其自身)的Array对象,并通过将消息传递给所有数组项并收集返回值来动态创建新数组(类似于Lisp中的map)。另一个是键值观察的实现,通过该操作,我可以将UI设置为立即响应后端类的成员的更改,而不必不断地轮询数据或者不必要地重绘显示。

对我们来说也许更有趣的是,我们还可以转储为类定义的所有方法和成员,并且不少于字符串形式。

该系统的缺点可能会打扰我们:添加所有消息和键值非常繁琐;比没有任何思考要慢;我们会讨厌在整个代码库中看到" boost :: static_pointer_cast"和" boost :: dynamic_pointer_cast",而变得非常讨厌;强类型系统的局限性仍然存在,我们实际上只是将它们隐藏了一点,因此并不那么明显。字符串中的错别字也不是有趣或者容易发现的惊喜。

至于如何实现这样的事情:只需要使用共享和弱指针来指向一些通用的基数(我的名字就被想象为"对象"),并为我们要使用的所有类型派生。我建议安装Boost.Function而不是像以前那样安装,它带有一些自定义废话和大量丑陋的宏来包装函数指针调用。由于所有内容都已映射,因此检查对象仅是遍历所有键的问题。由于我的课程从本质上讲仅使用C ++尽可能接近直接获取Cocoa,所以如果我们想要类似的东西,那么我建议我们使用Cocoa文档作为蓝图。

回答

该信息确实存在,但不以我们需要的格式提供,并且仅在我们导出类时才提供。这在Windows中有效,我对其他平台一无所知。例如,使用存储类说明符:

class __declspec(export) MyClass
{
public:
    void Foo(float x);
}

这使编译器将类定义数据构建到DLL / Exe中。但这不是我们可以随时用于反射的格式。

在我公司,我们建立了一个解释该元数据的库,并允许我们在不向类本身插入额外宏等的情况下反映一个类。它允许如下调用函数:

MyClass *instance_ptr=new MyClass;
GetClass("MyClass")->GetFunction("Foo")->Invoke(instance_ptr,1.331);

这有效地做到了:

instance_ptr->Foo(1.331);

Invoke(this_pointer,...)函数具有可变参数。显然,通过以这种方式调用函数可以避免const安全等问题,因此这些方面都可以作为运行时检查来实现。

我敢肯定语法会得到改进,到目前为止,它仅适用于Win32和Win64. 我们发现它对于使类具有自动GUI界面,在C ++中创建属性,与XML进行流传输以及从XML进行流传输等非常有用,并且不需要从特定的基类派生。如果有足够的需求,也许我们可以将其成型以供发布。

回答

缺少C ++中的内置反射是现代C ++不用于Web开发的唯一原因(并且缺少ORM和其他框架)

你可以试试
http://www.extreme.indiana.edu/reflcpp/

回答

当我想用C ++进行反思时,我读了这篇文章,并对那里的内容进行了改进。对不起,没有可以。我不拥有结果...但是我们当然可以得到我拥有的一切,然后从那里去。

我目前正在研究自己喜欢的方法,以线性方式使用Inherited_linear来简化可反射类型的定义。实际上我已经走了很远,但是我还有路要走。 C ++ 0x的更改很有可能在此领域提供很多帮助。

回答

编辑:不再维护CAMP;提供两个叉子:

  • 一种也称为CAMP,并且基于相同的API。
  • Ponder是部分重写,因此应优先使用,因为它不需要Boost;它使用的是C ++ 11.

CAMP是MIT许可的库(以前为LGPL),可为C ++语言增加反射。它不需要在编译中进行特定的预处理步骤,但必须手动进行绑定。

当前的Tegesoft库使用Boost,但是还有一个使用C ++ 11的fork,不再需要Boost。

回答

这个问题现在有点老了(不知道为什么今天我会继续提出老问题),但是我正在考虑引入编译时反射的BOOST_FUSION_ADAPT_STRUCT。

当然,这取决于我们将其映射到运行时反射,这并不是一件容易的事,但是有可能朝这个方向发展,而并非相反:)

我真的认为一个宏可以封装" BOOST_FUSION_ADAPT_STRUCT",它可以生成必要的方法来获取运行时行为。

回答

反思本质上是关于编译器决定在运行时代码可以查询的代码中留下的内容。 C ++以不支付我们不使用的东西而闻名。因为大多数人不使用/不希望反射,所以C ++编译器通过不记录任何内容来避免成本。

因此,C ++不能提供反射,而且要像其他答案所指出的那样,一般性地自己"模拟"它并不容易。

在"其他技术"下,如果我们没有使用反射的语言,请获取一个可以在编译时提取所需信息的工具。

我们的DMS软件再造工具包是由明确的语言定义参数化的通用编译器技术。它具有针对C,C ++,Java,COBOL,PHP等的语言定义。

对于C,C ++,Java和COBOL版本,它提供对语法分析树和符号表信息的完整访问。该符号表信息包括"反射"可能需要的数据类型。如果目标是枚举一组字段或者方法并对其进行处理,则可以使用DMS根据我们在符号表中找到的内容以任意方式转换代码。