【C++ 仿 MFC 反射系统】

C++ 仿 MFC 反射系统实现:支持动态创建与成员函数调用

一、背景

在 C++ 开发中,反射(Reflection)是一项强大但语言本身不支持的特性。许多框架(如 MFC、Qt)通过各自的宏和运行时类型信息(RTTI)机制实现了类似反射的功能。本文参考 MFC 的 CRuntimeClass 设计思想,实现一套轻量级、可复用的反射系统,具备以下能力:

  • 通过类名字符串动态创建对象。
  • 运行时类型判断(IsKindOf)。
  • 支持带任意参数和返回值的成员函数动态调用 (使用 std::any 类型擦除)。
  • 宏自动化注册,无需手动编写重复代码。

完整代码基于标准 C++17,不依赖 Qt 等大型框架,可直接集成到现有项目中。

二、设计思路

2.1 运行时类信息(CRuntimeClass)

每个需要反射的类都对应一个静态 CRuntimeClass 对象,存储:

  • 类名(m_lpszClassName
  • 基类运行时信息指针(m_pBaseClass
  • 创建对象的函数指针(m_pfnCreateObject
  • 方法调用表(m_methods

通过 CRuntimeClass 可以创建对象、判断继承关系、动态调用方法。

2.2 反射工厂(CRttiFactory)

单例模式管理所有已注册类的 CRuntimeClass 指针。提供:

  • RegisterClass:注册类信息(由宏自动调用)。
  • CreateObject:根据类名查找并创建对象。
  • GetRegisteredClassNames:获取所有类名(调试用)。

2.3 成员函数反射(MethodInvoker)

使用 std::any 统一处理任意类型的参数和返回值。每个成员函数被包装成一个 MethodInvoker 类型的可调用对象:

cpp 复制代码
std::any(CRttiObject*, const std::vector<std::any>&)

内部通过可变参数模板和 std::index_sequence 解析参数列表并调用真实函数。

2.4 宏自动化

借鉴 MFC 的 DECLARE_DYNAMIC / IMPLEMENT_DYNAMIC 宏,自动生成静态 CRuntimeClass 对象和注册代码。成员函数反射也通过宏声明和注册。

三、核心代码解析

3.1 类与结构定义

cpp 复制代码
// 创建对象函数类型
using ObjectConstructor = std::function<CRttiObject*()>;

// 方法调用器类型
using MethodInvoker = std::function<std::any(CRttiObject*, const std::vector<std::any>&)>;

// 方法元信息
class CMethodInfo {
public:
    const char* name;
    MethodInvoker invoker;
    CMethodInfo(const char* n, MethodInvoker i) : name(n), invoker(std::move(i)) {}
};

// 运行时类信息
class CRuntimeClass {
public:
    const char* m_lpszClassName;
    CRuntimeClass* m_pBaseClass;
    ObjectConstructor m_pfnCreateObject;

    CRuntimeClass(const char* className, CRuntimeClass* baseClass, ObjectConstructor ctor);

    CRttiObject* CreateObject() const;
    bool IsDerivedFrom(const CRuntimeClass* pBaseClass) const;

    // 方法管理
    void RegisterMethod(const char* name, MethodInvoker invoker);
    std::any InvokeMethod(CRttiObject* obj, const char* name, const std::vector<std::any>& args = {}) const;
    bool HasMethod(const char* name) const;

private:
    std::unordered_map<std::string, CMethodInfo> m_methods;
};

3.2 反射基类与工厂

cpp 复制代码
// 所有反射类的基类
class CRttiObject {
public:
    virtual ~CRttiObject() = default;
    virtual const CRuntimeClass* GetRuntimeClass() const = 0;
    bool IsKindOf(const CRuntimeClass* pClass) const;
    static CRttiObject* CreateObject(const char* className);
};

// 单例工厂(省略部分代码,详见文末源码)
class CRttiFactory {
    // 线程安全注册与查找
    std::unordered_map<std::string, CRuntimeClass*> m_classMap;
    std::vector<CRuntimeClass*> m_vecRuntimeClasses;
    mutable std::mutex m_mutex;
    // ...
};

3.3 成员函数调用适配器

使用模板将任意成员函数转换为统一签名:

cpp 复制代码
namespace ReflectDetail {
    // 实际调用包装,通过 std::any_cast 提取参数
    template<typename Class, typename Ret, typename... Args, std::size_t... I>
    std::any InvokeImpl(Class* obj, Ret(Class::* func)(Args...), 
                        const std::vector<std::any>& args, std::index_sequence<I...>) {
        if (args.size() != sizeof...(Args))
            throw std::runtime_error("Argument count mismatch");
        if constexpr (std::is_void_v<Ret>) {
            (obj->*func)(std::any_cast<std::decay_t<Args>>(args[I])...);
            return std::any();
        } else {
            return std::any((obj->*func)(std::any_cast<std::decay_t<Args>>(args[I])...));
        }
    }

    // 生成 MethodInvoker
    template<typename Class, typename Ret, typename... Args>
    MethodInvoker MakeInvoker(Ret(Class::* func)(Args...)) {
        return [func](CRttiObject* base, const std::vector<std::any>& args) -> std::any {
            Class* obj = dynamic_cast<Class*>(base);
            if (!obj) throw std::runtime_error("Invalid object type");
            return InvokeImpl(obj, func, args, std::index_sequence_for<Args...>{});
        };
    }
    // 同样提供 const 成员函数版本(略)
}

3.4 宏定义

类反射宏

cpp 复制代码
#define DECLARE_DYNAMIC(className) \
public: \
    static CRuntimeClass class##className; \
    virtual const CRuntimeClass* GetRuntimeClass() const override { return &class##className; } \
    static CRttiObject* CreateObject() { return new className(); }

#define IMPLEMENT_ROOT_DYNAMIC(className) \
    CRuntimeClass className::class##className(#className, nullptr, &className::CreateObject); \
    static struct __Register_##className { \
        __Register_##className() { CRttiFactory::Instance().RegisterClass(&className::class##className); } \
    } __register_##className;

成员函数反射宏(支持0~4个参数)

cpp 复制代码
#define DECLARE_REFLECT_METHOD(className, method) \
    static void _RegisterMethod_##method();

#define IMPLEMENT_REFLECT_METHOD(className, method, ...) \
    /* 通过参数个数选择具体宏,调用 MakeInvoker 并注册 */

宏自动展开后,会在 main 前执行注册代码,确保类和方法在运行时可用。

四、使用示例

4.1 定义反射类

TestReflect.h

cpp 复制代码
#pragma once
#include "ReflectClass.h"

class CTestReflect : public CRttiObject
{
    DECLARE_DYNAMIC(CTestReflect)
    DECLARE_REFLECT_METHOD(CTestReflect, SetValue)
    DECLARE_REFLECT_METHOD(CTestReflect, GetValue)

public:
    CTestReflect() : m_nValue(2) {}
    virtual ~CTestReflect() {}

    void SetValue(int nValue, float fValue) { m_nValue = nValue; m_fValue = fValue; }
    int GetValue() { return m_nValue; }

private:
    int m_nValue;
    float m_fValue;
};

4.2 实现注册

TestReflect.cpp

cpp 复制代码
#include "TestReflect.h"

IMPLEMENT_ROOT_DYNAMIC(CTestReflect)
IMPLEMENT_REFLECT_METHOD(CTestReflect, SetValue, int, float)
IMPLEMENT_REFLECT_METHOD(CTestReflect, GetValue)

4.3 动态调用

Main.cpp

cpp 复制代码
#include "TestReflect.h"
#include <iostream>

int main()
{
    CRttiObject* obj = CRttiObject::CreateObject("CTestReflect");
    if (obj) {
        const CRuntimeClass* cls = obj->GetRuntimeClass();

        // 带参数调用
        std::vector<std::any> args = { 10, 2.5f };
        cls->InvokeMethod(obj, "SetValue", args);

        // 带返回值调用
        std::any ret = cls->InvokeMethod(obj, "GetValue", {});
        if (ret.has_value()) {
            int val = std::any_cast<int>(ret);
            std::cout << "GetValue: " << val << std::endl; // 输出 10
        }
        delete obj;
    }
    return 0;
}

五、关键技术细节与注意事项

5.1 静态初始化顺序

  • 使用局部静态单例(CRttiFactory::Instance())确保工厂在首次访问时构造。
  • 宏生成的注册类在 main 前执行,但调用 RegisterClass 时工厂已构造,顺序安全。

5.2 类型严格匹配

std::any_cast 要求类型完全一致 ,包括 cv 限定符和引用。示例中 2.5f 显式指定为 float,若误写 2.5double)将抛出 std::bad_any_cast

5.3 函数重载处理

若存在同名重载函数,需通过 static_cast 指定确切签名,宏已包含此转换。

5.4 线程安全

工厂的 RegisterClass 和查询方法均加锁,可在多线程环境下安全使用。

5.5 扩展性

  • 支持更多参数:参照现有宏模式增加 IMPLEMENT_REFLECT_METHOD_5 等即可。
  • 支持非 void 返回值:已有完整支持,无需额外处理。

六、完整代码获取

完整头文件 ReflectClass.h 可进q裙提供。项目结构如下:

复制代码
├── ReflectClass.h       # 反射核心实现
├── TestReflect.h/cpp    # 示例反射类
└── Main.cpp             # 测试入口

七、总结

本文实现了一套仿 MFC 的 C++ 反射系统,具备以下亮点:

  • 轻量级:仅依赖 C++17 标准库,无第三方依赖。
  • 功能全面:支持动态创建、继承判断、成员函数调用。
  • 类型安全 :利用 std::any 和模板推导,编译期保证类型正确。
  • 易用性:宏自动化注册,对业务代码侵入极小。

这套反射机制可应用于插件系统、序列化/反序列化、脚本绑定、RPC 调用等场景,为 C++ 项目带来动态语言的灵活性。

相关推荐
fqbqrr10 小时前
2606C++,C++构的多态
开发语言·c++
biter down10 小时前
从 0 到 1 搭建 Python 接口自动化测试框架(博客系统实战)
开发语言·python
小欣加油10 小时前
leetcode56 合并区间
c++·算法·leetcode·职场和发展
Yolo_TvT11 小时前
C++:析构函数
c++
threelab12 小时前
Three.js 物理模拟着色器 | 三维可视化 / AI 提示词
开发语言·前端·javascript·人工智能·3d·着色器
武器大师7212 小时前
lv_binding_js 代码解读
开发语言·javascript·ecmascript
不知名的老吴12 小时前
线程的生命周期之线程“插队“
java·开发语言·python
Hello:CodeWorld13 小时前
C 风格变参 vs C++ 变参模板:核心区别与选型指南
c语言·c++·算法
kaikaile199513 小时前
数字全息图处理系统(C# 实现)
开发语言·c#