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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/41453/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 12:25:17  来源:igfitidea点击:

How can I add reflection to a C++ application?

c++reflectiontemplatessfinae

提问by Nick

I'd like to be able to introspect a C++ class for its name, contents (i.e. members and their types) etc. I'm talking native C++ here, not managed C++, which has reflection. I realise C++ supplies some limited information using RTTI. Which additional libraries (or other techniques) could supply this information?

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

采纳答案by Nick

Ponderis a C++ reflection library, in answer to this question. I considered the options and decided to make my own since I couldn't find one that ticked all my boxes.

Ponder是一个 C++ 反射库,用于回答这个问题。我考虑了这些选项并决定自己做一个,因为我找不到一个能满足我所有要求的选项。

Although there are great answers to this question, I don't want to use tonnes of macros, or rely on Boost. Boost is a great library, but there are lots of small bespoke C++0x projects out that are simpler and have faster compile times. There are also advantages to being able to decorate a class externally, like wrapping a C++ library that doesn't (yet?) support C++11. It is fork of CAMP, using C++11, that no longer requires Boost.

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

回答by Paul Fultz II

What you need to do is have the preprocessor generate reflection data about the fields. This data can be stored as nested classes.

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

First, to make it easier and cleaner to write it in the preprocessor we will use typed expression. A typed expression is just an expression that puts the type in parenthesis. So instead of writing int xyou will write (int) x. Here are some handy macros to help with typed expressions:

首先,为了使在预处理器中编写它更容易和更清晰,我们将使用类型化表达式。类型化表达式只是将类型放在括号中的表达式。所以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

Next, we define a REFLECTABLEmacro to generate the data about each field(plus the field itself). This macro will be called like this:

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

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

So using Boost.PPwe iterate over each argument and generate the data like this:

因此,使用Boost.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)); \
    } \
}; \

What this does is generate a constant fields_nthat is number of reflectable fields in the class. Then it specializes the field_datafor each field. It also friends the reflectorclass, this is so it can access the fields even when they are private:

这样做是生成一个常量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;
    };
};

Now to iterate over the fields we use the visitor pattern. We create an MPL range from 0 to the number of fields, and access the field data at that index. Then it passes the field data on to the user-provided visitor:

现在要遍历字段,我们使用访问者模式。我们创建一个从 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));
}

Now for the moment of truth we put it all together. Here is how we can define a Personclass that is reflectable:

现在到了关键时刻,我们把它们放在一起。下面是我们如何定义一个Person可反射的类:

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

Here is a generalized print_fieldsfunction using the reflection data to iterate over the fields:

这是一个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());
}

An example of using the print_fieldswith the reflectable Personclass:

print_fields与反射Person类一起使用的示例:

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

Which outputs:

哪些输出:

name=Tom
age=82

And voila, we have just implemented reflection in C++, in under 100 lines of code.

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

回答by Johannes Schaub - litb

There are two kinds of reflectionswimming around.

reflection游泳有两种。

  1. Inspection by iterating over members of a type, enumerating its methods and so on.

    This is not possible with C++.
  2. 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_traitsfor many things (like checking whether a type is integral). For checking for the existance of a member function, use Is it possible to write a template to check for a function's existence?. For checking whether a certain nested type exists, use plain SFINAE.
  1. 通过迭代类型的成员、枚举其方法等进行检查。

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

    使用 C++ 可以实现这种事情template-tricks。使用boost::type_traits了很多东西(如检查类型是否为整数)。要检查成员函数是否存在,请使用是否可以编写模板来检查函数是否存在?. 要检查某个嵌套类型是否存在,请使用普通SFINAE

If you are rather looking for ways to accomplish 1), like looking how many methods a class has, or like getting the string representation of a class id, then i'm afraid there is no Standard C++ way of doing this. You have to use either

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

  • A Meta Compiler like the Qt Meta Object Compiler which translates your code adding additional meta informations.
  • A Framework constisting of macros that allow you to add the required meta-informations. You would need to tell the framework all methods, the class-names, base-classes and everything it needs.
  • 像 Qt Meta Object Compiler 这样的 Meta Compiler,它可以翻译您的代码并添加额外的元信息。
  • 由宏组成的框架,允许您添加所需的元信息。你需要告诉框架所有的方法、类名、基类和它需要的一切。

C++ is made with speed in mind. If you want high-level inspection, like C# or Java has, then I'm afraid i have to tell you there is no way without some effort.

C++ 的设计考虑到了速度。如果你想要高层次的检查,像C#或Java那样,那么恐怕我不得不告诉你,没有一些努力是没有办法的。

回答by Brad Wilson

And I would love a pony, but ponies aren't free. :-p

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

http://en.wikibooks.org/wiki/C%2B%2B_Programming/RTTIis what you're going to get. Reflection like you're thinking about -- fully descriptive metadata available at runtime -- just doesn't exist for C++ by default.

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

回答by Roderick

The information does exist - but not in the format you need, and only if you export your classes. This works in Windows, I don't know about other platforms. Using the storage-class specifiers as in, for example:

信息确实存在 - 但不是您需要的格式,并且仅当您导出类时。这适用于Windows,我不知道其他平台。使用存储类说明符,例如:

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

This makes the compiler build the class definition data into the DLL/Exe. But it's not in a format that you can readily use for reflection.

这使得编译器将类定义数据构建到 DLL/Exe 中。但它不是您可以轻松用于反射的格式。

At my company we built a library that interprets this metadata, and allows you to reflect a class without inserting extra macros etc. into the class itself. It allows functions to be called as follows:

在我的公司,我们构建了一个解释此元数据的库,并允许您反映一个类,而无需在类本身中插入额外的宏等。它允许按如下方式调用函数:

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

This effectively does:

这有效地做到了:

instance_ptr->Foo(1.331);

The Invoke(this_pointer,...) function has variable arguments. Obviously by calling a function in this way you're circumventing things like const-safety and so on, so these aspects are implemented as runtime checks.

Invoke(this_pointer,...) 函数具有可变参数。显然,通过以这种方式调用函数,您可以绕过诸如 const-safety 之类的东西,因此这些方面被实现为运行时检查。

I'm sure the syntax could be improved, and it only works on Win32 and Win64 so far. We've found it really useful for having automatic GUI interfaces to classes, creating properties in C++, streaming to and from XML and so on, and there's no need to derive from a specific base class. If there's enough demand maybe we could knock it into shape for release.

我确信语法可以改进,目前它只适用于 Win32 和 Win64。我们发现它对于类的自动 GUI 接口、在 C++ 中创建属性、与 XML 之间的流传输等非常有用,并且不需要从特定的基类派生。如果有足够的需求,也许我们可以将其成型以进行发布。

回答by Damian Dixon

Reflection is not supported by C++ out of the box. This is sad because it makes defensive testing a pain.

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

There are several approaches to doing reflection:

有几种方法可以进行反射:

  1. use the debug information (non portable).
  2. Sprinkle your code with macro's/templates or some other source approach (looks ugly)
  3. Modify a compiler such as clang/gcc to produce a database.
  4. Use Qt moc approach
  5. Boost Reflect
  6. Precise and Flat Reflection
  1. 使用调试信息(不可移植)。
  2. 使用宏/模板或其他一些源代码方法(看起来很难看)散布您的代码
  3. 修改clang/gcc等编译器来生成数据库。
  4. 使用 Qt moc 方法
  5. 增强反射
  6. 精确而平坦的反射

The first link looks the most promising (uses mod's to clang), the second discusses a number of techniques, the third is a different approach using gcc:

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

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

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

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

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

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

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

There is now a working group for C++ reflection. See the news for C++14 @ CERN:

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

Edit 13/08/17:

编辑 13/08/17:

Since the original post there have been a number of potential advancements on the reflection. The following provides more detail and a discussion on the various techniques and status:

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

  1. Static Reflection in a Nutshell
  2. Static Reflection
  3. A design for static reflection
  1. 简而言之静态反射
  2. 静态反射
  3. 静态反射设计

However it does not look promising on a standardised reflections approach in C++ in the near future unless there is a lot more interest from the community in support for reflection in C++.

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

The following details the current status based on feedback from the last C++ standards meeting:

下面根据上次 C++ 标准会议的反馈详细说明当前状态:

Edit 13/12/2017

编辑 13/12/2017

Reflection looks to be moving towards C++ 20 or more probably a TSR. Movement is however slow.

反射看起来正在朝着 C++ 20 或更可能是 TSR 的方向发展。然而移动缓慢。

Edit 15/09/2018

编辑 15/09/2018

A draft TS has been sent out to the national bodies for ballot.

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

The text can be found here: https://github.com/cplusplus/reflection-ts

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

Edit 11/07/2019

编辑 11/07/2019

The reflection TS is feature complete and is out for comment and vote over the summer (2019).

反射 TS 功能完整,将于夏季(2019 年)进行评论和投票。

The meta-template programing approach is to be replaced with a simplier compile time code approach (not reflected in the TS).

元模板编程方法将替换为更简单的编译时代码方法(未反映在 TS 中)。

Edit 10/02/2020

编辑 10/02/2020

There is a request to support the reflection TS in Visual Studio here:

这里有一个在 Visual Studio 中支持反射 TS 的请求:

Talk on the TS by the author David Sankel:

关于作者 David Sankel 的 TS 的讨论:

Edit 17 March 2020

2020 年 3 月 17 日编辑

Progress on reflection is being made. A report from '2020-02 Prague ISO C++ Committee Trip Report' can be found here:

反思正在取得进展。可以在此处找到“2020-02 布拉格 ISO C++ 委员会旅行报告”的报告:

Details on what is being considered for C++23 can be found here (includes short section on Reflection):

可以在此处找到有关 C++23 正在考虑的内容的详细信息(包括关于反射的简短部分):

回答by KeithB

You need to look at what you are trying to do, and if RTTI will satisfy your requirements. I've implemented my own pseudo-reflection for some very specific purposes. For example, I once wanted to be able to flexibly configure what a simulation would output. It required adding some boilerplate code to the classes that would be output:

您需要查看您正在尝试做什么,以及 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;
}

The first call adds this object to the filtering system, which calls the BuildMap()method to figure out what methods are available.

第一次调用将此对象添加到过滤系统,过滤系统调用该BuildMap()方法以确定哪些方法可用。

Then, in the config file, you can do something like this:

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

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

Through some template magic involving boost, this gets translated into a series of method calls at run-time (when the config file is read), so it's fairly efficient. I wouldn't recommend doing this unless you really need to, but, when you do, you can do some really cool stuff.

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

回答by Ferruccio

What are you trying to do with reflection?
You can use the Boost type traitsand typeoflibraries as a limited form of compile-time reflection. That is, you can inspect and modify the basic properties of a type passed to a template.

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

回答by philant

EDIT: CAMPis no more maintained ; two forks are available:

编辑CAMP不再维护;有两个叉子可用:

  • One is also called CAMPtoo, and is based on the same API.
  • Ponderis a partial rewrite, and shall be preferred as it does not requires Boost ; it's using C++11.
  • 一种也称为CAMP,基于相同的 API。
  • Ponder是部分重写,应该是首选,因为它不需要 Boost ;它使用 C++11。


CAMPis an MIT licensed library (formerly LGPL) that adds reflection to the C++ language. It doesn't require a specific preprocessing step in the compilation, but the binding has to be made manually.

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

The current Tegesoft library uses Boost, but there is also a forkusing C++11 that no longer requires Boost.

当前的 Tegesoft 库使用 Boost,但也有一个使用 C++11的分支不再需要 Boost

回答by Jér?me

I would recommend using Qt.

我建议使用Qt

There is an open-source licence as well as a commercial licence.

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

回答by Michel

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.

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

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.

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

Maybe more interesting to you is the fact that you can also dump all methods and members defined for a class, and in string form no less.

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

Downsides to the system that might discourage you from bothering: adding all of the messages and key-values is extremely tedious; it's slower than without any reflection; you'll grow to hate seeing boost::static_pointer_castand boost::dynamic_pointer_castall over your codebase with a violent passion; the limitations of the strongly-typed system are still there, you're really just hiding them a bit so it isn't as obvious. Typos in your strings are also not a fun or easy to discover surprise.

系统的缺点可能会阻止您打扰:添加所有消息和键值非常乏味;它比没有任何反射要慢;你会成长讨厌看到boost::static_pointer_castboost::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.

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