C/C++ 人们自行构建高性能反射框架,应该怎么设计比较好。

在 C/C++ 之中默认提供的 “RTTI” 运行时类型信息反射机制过于脆弱,基本就等于没什么卵用,但在 C/C++ 11 之前模板元编程不是那么强大的时候,还是有那么一点点作用。

我们通常利用RTTI反射T的类型或其泛型类型(即T的T)(字符串,缩写形式)根据类型的判断来调用对应的特例实现,但是用的不多,在C/C++ 11及以上版本之中,实战的用处基本就等于没有了。
 

但是我们希望实现一个没那么强大的类型反射系统,可以获取、设置属性、调用方法的话,其实也是有一些办法的。

本文简单的例子,不代表需要去构造完整的框架链,只是让大家知道,C/C++ 应该怎么做反射框架,就像有些博主教 C/C++ 怎么做GC自动垃圾回收系统是一样的。

C/C++ 什么都可以办到,这么强大的中高级语言编程工具,不能只是写点 Simple 无涵养的业务逻辑代码,喜欢做搬砖的杂活,Java、Go 语言会是很好的一个选择。

本文将实现一个简单反射读取、修改结构体的成员字段的示例程式:

玩家们可以不选择我们这种实现形式,方法有很多,比如:nlohmann / json 实现RTTI反射框架的形式,也挺有一些意思的。

调用本文做的反射读写字段小例子:

int main(int argc, const char* argv[]) noexcept
{
    Foo foo;
    foo.x = 200;
    foo.y = 100;

    std::unordered_map<std::string, int>& fields = Foo::__RTTI__::c().__rtti_field;
    RTTI_SetField(fields, &foo, "x", 300);
    RTTI_SetField(fields, &foo, "y", 400);

    std::cout << "x: " << RTTI_GetField(fields, &foo, "x", 0) << " y: " << RTTI_GetField(fields, &foo, "y", 0) << std::endl;
}

实现 C/C++ 结构类成员字段反射:

#define RTTI_FILED(var) __rtti_field[#var] = offset_of(_Ty, var);
#define RTTI_S(T)                                       \
struct __RTTI__ {                                       \
    typedef T                                      _Ty; \
                                                        \
    std::unordered_map<std::string, int> __rtti_field;  \
                                                        \
    static __RTTI__& c() {                              \
        static __RTTI__ r;                              \
        return r;                                       \
    }                                                   \
                                                        \
    __RTTI__() {                                        

#define RTTI_E                                          \
    }                                                   \
};

struct Foo {
    int x;
    int y;

    RTTI_S(Foo);
    RTTI_FILED(x);
    RTTI_FILED(y);
    RTTI_E;
};

template <typename T, typename TValue>
bool RTTI_SetField(std::unordered_map<std::string, int>& fields, T* obj, const std::string& method, TValue value) {
    auto tail = fields.find(method);
    auto endl = fields.end();
    if (tail == endl) {
        return false;
    }

    *(TValue*)((char*)obj + tail->second) = value;
    return true;
}

template <typename T, typename TValue>
TValue RTTI_GetField(std::unordered_map<std::string, int>& fields, T* obj, const std::string& method, TValue deafult_value) {
    auto tail = fields.find(method);
    auto endl = fields.end();
    if (tail == endl) {
        return deafult_value;
    }

    return *(TValue*)((char*)obj + tail->second);
}

宏函数:(依赖)

查看本人该篇博文;

C/C++ 11 offsetof、container_of 宏函数的实现(无限制编译器)_c++ 11 container_of-CSDN博客

我们可以利用宏编程,在每个侵入的结构体类之中构造其成员(字段、函数)的反射信息,这个 C/C++ 框架做起来并不困难。

或者把存储结构体元数据的信息放在全局存储也可以,通过模板泛型类型T来捕获元数据信息就可以。

对于字段类设置值的话,有两种办法:

1、向本文这样计算字段在内存之中的偏移量

2、利用宏编程来完成 get、set 属性访问器的实现

函数调用这个也不困难的,

C++ 11 提供的可变参数模板,自己调用转发就好了,就是需要注意:返回值为 void 类型还是有值类型的。

最简单的做法是分一个 Call_Void 函数出来,一个 Call 带返回值的函数出来,当然 C/C++ 相当专业的做法显然是模板元编程咯,在 C/C++ 17 上面更容易处理,在 C/C++ 11 ~ 14 上面就需要注意下这些小问题。

都差不多,效率都挺OK,C/C++ 之中自己设计并且构造RTTI系统,只要不是SX玩意,乱整一通,基本上RTTI反射执行效率都是快到飞起滴。

当然有一些想当秀技术的也行,我之前看过一个开源项目,那个只有一个宏,把类型、字段、成员都在这个可变参数宏之中传递进去,然后大量通过 C/C++ 编译器模板元编程来处理。

项目的作者 C/C++ 水平相当秀儿,但大家在自己做 RTTI C/C++ 反射框架的时候大可不必这么秀技术水平,因为模板元编程虽好,但是过于多会导致编译器占用极大的设备内存,编译速度慢到飞起,能一定稳定编译过算运气好。

编译器内存OOM导致崩溃的问题,相信不少搞 C/C++ 的童靴应该都有遇到过,当模板过于复杂的时候,编译器编译一个CPP展开代码过多就有可能导致编译直接OOM炸掉故而编译失败。

  • 11
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值