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

有帮助吗?

解决方案 17

思考 是一个C++反射库,回答这个问题。我考虑了这些选择并决定自己制作,因为我找不到一个符合我所有要求的选项。

尽管这个问题有很好的答案,但我不想使用大量的宏,也不想依赖 Boost。Boost 是一个很棒的库,但是有许多小型定制的 C++0x 项目更简单并且编译时间更快。能够从外部修饰类也有一些优点,例如包装还不支持 C++11 的 C++ 库。它是 CAMP 的分支,使用 C++11, 不再需要Boost.

其他提示

您需要做的是让预处理器生成有关字段的反射数据。该数据可以存储为嵌套类。

首先,为了使在预处理器中编写它更容易、更清晰,我们将使用类型化表达式。类型化表达式只是将类型放在括号中的表达式。所以而不是写 int x 你会写 (int) x. 。这里有一些方便的宏来帮助处理类型表达式:

#define REM(...) __VA_ARGS__
#define EAT(...)

// Retrieve the type
#define TYPEOF(x) DETAIL_TYPEOF(DETAIL_TYPEOF_PROBE x,)
#define DETAIL_TYPEOF(...) DETAIL_TYPEOF_HEAD(__VA_ARGS__)
#define DETAIL_TYPEOF_HEAD(x, ...) REM x
#define DETAIL_TYPEOF_PROBE(...) (__VA_ARGS__),
// Strip off the type
#define STRIP(x) EAT x
// Show the type without parenthesis
#define PAIR(x) REM x

接下来,我们定义一个 REFLECTABLE 宏生成有关每个字段的数据(加上字段本身)。这个宏将被这样调用:

REFLECTABLE
(
    (const char *) name,
    (int) age
)

所以使用 升压PP 我们迭代每个参数并生成如下数据:

// A helper metafunction for adding const to a type
template<class M, class T>
struct make_const
{
    typedef T type;
};

template<class M, class T>
struct make_const<const M, T>
{
    typedef typename boost::add_const<T>::type type;
};


#define REFLECTABLE(...) \
static const int fields_n = BOOST_PP_VARIADIC_SIZE(__VA_ARGS__); \
friend struct reflector; \
template<int N, class Self> \
struct field_data {}; \
BOOST_PP_SEQ_FOR_EACH_I(REFLECT_EACH, data, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))

#define REFLECT_EACH(r, data, i, x) \
PAIR(x); \
template<class Self> \
struct field_data<i, Self> \
{ \
    Self & self; \
    field_data(Self & self) : self(self) {} \
    \
    typename make_const<Self, TYPEOF(x)>::type & get() \
    { \
        return self.STRIP(x); \
    }\
    typename boost::add_const<TYPEOF(x)>::type & get() const \
    { \
        return self.STRIP(x); \
    }\
    const char * name() const \
    {\
        return BOOST_PP_STRINGIZE(STRIP(x)); \
    } \
}; \

它的作用是生成一个常数 fields_n 这是类中可反射字段的数量。然后它专门 field_data 对于每个字段。它还朋友 reflector 类,这样即使字段是私有的,它也可以访问它们:

struct reflector
{
    //Get field_data at index N
    template<int N, class T>
    static typename T::template field_data<N, T> get_field_data(T& x)
    {
        return typename T::template field_data<N, T>(x);
    }

    // Get the number of fields
    template<class T>
    struct fields
    {
        static const int n = T::fields_n;
    };
};

现在,为了迭代字段,我们使用访问者模式。我们创建一个从 0 到字段数的 MPL 范围,并访问该索引处的字段数据。然后它将字段数据传递给用户提供的访问者:

struct field_visitor
{
    template<class C, class Visitor, class I>
    void operator()(C& c, Visitor v, I)
    {
        v(reflector::get_field_data<I::value>(c));
    }
};


template<class C, class Visitor>
void visit_each(C & c, Visitor v)
{
    typedef boost::mpl::range_c<int,0,reflector::fields<C>::n> range;
    boost::mpl::for_each<range>(boost::bind<void>(field_visitor(), boost::ref(c), v, _1));
}

现在,为了关键时刻,我们将所有内容放在一起。这是我们如何定义一个 Person 可反射的类:

struct Person
{
    Person(const char *name, int age)
        :
        name(name),
        age(age)
    {
    }
private:
    REFLECTABLE
    (
        (const char *) name,
        (int) age
    )
};

这里有一个概括的 print_fields 使用反射数据迭代字段的函数:

struct print_visitor
{
    template<class FieldData>
    void operator()(FieldData f)
    {
        std::cout << f.name() << "=" << f.get() << std::endl;
    }
};

template<class T>
void print_fields(T & x)
{
    visit_each(x, print_visitor());
}

使用的示例 print_fields 与可反射的 Person 班级:

int main()
{
    Person p("Tom", 82);
    print_fields(p);
    return 0;
}

哪个输出:

name=Tom
age=82

瞧,我们刚刚在 C++ 中用不到 100 行代码实现了反射。

有两种 reflection 游来游去。

  1. 通过迭代类型的成员、枚举其方法等进行检查。

    这对于 C++ 是不可能的。
  2. 通过检查类类型(类、结构、联合)是否具有方法或嵌套类型来检查是否派生自另一个特定类型。

    这种事情在 C++ 中是可能的 template-tricks. 。使用 boost::type_traits 对于很多事情(比如检查类型是否是整数)。要检查成员函数是否存在,请使用 是否可以编写一个模板来检查函数是否存在? 。要检查某个嵌套类型是否存在,请使用 plain 新加坡金融学会 .

如果您正在寻找实现 1) 的方法,例如查看一个类有多少个方法,或者喜欢获取类 id 的字符串表示形式,那么恐怕没有标准 C++ 方法可以做到这一点。你必须使用

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

C++ 的设计注重速度。如果您想要像 C# 或 Java 那样进行高级检查,那么恐怕我必须告诉您,不付出一些努力是没有办法的。

我想要一匹小马,但小马不是免费的。:-p

http://en.wikibooks.org/wiki/C%2B%2B_Programming/RTTI 就是你将会得到的。默认情况下,C++ 不存在您正在考虑的反射(在运行时可用的完整描述性元数据)。

C++ 不存在 RTTI。

这是完全错误的。实际上,“RTTI”这个术语是由 C++ 标准创造的。另一方面,RTTI 在实现反射方面并没有走得太远。

该信息确实存在 - 但不是您需要的格式,并且仅当您导出类时才存在。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 进行流式传输等等非常有用,并且不需要从特定的基类派生。如果有足够的需求,也许我们可以将其成型并发布。

您需要看看您想要做什么,以及 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.

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

编辑: 不再维护;有两种叉子可供选择:

  • 有一种也叫 也是如此,并且基于相同的 API。
  • 思考 是部分重写,并且应该是首选,因为它不需要 Boost ;它使用 C++11。

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

当前的Tegesoft库使用Boost,但也有 一把叉子 使用 C++11 不再需要Boost.

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

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

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

该系统的缺点可能会阻止您打扰:添加所有消息和键值非常繁琐;它比没有任何反射要慢;你会越来越讨厌看到 boost::static_pointer_castboost::dynamic_pointer_cast 充满激情地遍布你的代码库;强类型系统的局限性仍然存在,您实际上只是将它们隐藏了一点,所以它不那么明显。字符串中的拼写错误也不是一个有趣或容易发现的惊喜。

至于如何实现这样的事情:只需使用指向某个公共基础的共享指针和弱指针(我的指针非常富有想象力地称为“对象”)并派生出您想要使用的所有类型。我建议安装 Boost.Function 而不是像我那样做,我用一些自定义的废话和大量丑陋的宏来包装函数指针调用。由于所有内容都已映射,因此检查对象只需迭代所有键即可。由于我的课程本质上尽可能接近于直接抄袭 Cocoa,仅使用 C++,如果您想要类似的东西,那么我建议使用 Cocoa 文档作为蓝图。

我从 C++ 时代了解到的两个类似反射的解决方案是:

1) 使用 RTTI,如果您能够从“对象”基类派生所有类,它将为您提供构建类似反射的行为的引导程序。该类可以提供一些方法,例如 GetMethod、GetBaseClass 等。至于这些方法如何工作,您需要手动添加一些宏来装饰您的类型,这些宏在幕后创建类型中的元数据以提供 GetMethods 等的答案。

2)如果您有权访问编译器对象,另一种选择是使用 DIA SDK. 。如果我没记错的话,这可以让你打开 pdb,它应该包含你的 C++ 类型的元数据。这可能足以满足您的需要。 这一页 例如,展示如何获取类的所有基类型。

但这两种解决方案都有点难看!没有什么比了解一点 C++ 更能让您体会到 C# 的魅力了。

祝你好运。

C++ 中还有另一个用于反射的新库,称为 RTTR (运行时类型反射,另请参阅 github).

该接口类似于 C# 中的反射,并且无需任何 RTTI 即可工作。

编辑:截至 2017 年 2 月 7 日更新了损坏的链接。

我想没有人提到过这一点:

在 CERN,他们使用 C++ 的完整反射系统:

欧洲核子研究中心反射. 。看起来效果很好。

C++ 不支持反射。这是可悲的,因为它使防御测试变得痛苦。

有几种进行反射的方法:

  1. 使用调试信息(不可移植)。
  2. 在代码中添加宏/模板或其他一些源代码方法(看起来很难看)
  3. 修改编译器(例如 clang/gcc)来生成数据库。
  4. 使用Qt moc方法
  5. 增强反射
  6. 精确且平坦的反射

第一个链接看起来最有前途(使用 mod 来 clang),第二个链接讨论了许多技术,第三个链接是使用 gcc 的不同方法:

  1. http://www.donw.org/rfl/

  2. https://bitbucket.org/dwilliamson/clreflect

  3. https://root.cern.ch/how/how-use-reflex

现在有一个 C++ 反射工作组。请参阅 C++14 @ CERN 的新闻:

编辑 2017 年 8 月 13 日:自最初的帖子以来,反思已经取得了许多潜在的进展。以下提供了有关各种技术和状态的更多详细信息和讨论:

  1. 简而言之,静态反射
  2. 静态反射
  3. 静态反射设计

然而,在不久的将来,C++ 中的标准化反射方法看起来前景并不乐观,除非社区对支持 C++ 反射表现出更大的兴趣。

以下根据上次 C++ 标准会议的反馈详细介绍了当前状态:

编辑 2017 年 12 月 13 日

Reflection 看起来正在向 C++ 20 或更可能是 TSR 迈进。然而运动却很缓慢。

编辑 15/09/2018

TS 草案已发送给国家机构进行投票。

文本可以在这里找到: https://github.com/cplusplus/reflection-ts

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

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

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

我想您可能会对 Dominic Filion 的文章“在 C++ 中使用反射模板”感兴趣。它位于第 1.4 节 游戏编程宝石 5. 。不幸的是,我没有随身携带副本,但请查找它,因为我认为它解释了您的要求。

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

因此,C++ 不提供反射,并且像其他答案所指出的那样,将其作为一般规则自己“模拟”并不容易。

在“其他技术”下,如果你没有带有反射的语言, 获取一个可以在编译时提取所需信息的工具。

我们的 DMS 软件再造工具包 是通过显式语言定义参数化的通用编译器技术。它具有 C、C++、Java、COBOL、PHP 等语言定义

对于 C、C++、Java 和 COBOL 版本,它提供对解析树和符号表信息的完整访问。该符号表信息包括您可能想要从“反射”中获得的数据类型。如果您的目标是枚举某些字段或方法集并且 有了它们,DMS 可用于根据您在符号表中找到的内容以任意方式转换代码。

您可以在这里找到另一个库: http://www.garret.ru/cppreflection/docs/reflect.html它支持2种方式:从调试信息中获取类型信息并让程序员提供此信息。

我也对我的项目的反思感兴趣,并找到了这个库,我还没有尝试过,但尝试了这个人的其他工具,我喜欢它们的工作方式:-)

查看类描述 http://classdesc.sf.net. 。它以类“描述符”的形式提供反射,可以与任何标准 C++ 编译器一起使用(是的,它可以与 Visual Studio 以及 GCC 一起使用),并且不需要源代码注释(尽管存在一些编译指示来处理棘手的情况) )。它已经开发了十多年,并用于许多工业规模的项目。

当我想用 C++ 进行反射时,我读了 本文 并改进了我在那里看到的内容。抱歉,没有罐头。我不拥有结果......但你当然可以得到我所拥有的并从那里开始。

我目前正在研究,当我愿意时,使用inherit_linearly 的方法可以使可反射类型的定义变得更加容易。实际上我已经取得了相当大的进展,但我还有很长的路要走。C++0x 中的变化很可能会在这方面提供很多帮助。

貌似C++还没有这个功能。和 C++11 也推迟了反思((

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

尝试看看这个项目 http://www.garret.ru/cppreflection/docs/reflect.html向 C++ 添加了反射。它将元数据添加到您随后可以使用的类中。

尽管 C++ 中不支持开箱即用的反射,但实现起来并不难。我遇到过这篇很棒的文章:http://replicaisland.blogspot.co.il/2010/11/building-reflective-object-system-in-c.html

这篇文章非常详细地解释了如何实现一个非常简单和基本的反射系统。当然,这不是最健康的解决方案,并且还有一些粗糙的边缘需要解决,但对于我的需求来说,它已经足够了。

底线 - 如果做得正确,反射可以得到回报,并且在 C++ 中是完全可行的。

我想宣传自动内省/反思工具包“IDK”的存在。它使用类似于 Qt 的元编译器,并将元信息直接添加到目标文件中。据称它易于使用。没有外部依赖。它甚至允许您自动反映 std::string 然后在脚本中使用它。请看 IDK

C++ 中的反射非常有用,如果您需要为每个成员运行一些方法(例如:序列化、散列、比较)。我提供了通用的解决方案,语法非常简单:

struct S1
{
    ENUMERATE_MEMBERS(str,i);
    std::string str;
    int i;
};
struct S2
{
    ENUMERATE_MEMBERS(s1,i2);
    S1 s1;
    int i2;
};

其中ENUMERATE_MEMBERS是一个宏,稍后描述(UPDATE):

假设我们为 int 和 std::string 定义了序列化函数,如下所示:

void EnumerateWith(BinaryWriter & writer, int val)
{
    //store integer
    writer.WriteBuffer(&val, sizeof(int));
}
void EnumerateWith(BinaryWriter & writer, std::string val)
{
    //store string
    writer.WriteBuffer(val.c_str(), val.size());
}

我们在“秘密宏”附近有通用函数;)

template<typename TWriter, typename T>
auto EnumerateWith(TWriter && writer, T && val) -> is_enumerable_t<T>
{
    val.EnumerateWith(write); //method generated by ENUMERATE_MEMBERS macro
}

现在你可以写

S1 s1;
S2 s2;
//....
BinaryWriter writer("serialized.bin");

EnumerateWith(writer, s1); //this will call EnumerateWith for all members of S1
EnumerateWith(writer, s2); //this will call EnumerateWith for all members of S2 and S2::s1 (recursively)

因此,在结构定义中使用 ENUMERATE_MEMBERS 宏,您可以构建序列化、比较、散列和其他内容,而无需触及原始类型,唯一的要求是为每个类型实现“EnumerateWith”方法,该方法不可枚举,每个枚举器(如 BinaryWriter) 。通常您必须实现 10-20 个“简单”类型才能支持项目中的任何类型。

该宏在运行时创建/销毁结构体的开销应为零,并且 T.EnumerateWith() 的代码应按需生成,这可以通过使其成为模板内联函数来实现,因此唯一的开销所有的故事都是向每个结构添加 ENUMERATE_MEMBERS(m1,m2,m3...) ,而在任何解决方案中都必须实现每个成员类型的特定方法,因此我不认为它是开销。

更新:ENUMERATE_MEMBERS 宏的实现非常简单(但是可以稍微扩展以支持从可枚举结构继承)

#define ENUMERATE_MEMBERS(...) \
template<typename TEnumerator> inline void EnumerateWith(TEnumerator & enumerator) const { EnumerateWithHelper(enumerator, __VA_ARGS__ ); }\
template<typename TEnumerator> inline void EnumerateWith(TEnumerator & enumerator) { EnumerateWithHelper(enumerator, __VA_ARGS__); }

// EnumerateWithHelper
template<typename TEnumerator, typename ...T> inline void EnumerateWithHelper(TEnumerator & enumerator, T &...v) 
{ 
    int x[] = { (EnumerateWith(enumerator, v), 1)... }; 
}

// Generic EnumerateWith
template<typename TEnumerator, typename T>
auto EnumerateWith(TEnumerator & enumerator, T & val) -> std::void_t<decltype(val.EnumerateWith(enumerator))>
{
    val.EnumerateWith(enumerator);
}

这 15 行代码不需要任何第 3 方库;)

使用 C++20,你得到了 扩展语句, ,它允许您迭代聚合类型:

struct my_type {
    double data;
    std::string another_data;
    int last_data;
};

auto object = my_type{};

for...(auto& member : object) {
    using member_type = std::remove_cvref_t<decltype(member)>;
    member = get_data<member_type>();
}

如果您正在寻找相对简单的 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 定义使用类名+字段名 offsetof - 识别特定字段位于内存中的哪个位置。我尝试尽可能地学习 .NET 术语,但 C++ 和 C# 是不同的,所以它不是 1 对 1 的。整个 C++ 反射模型位于 TypeInfoFieldInfo 类。

我已经使用 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 类和部分模板规范启用任何第 3 方类/结构支持 - 以与 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.

但由于主题非常高级 - 我建议先查看视频。

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

Root Reflex 项目对此提供了支持。

https://root.cern.ch/how/how-use-reflex

如果您声明一个指向函数的指针,如下所示:

int (*func)(int a, int b);

您可以像这样在内存中为该函数分配一个位置(需要 libdldlopen)

#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);
}

要使用间接加载本地符号,您可以使用 dlopen 在调用二进制文件上(argv[0]).

对此的唯一要求(除了 dlopen(), libdl, , 和 dlfcn.h) 就是知道函数的参数和类型。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top