C++编译期反射——以AOP为例
这篇文章以 AOP 为落点,展示了如何用 C++ 模板元编程实现“编译期反射式能力探测 + 切面调用编排”。编译期能力检测基于 SFINAE/declval/is_same 判断类型是否含有指定成员方法(如 before/after)。通过宏抽象 HAS_MEMBER,把方法存在性检测变为可复用组件。在 C++17 下…
编译期反射实践 自古以来,C++就一直缺少一个编程语言的重要特性——反射,但如果熟悉C++元模板编程的同学,就知道以C++的风格,肯定是不会在标准库中添加运行时的反射支持的,从最新的C++版本演进来看,倒是编译期反射可能得到更好的支持。C++11 -> C++14 -> C++17 -> C++20... 不断让元模板编程变得更简单,更规范。 本次的编译期反射实践,代码要求的最低C++版本为14,因为用到了 make_shared、decay_t。 本次实践的完整代码仓库:MyUtil/tree/master/aop获取类的方法判断类是否具有某方法 我们如何判断某个类是否具有某个方法呢? 要想在编译期间实现这样一个判断,我们的思路可以是这样:写两个模板,如果这个类型具有这个方法,就匹配到返回 std::true_type() 的模板,如果不具备则匹配到返回 std::false_type() 的模板,最后通过 std::is_same 能够判断匹配结果,也就是实现了在编译期间判断类是否有这个方法。 上述过程,利用 SFINAE 的原理可以轻松实现,如果不了解 SFINAE 以及对应的 enable_if 的运用,可以看看这篇文章:C++模板进阶指南:SFINAE。 我们现在就开始动手实现上述代码,假设我们需要判断一个类型是否有 before() 方法。template <typename T, typename... Args> struct has_member_before { private: template <typename U> static auto Check(int) -> decltype( std::declval<U>().before(std::declval<Args>()...),std::true_type() //1 ); template <typename U> static std::false_type Check(...); //2 public: enum { value = std::is_same<decltype(Check<T>(0)), std::true_type>::value //3 }; }; 先讲下上述代码定义后如何使用吧,比如现在有个 Student 类型,我们来判断是否具有 before 成员函数,则只需要写下下面的代码:has_member_before<Student,int>::value //判断Student类是否有Student::before(int)方法 上面的代码重点有三段,已经作为标记1、2、3: 代码1处,利用了 std::declval 在编译期创建类型U的实例,并调用其 before 方法,这是在元模板中判断一个类型是否具有某个方法的常有手段,因为 SFINAE 的存在,即便该处替换出错,编译器会去继续寻找下一个替换是否能够正确,直到所有的替换都出错。 很明显这里是一定会替换成功的,因为代码2有一个包容性很强的重载,这个重载的参数不能和代码1处的重载参数一致,否则会算作重复定义,当然如果你使用 std::enable_if 对参数一致的模板参数进行唯一性的限制,那么重复定义的错误也可以避免。但是写成 C 的可变参数是最快的解决方式。 代码1处,有个逗号表达式的细节,如果成功被代码1处替换,那么返回值类型将会是 decltype() 中的表达式类型,也就是逗号表达式最后的结果 std::true_type。 代码3是利用enum类型在编译期得到具体的常量值。具体是通过调用 Check<T>(0) 获取该函数的返回值类型,这期间模板的匹配替换就会牵扯到前面的代码1、2。所以一旦模板被实例化,那么该class是否具有该方法的信息也就清楚了。 最后我们可以把该段代码提取为宏作为通用代码:#define HAS_MEMBER(member) \ template <typename T, typename... Args> struct has_member_##member { \ private: \ template <typename U> \ static auto Check(int) \ -> decltype(std::declval<U>().member(std::declval<Args>()...), \ std::true_type()); \ template <typename U> static std::false_type Check(...); \ \ public: \ enum { \ value = std::is_same<decltype(Check<T>(0)), std::true_typ…
正在初始化 WebAssembly 引擎…
首次编译原生模块可能需要数秒
就绪后,页面交互将以接近原生的速度运行