我希望能够自省一个c++类的名称,内容(即成员及其类型)等。这里我说的是原生c++,而不是托管c++,托管c++有反射。我意识到c++使用RTTI提供了一些有限的信息。哪些附加库(或其他技术)可以提供这些信息?


当前回答

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

其他回答

这个问题现在有点老了(不知道为什么我今天一直在问老问题),但我在想BOOST_FUSION_ADAPT_STRUCT,它引入了编译时反射。

当然,这取决于你将其映射到运行时反射,这不会太容易,但在这个方向上是可能的,而不是在相反的方向上:)

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

周围有两种反射。

Inspection by iterating over members of a type, enumerating its methods and so on. This is not possible with C++. Inspection by checking whether a class-type (class, struct, union) has a method or nested type, is derived from another particular type. This kind of thing is possible with C++ using template-tricks. Use boost::type_traits for many things (like checking whether a type is integral). For checking for the existence of a member function, use Templated check for the existence of a class member function? . For checking whether a certain nested type exists, use plain SFINAE .

如果你想要实现1),比如查看一个类有多少个方法,或者获取一个类id的字符串表示形式,那么恐怕标准c++没有办法做到这一点。你必须使用其中任何一个

一个元编译器,如Qt元对象编译器,它翻译你的代码,添加额外的元信息。 由宏组成的框架,允许您添加所需的元信息。你需要告诉框架所有的方法、类名、基类和它需要的一切。

c++的设计考虑到了速度。如果您想要高级别的检查,就像c#或Java所做的那样,不做一些额外的工作就无法做到这一点。

如果你正在寻找相对简单的c++反射——我从各种来源的宏/定义中收集了它们,并注释了它们的工作方式。你可以下载页眉 这里的文件:

https://github.com/tapika/TestCppReflect/blob/master/MacroHelpers.h

一组定义,加上它上面的功能:

https://github.com/tapika/TestCppReflect/blob/master/CppReflect.h https://github.com/tapika/TestCppReflect/blob/master/CppReflect.cpp https://github.com/tapika/TestCppReflect/blob/master/TypeTraits.h

示例应用程序驻留在git存储库以及,在这里: https://github.com/tapika/TestCppReflect/

我将部分复制在这里并进行解释:

#include "CppReflect.h"
using namespace std;


class Person
{
public:

    // Repack your code into REFLECTABLE macro, in (<C++ Type>) <Field name>
    // form , like this:

    REFLECTABLE( Person,
        (CString)   name,
        (int)       age,
...
    )
};

void main(void)
{
    Person p;
    p.name = L"Roger";
    p.age = 37;
...

    // And here you can convert your class contents into xml form:

    CStringW xml = ToXML( &p );
    CStringW errors;

    People ppl2;

    // And here you convert from xml back to class:

    FromXml( &ppl2, xml, errors );
    CStringA xml2 = ToXML( &ppl2 );
    printf( xml2 );

}

REFLECTABLE定义使用类名+字段名+偏移量-来标识特定字段位于内存中的哪个位置。我已经尽可能地学习。net术语,但是c++和c#是不同的,所以不是一对一的。整个c++反射模型驻留在TypeInfo和FieldInfo类中。

我已经使用pugi xml解析器获取演示代码到xml并从xml恢复回来。

演示代码的输出如下所示:

<?xml version="1.0" encoding="utf-8"?>
<People groupName="Group1">
    <people>
        <Person name="Roger" age="37" />
        <Person name="Alice" age="27" />
        <Person name="Cindy" age="17" />
    </people>
</People>

也可以通过TypeTraits类和部分模板规范来启用任何第三方类/结构支持-以类似于CString或int的方式定义自己的TypeTraitsT类-参见中的示例代码

https://github.com/tapika/TestCppReflect/blob/master/TypeTraits.h#L195

该解决方案适用于Windows / Visual studio。它可以移植到其他操作系统/编译器,但还没有这样做。(如果你真的喜欢解决方案,请问我,我可能会帮助你)

该解决方案适用于一个类和多个子类的一次序列化。

然而,如果你正在寻找序列化类部分的机制,甚至是控制反射调用产生的功能,你可以看看下面的解决方案:

https://github.com/tapika/cppscriptcore/tree/master/SolutionProjectModel

更详细的信息可以从youtube视频中找到:

c++运行时类型反射 https://youtu.be/TN8tJijkeFE

我试图更深入地解释c++反射是如何工作的。

示例代码如下所示:

https://github.com/tapika/cppscriptcore/blob/master/SolutionProjectModel/testCppApp.cpp

c.General.IntDir = LR"(obj\$(ProjectName)_$(Configuration)_$(Platform)\)";
c.General.OutDir = LR"(bin\$(Configuration)_$(Platform)\)";
c.General.UseDebugLibraries = true;
c.General.LinkIncremental = true;
c.CCpp.Optimization = optimization_Disabled;
c.Linker.System.SubSystem = subsystem_Console;
c.Linker.Debugging.GenerateDebugInformation = debuginfo_true;

但是这里的每一步实际上都会导致函数调用 使用c++属性__declspec(property(get =, put…).

它以路径的形式接收有关c++数据类型、c++属性名和类实例指针的全部信息,并根据这些信息生成xml、json,甚至在互联网上序列化这些信息。

这样的虚拟回调函数的例子可以在这里找到:

https://github.com/tapika/cppscriptcore/blob/master/SolutionProjectModel/VCConfiguration.cpp

参见函数ReflectCopy和虚函数::OnAfterSetProperty。

但是因为这个话题很高级,我建议先看视频。

如果您有一些改进的想法,请随时与我联系。

看起来c++仍然没有这个特性。 c++ 11也有延迟反射(

搜索一些宏或者自己制作。Qt还可以帮助进行反射(如果可以使用的话)。

I did something like what you're after once, and while it's possible to get some level of reflection and access to higher-level features, the maintenance headache might not be worth it. My system was used to keep the UI classes completely separated from the business logic through delegation akin to Objective-C's concept of message passing and forwarding. The way to do it is to create some base class that is capable of mapping symbols (I used a string pool but you could do it with enums if you prefer speed and compile-time error handling over total flexibility) to function pointers (actually not pure function pointers, but something similar to what Boost has with Boost.Function--which I didn't have access to at the time). You can do the same thing for your member variables as long as you have some common base class capable of representing any value. The entire system was an unabashed ripoff of Key-Value Coding and Delegation, with a few side effects that were perhaps worth the sheer amount of time necessary to get every class that used the system to match all of its methods and members up with legal calls: 1) Any class could call any method on any other class without having to include headers or write fake base classes so the interface could be predefined for the compiler; and 2) The getters and setters of the member variables were easy to make thread-safe because changing or accessing their values was always done through 2 methods in the base class of all objects.

It also led to the possibility of doing some really weird things that otherwise aren't easy in C++. For example I could create an Array object that contained arbitrary items of any type, including itself, and create new arrays dynamically by passing a message to all array items and collecting the return values (similar to map in Lisp). Another was the implementation of key-value observing, whereby I was able to set up the UI to respond immediately to changes in the members of backend classes instead of constantly polling the data or unnecessarily redrawing the display.

也许您更感兴趣的是,您还可以转储为类定义的所有方法和成员,而且是字符串形式。

该系统的缺点可能会让您望而却步:添加所有消息和键值非常繁琐;它比没有反射要慢;你会讨厌看到boost::static_pointer_cast和boost::dynamic_pointer_cast遍布你的代码库;强类型系统的局限性仍然存在,您实际上只是将它们隐藏了一些,所以它不那么明显。字符串中的错别字也不是一个有趣或容易发现的惊喜。

As to how to implement something like this: just use shared and weak pointers to some common base (mine was very imaginatively called "Object") and derive for all the types you want to use. I'd recommend installing Boost.Function instead of doing it the way I did, which was with some custom crap and a ton of ugly macros to wrap the function pointer calls. Since everything is mapped, inspecting objects is just a matter of iterating through all of the keys. Since my classes were essentially as close to a direct ripoff of Cocoa as possible using only C++, if you want something like that then I'd suggest using the Cocoa documentation as a blueprint.