我希望能够自省一个c++类的名称,内容(即成员及其类型)等。这里我说的是原生c++,而不是托管c++,托管c++有反射。我意识到c++使用RTTI提供了一些有限的信息。哪些附加库(或其他技术)可以提供这些信息?
当前回答
查看Classdesc http://classdesc.sf.net。它以类“描述符”的形式提供了反射,可以与任何标准c++编译器一起工作(是的,它可以与Visual Studio和GCC一起工作),并且不需要源代码注释(尽管存在一些处理棘手情况的pragmas)。它已经开发了十多年,并在一些工业规模的项目中使用。
其他回答
如果你像这样声明一个指向函数的指针:
int (*func)(int a, int b);
您可以像这样在内存中为该函数分配一个位置(需要libdl和dlopen)
#include <dlfcn.h>
int main(void)
{
void *handle;
char *func_name = "bla_bla_bla";
handle = dlopen("foo.so", RTLD_LAZY);
*(void **)(&func) = dlsym(handle, func_name);
return func(1,2);
}
要使用间接方式加载局部符号,可以对调用二进制文件(argv[0])使用dlopen。
这样做的唯一要求(除了dlopen()、libdl和dlfcn.h)是知道函数的参数和类型。
您需要查看您正在尝试做什么,以及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的模板魔法,可以在运行时(读取配置文件时)将其转换为一系列方法调用,因此相当高效。我不建议你这样做,除非你真的需要,但是,当你这样做的时候,你可以做一些非常酷的事情。
我建议使用Qt。
有一个开源许可证和一个商业许可证。
如果你正在寻找相对简单的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。
但是因为这个话题很高级,我建议先看视频。
如果您有一些改进的想法,请随时与我联系。
这些信息确实存在——但不是你需要的格式,而且只有当你导出你的类时。这适用于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-safety之类的东西,所以这些方面是作为运行时检查实现的。
我相信语法可以改进,到目前为止它只适用于Win32和Win64。我们发现它非常有用,可以为类提供自动GUI接口,在c++中创建属性,流到XML和从XML输出等等,而且不需要从特定的基类派生。如果有足够的需求,也许我们可以把它做成样子发布。
推荐文章
- 是否需要手动关闭ifstream?
- BindingFlags。IgnoreCase不为Type.GetProperty()工作?
- 为什么函数指针定义可以使用任意数量的&号或星号* ?
- 为什么我必须通过this指针访问模板基类成员?
- 什么是可重入函数?
- 什么是栈展开?
- 我如何读整个文件到性病::字符串在c++ ?
- 如何在c++中使用枚举
- 为什么512x512矩阵的转置比513x513矩阵的转置慢得多?
- 在Java中检查一个类对象是否是另一个类对象的子类
- 如何在underscore.js模板中使用if语句?
- 通过引用传递数组
- 如何获得具有给定属性的属性列表?
- 为什么非const引用不能绑定到临时对象?
- sizeof(某个指针)总是等于4吗?