xcmixin 是现代 C++ 的静态混入(Mixin)解决方案,通过 CRTP 模式在编译期将多个方法混入类中,无需修改类的原始定义。
# 配置并构建
cmake -B build -G Ninja
cmake --build build
# 运行示例
build/examples/oop_example#include <iostream>
#include <string>
#include "xcmixin/xcmixin.hpp"
class MyClass;
XCMIXIN_IMPL_AVAILABLE(MyClass);
// 1. 定义混入方法
XCMIXIN_DEF_BEGIN(name_method)
std::string name() { return "Unknown"; }
XCMIXIN_DEF_END()
XCMIXIN_DEF_BEGIN(print_method)
void print() { std::cout << xcmixin_self.name() << std::endl; }
XCMIXIN_DEF_END()
// 2. 实现混入(可选:为特定类定制)
XCMIXIN_IMPL_BEGIN(name_method)
XCMIXIN_IMPL_FOR(MyClass)
std::string name() { return "MyClass"; }
XCMIXIN_IMPL_END()
// 3. 组合混入
using recorder = xcmixin::mixin_recorder<print_method, name_method>;
// 4. 应用到类
class MyClass : public xcmixin::impl_recorder<MyClass, recorder> {
xcmixin_init_class;
};
int main() {
MyClass obj;
obj.print(); // 输出: MyClass
obj.name();
return 0;
}通过 mixin_recorder 灵活组合多个混入:
using recorder = xcmixin::mixin_recorder<print_method, new_name_method,
dosomethings1_method>;将组合后的混入注入到目标类:
class MyClass : public xcmixin::impl_recorder<MyClass, recorder> {
xcmixin_init_class; // 须在类定义末尾调用以进行编译期验证
};与普通成员函数调用方式相同:
MyClass obj;
obj.print();
obj.name();
obj.dosomethings1();使用 Impl 概念对模板类型进行约束,作为传统基类引用的替代和增强:
template <xcmixin::Impl<print_method, name_method> T>
void print(T& p) {
p.print();
std::cout << "class_name: " << p.name() << std::endl;
}
int main() {
MyClass obj;
print(obj);
return 0;
}与传统的基类引用相比,Impl 概念不需要实际的继承关系,只需要派生类包含指定的混入注入,提供更灵活的约束方式。
- 编译期完成:所有验证均在编译期完成,无运行时开销
- 单继承链:生成单继承结构,无多重继承或 vtable 开销
- EBO 优化:无数据成员的混入使用空基类优化,保持标准布局
编译期验证确保混入的有效性与正确性:
类前置声明
class MyClass;
XCMIXIN_IMPL_AVAILABLE(MyClass); // 所有注入行为须在类定义之前完成
// ...
class MyClass { /* ... */ };依赖约束
XCMIXIN_REQUIRE(print_method,
xcmixin_require_mixin(name_method););方法签名验证
XCMIXIN_REQUIRE(print_method,
xcmixin_require_method(print, void); // 验证 const/volatile/const volatile 重载
);隐藏检测
XCMIXIN_REQUIRE(name_method,
xcmixin_no_hiding(name, long, int); // 验证 name(int, long) 重载不被隐藏
xcmixin_no_hiding(name, int, const_););定义可复用的方法集合:
XCMIXIN_DEF_BEGIN(new_name_method)
std::string name() { return "NewName"; }
std::string name() const { return "NewName"; }
XCMIXIN_DEF_END()使用 xcmixin_self / xcmixin_const_self 在任意混入中访问最终派生类的所有方法:
XCMIXIN_DEF_BEGIN(print_method)
void print() {
std::cout << xcmixin_self.name() << std::endl;
std::cout << xcmixin_self.name(11) << std::endl;
std::cout << xcmixin_const_self.name(11) << std::endl;
}
XCMIXIN_DEF_END()为特定派生类提供定制化的方法实现:
XCMIXIN_IMPL_BEGIN(name_method)
XCMIXIN_IMPL_FOR(MyClass)
std::string name() { return "MyClass"; }
std::string name(int i) { return "MyClass " + std::to_string(i); }
std::string name(int i) const { return "const MyClass " + std::to_string(i); }
std::string name(long i) const { return "const MyClass " + std::to_string(i); }
XCMIXIN_IMPL_END()基于已有 Mixin 派生新的 Mixin:
XCMIXIN_DEF_BEGIN(name_method)
std::string name() { return "Unknown"; }
XCMIXIN_DEF_END()
XCMIXIN_DEF_EXTEND_BEGIN(new_name_method, name_method)
using base::name; // `base` 指向直接基类
std::string name() { return "NewName"; }
XCMIXIN_DEF_END()为特定派生类扩展已实现的 Mixin:
XCMIXIN_IMPL_BEGIN(new_name_method)
XCMIXIN_IMPL_EXTEND_FOR(name_method, MyClass)
using base::name;
std::string name() { return "NewName"; }
XCMIXIN_IMPL_END()xcmixin 同时支持模板类。使用 XCMIXIN_PRE_DECL 前置声明 Mixin,并使用 xcmixin_init_template 进行验证:
template <typename T>
class MyTemplate;
XCMIXIN_PRE_DECL(name_mixin)
XCMIXIN_REQUIRE(name_mixin, xcmixin_no_hiding(name);)
XCMIXIN_DEF_BEGIN(name_mixin)
std::string name() { return "Unknown"; }
XCMIXIN_DEF_END()
XCMIXIN_DEF_BEGIN(sayhello_mixin)
void say_hello() { std::cout << xcmixin_self.name() << " hello " << std::endl; }
XCMIXIN_DEF_END()
using recorder = xcmixin::mixin_recorder<name_mixin, sayhello_mixin>;
template <typename T>
class MyTemplate : public xcmixin::impl_recorder<MyTemplate<T>, recorder> {
xcmixin_init_template(xcmixin::impl_recorder<MyTemplate<T>, recorder>);
};XCMIXIN_IMPL_BEGIN 支持扩展参数以声明模板参数,可为不同模板特化提供定制实现:
// 通用模板实现
XCMIXIN_IMPL_BEGIN(name_mixin, typename T)
XCMIXIN_IMPL_FOR(MyTemplate<T>)
std::string name() { return "MyTemplate default"; }
XCMIXIN_IMPL_END()
// MyTemplate<int> 特化
XCMIXIN_IMPL_BEGIN(name_mixin)
XCMIXIN_IMPL_FOR(MyTemplate<int>)
std::string name() { return "MyTemplate<int>"; }
XCMIXIN_IMPL_END()
// MyTemplate<float> 特化
XCMIXIN_IMPL_BEGIN(name_mixin)
XCMIXIN_IMPL_FOR(MyTemplate<float>)
std::string name() { return "MyTemplate<float>"; }
XCMIXIN_IMPL_END()
int main() {
MyTemplate<double> obj1;
obj1.say_hello(); // 输出: MyTemplate default hello
MyTemplate<int> obj2;
obj2.say_hello(); // 输出: MyTemplate<int> hello
MyTemplate<float> obj3;
obj3.say_hello(); // 输出: MyTemplate<float> hello
}- 高性能数学库:为通用计算提供默认实现,为特定场景提供定制优化
- 策略模式:编译期决定策略行为,消除运行时分派开销
- 状态模式:编译期状态管理,无运行时状态对象开销
- 接口组合:按需组合不同功能模块
| 编译器 | 支持情况 |
|---|---|
| clang / clang-cl | 完整支持 |
| gcc | 完整支持 |
| MSVC | 部分支持 |
MSVC 编译器在处理扩展混入宏时存在重载决议缺陷。当使用 XCMIXIN_DEF_EXTEND_BEGIN 或 XCMIXIN_IMPL_EXTEND_FOR 宏,并通过 using base::func 语法引入基类重载函数时,xcmixin_no_hiding 检测会在编译期产生误判。
此问题仅影响编译期的有效性验证,不会导致运行时错误。
| 项目 | 说明 |
|---|---|
| 受影响编译器 | MSVC |
| 受影响宏 | XCMIXIN_DEF_EXTEND_BEGIN / XCMIXIN_IMPL_EXTEND_FOR |
| 触发条件 | 派生类通过 using Base::func 引入基类同名重载函数 |
| 检测机制 | xcmixin_no_hiding 宏 |
| 影响范围 | 仅编译期类有效性验证阶段 |
| 运行时行为 | 正常 |
详细说明请参考 examples/overload-msvc-bug.cc。