大多数成熟的 C++ 项目似乎都有自己的反射和属性系统,即用于定义可以通过字符串访问并自动序列化的属性。至少我参与的许多 C++ 项目似乎都在重新发明轮子。
您是否知道任何支持反射和属性容器的 C++开源库,特别是:
- 通过宏定义 RTTI 和属性
- 通过代码访问 RTTI 和属性
- 属性的自动序列化
- 监听属性修改(例如 OnValueChanged)
大多数成熟的 C++ 项目似乎都有自己的反射和属性系统,即用于定义可以通过字符串访问并自动序列化的属性。至少我参与的许多 C++ 项目似乎都在重新发明轮子。
您是否知道任何支持反射和属性容器的 C++开源库,特别是:
有一个使用完全不同的方法在 C++ 中提供反射的新项目:CAMP。 https://github.com/tegesoft/camp
CAMP 不使用预编译器,classes/properties/functions/... 是使用类似于 boost.python 或 luabind 的语法手动声明的。当然,如果他们愿意,人们可以使用像 gccxml 或 open-c++ 这样的预编译器来生成这个声明。
它仅基于纯 C++ 和 boost 头文件,并且由于模板元编程的强大功能,它支持任何类型的可绑定实体(例如,继承和奇怪的构造函数不是问题)。
它是根据 MIT 许可证(以前的 LGPL)分发的。
这就是当 C++ 遇到反射时你得到的:
无论您选择什么,它都可能有可怕的宏、难以调试的代码或奇怪的构建步骤。我见过一个系统自动从 DevStudio 的 PDB 文件生成序列化代码。
不过说真的,对于小型项目,编写保存/加载函数(或使用流操作符)会更容易。事实上,这也可能适用于大型项目 - 很明显发生了什么,如果结构发生变化,您通常需要更改代码。
我看了这些东西很长一段时间,但它们往往是非常严厉的。它们可能会阻止您使用继承,或者使用奇怪的构造函数等。最后,它们最终成为太多的负担而不是便利。
我现在使用的这种公开成员的方法非常轻量级,例如,您可以探索一个用于序列化的类或将所有名为“x”的字段设置为 0。它也是静态确定的,因此非常非常快。无需担心会干扰构建过程的库代码或代码生成层。它概括为嵌套类型的层次结构。
使用一些宏设置您的编辑器以自动编写其中一些内容。
struct point
{
int x;
int y;
// add this to your classes
template <typename Visitor>
void visit(Visitor v)
{
v->visit(x, "x");
v->visit(y, "y");
}
};
/** Outputs any type to standard output in key=value format */
struct stdout_visitor
{
template <typename T>
void visit(const T& rhs)
{
rhs.visit(this);
}
template <typename Scalar>
void visit (const Scalar& s, const char* name)
{
std::cout << name << " = " << s << " ";
}
}
这通常是 C++ 语言的一个臭名昭著的弱点,因为需要标准化以使反射实现可移植和有价值的东西不是标准的。我想到了调用约定、对象布局和符号修饰,但还有其他一些。
缺乏来自标准的方向意味着编译器实现者会做一些不同的事情,这意味着很少有人有动力去写一个可移植的反射库,这意味着需要反射的人重新发明轮子,但只是够用了他们需要什么。这种情况会无限发生,我们就在这里。
这个也看了一会儿。当前最简单的解决方案似乎是BOOST_FUSION_ADAPT_STRUCT。实际上,一旦有了库/头文件,您只需将结构字段添加到 BOOST_FUSION_ADAPT_STRUCT() 宏中,如代码的最后一段所示。是的,它有许多其他人提到的限制。而且它不直接支持听众。
我研究的其他有希望的解决方案是
c2ph
/pstruct
从 的输出中转储元信息,gcc -gstabs
虽然它对我来说非常有效,但它的侵入性较小,但需要更多的工作。关于 boost/__cxa 方法,一旦弄清楚所有小细节,添加/更改结构或字段就很容易维护。我们目前使用它在 dbus 之上构建自定义类型绑定层,以序列化 API 并隐藏托管对象服务子系统的传输/RPC 详细信息。
不是通用的,但 QT 通过元编译器支持这一点,并且是 GPL。我与 QT 人员交谈的理解是,纯 C++ 无法做到这一点,因此需要 moc。
自动内省/反思工具包。使用 Qt 之类的元编译器并将元信息直接添加到目标文件中。直观易用。没有外部依赖。甚至允许自动反映 std::string 然后在脚本中使用它。请访问IDK