CppCon 2024 学习:SuperCharge Your IPC Programs With C++20 and CCI Pattern
提供的内容讲的是 IPC(进程内通信)中图像通道提取的服务实现,并且使用 CCI(Contract-Concept-Implementation)模式组织软件。
1⃣ 术语解释
| 缩写 | 含义 |
|---|---|
| IPC | Intra-Process Communication,进程内通信,用于不同模块或线程间的数据交换 |
| Programs | 在受限环境下运行的软件(如嵌入式或实时系统) |
| CCI | Contract-Concept-Implementation 模式 |
| Contract | 接口约束,定义服务和功能 |
| Concept | 功能抽象,提供逻辑和行为说明 |
| Implementation | 具体实现,包括数据结构和算法 |
2⃣ 问题定义:Channel Extraction
- 图片在不同应用中广泛使用
- 我们希望提取图片的颜色通道,例如 R/G/B 通道
- 提供一个服务接口,让客户端通过通信端点(IPC)调用通道提取功能
思路:
- 输入:图像数据
- 输出:分离的通道数据
- 通信:使用 IPC(如 dbus)作为服务端和客户端的桥梁
公式化表示:
- 假设图像 III 大小为 H×W×3H \times W \times 3H×W×3(RGB三通道):
I=R,G,B,R,G,B∈RH×W I = { R, G, B }, \quad R,G,B \in \mathbb{R}^{H \times W} I=R,G,B,R,G,B∈RH×W - 通道提取函数:
f(I)=(R,G,B) f(I) = (R, G, B) f(I)=(R,G,B)
3⃣ 标准 IPC 实现(示例使用 sd-bus)
#include <systemd/sd-bus.h>
sd_bus_error error = SD_BUS_ERROR_NULL; // 初始化错误对象
sd_bus_message *msg = NULL; // 消息对象指针
sd_bus *bus = NULL; // IPC bus 对象
int32_t result; // 存储函数调用结果
const int r = 0; // 示例常量
注释:
sd_bus_error用于捕获 dbus 错误sd_bus_message表示 IPC 消息sd_bus表示连接到 dbus 的总线result用于返回函数调用状态r是常量或索引,用于操作图像通道
3.1 数据流逻辑
- 客户端调用 IPC 服务请求提取通道
- 服务端通过
sd_bus接收消息 - 调用图像通道提取函数
- 返回提取结果给客户端
公式化:
client→IPC msgserver→f(I)(R,G,B)→IPC msgclient \text{client} \xrightarrow{\text{IPC msg}} \text{server} \xrightarrow{f(I)} (R,G,B) \xrightarrow{\text{IPC msg}} \text{client} clientIPC msgserverf(I)(R,G,B)IPC msgclient
3.2 Channel Extraction 核心算法(示例)
void extract_channels(uint8_t* image, int width, int height,
uint8_t* R, uint8_t* G, uint8_t* B) {
for (int i = 0; i < width*height; ++i) {
R[i] = image[3*i + 0];
G[i] = image[3*i + 1];
B[i] = image[3*i + 2];
}
}
注释:
- 输入
image是线性 RGB 数据,大小为width*height*3- 分别填充三个输出通道数组 R/G/B
4⃣ 总结
- 使用 CCI 模式组织服务:
- Contract:定义通道提取接口
- Concept:说明如何分离 RGB 通道
- Implementation:基于
sd-bus的 IPC 服务实现
- IPC 数据流:
client request→sd-bus service→channel extraction→client response \text{client request} \rightarrow \text{sd-bus service} \rightarrow \text{channel extraction} \rightarrow \text{client response} client request→sd-bus service→channel extraction→client response - 图像通道提取是线性遍历操作,适合在受限环境中实时执行
1⃣ 背景
- 目标:通过 sd-bus 调用 IPC 服务提取图像通道
- 文件路径和通道参数:
const char* file = "/home/sample"; // 原始图像路径
const char* h = "/home/modified"; // 修改后的路径
const uint8_t channel = 1; // 要提取的通道
- 调用 sd-bus 方法:
sd_bus_call_methodC(
bus,
"com.cppcon2024.Channel", // 服务名
"/com/cppcon2024/Channel", // 对象路径
"com.cppcon2024.Channel", // 接口名
"ExtractChannel", // 方法名
&error, &m, // 错误对象和消息对象
"ooy", // 参数类型:object path, object path, uint8
file, h, channel // 参数值
);
注释:
"ooy"表示方法参数类型:o= object path,y= byte(uint8)- 这是 标准 IPC 调用,同步调用 sd-bus 方法
2⃣ CCI 模式:Contract-Concept-Implementation
2.1 Contract(契约)
- 契约定义类型或资源必须满足的条件
- 例如
SD_BUS_CONTRACT强制类型必须是特定的std::unique_ptr
typedef std::unique_ptr<sd_bus, decltype(&sd_bus_unref)> SD_BUS_INNER_TYPE;
struct SD_BUS_CONTRACT {
template<typename T>
static constexpr bool IsUniquePtr = std::is_same<T, SD_BUS_INNER_TYPE>::value;
};
注释:
SD_BUS_INNER_TYPE:管理sd_bus*的智能指针,自动释放资源IsUniquePtr:静态布尔值,用于类型检查
2.2 Concept(概念)
- 概念用于约束模板参数,确保类型满足契约
- C++20 的
concept功能可以在编译期检查类型
template <typename T>
concept SD_BUS_CONCEPT = requires {
requires SD_BUS_CONTRACT::IsUniquePtr<T>;
};
注释:
- 模板参数
T必须是SD_BUS_CONTRACT中定义的SD_BUS_INNER_TYPE- 编译器会报错,如果类型不符合契约
2.3 Implementation(实现)
- 实现是具体类型或函数,用于创建和操作契约类型
template<SD_BUS_CONCEPT T = SD_BUS_INNER_TYPE>
[[nodiscard]] T MAKE_SD_BUS() {
return T(nullptr, &sd_bus_unref); // 创建智能指针,默认空指针
}
- 使用结构体封装:
template<SD_BUS_CONCEPT T = SD_BUS_INNER_TYPE>
struct SD_BUS {
T bus = MAKE_SD_BUS(); // 内部智能指针
decltype(bus.get()) ptr = bus.get(); // 获取原始指针
decltype(bus.get())* addressof_ptr = &ptr;
inline auto operator()() { return addressof_ptr; } // 返回指针地址
inline auto operator*() const { return ptr; } // 解引用
};
注释:
bus管理sd_bus*,生命周期自动管理operator()和operator*提供访问接口
2.4 Message 类型封装(SD_BUS_MESSAGE)
- 同理,对 sd_bus_message 也可以封装:
typedef std::unique_ptr<sd_bus_message, decltype(&sd_bus_message_unref)> SD_BUS_MESSAGE_TYPE;
struct SD_BUS_MESSAGE_CONTRACT {
template<typename T>
static constexpr bool IsUniquePtr = std::is_same<T, SD_BUS_MESSAGE_TYPE>::value;
};
template <typename T>
concept SD_BUS_MESSAGE_CONCEPT = requires {
requires SD_BUS_MESSAGE_CONTRACT::IsUniquePtr<T>;
};
template<SD_BUS_MESSAGE_CONCEPT T = SD_BUS_MESSAGE_TYPE>
[[nodiscard]] T MAKE_SD_BUS_MESSAGE() {
return T(nullptr, &sd_bus_message_unref); // 创建智能指针
}
注释:
- 同样使用智能指针封装消息,确保资源安全
3⃣ 总结
3.1 工作流程
- Contract(契约):定义必须满足的类型特征(如
unique_ptr管理资源) - Concept(概念):在编译期约束模板参数,确保类型符合契约
- Implementation(实现):创建对象、封装操作接口(如
MAKE_SD_BUS)
公式化:
Implementation⊆Concept⊆Contract \text{Implementation} \subseteq \text{Concept} \subseteq \text{Contract} Implementation⊆Concept⊆Contract
- Contract → Concept → Implementation
- 确保 类型安全 和 资源管理正确
3.2 优势
- 自动管理
sd_bus/sd_bus_message的生命周期 - 编译期检查类型是否符合契约
- 可扩展性强,便于定义不同类型的 IPC 资源
1⃣ SD_BUS_MESSAGE 封装
1.1 Implementation(实现)
template<SD_BUS_MESSAGE_CONCEPT T = SD_BUS_MESSAGE_TYPE>
struct SD_BUS_MESSAGE {
T message = MAKE_SD_BUS_MESSAGE(); // 内部智能指针,自动管理资源
decltype(message.get()) ptr = message.get(); // 获取原始指针
decltype(message.get())* addressof_ptr = &ptr; // 指针地址
inline auto operator()() const { return addressof_ptr; } // 返回指针地址
inline operator decltype(auto)() const { return ptr; } // 解引用获取原始 sd_bus_message*
};
注释:
T必须符合SD_BUS_MESSAGE_CONCEPT- 内部
message使用std::unique_ptr管理 sd_bus_message 生命周期operator()和operator decltype(auto)提供访问接口
1.2 Concept(概念)
template<typename T>
concept SD_BUS_MESSAGE_CONCEPT =
std::is_same<T, SD_BUS_MESSAGE<SD_BUS_MESSAGE_TYPE>>::value;
注释:
- 确保模板参数类型是
SD_BUS_MESSAGE<SD_BUS_MESSAGE_TYPE>
2⃣ SD_BUS_ERROR_TYPE 封装
2.1 Contract(契约)
struct SD_BUS_ERROR_TYPE_CONTRACT {
template<typename T, typename = int>
static constexpr bool HasFunctionOperatorOverload = false;
template<typename T>
static constexpr bool HasFunctionOperatorOverload<
T, decltype(&T::operator const char*, 0)
> = true;
template<typename T>
static constexpr bool IsTriviallyConstructible = std::is_constructible<T>::value;
};
注释:
确保
SD_BUS_ERROR_TYPE:
- 有
operator const char*重载- 可平凡构造
2.2 Concept(概念)
template <typename T>
concept SD_BUS_ERROR_TYPE_CONCEPT =
requires { requires SD_BUS_ERROR_TYPE_CONTRACT::HasFunctionOperatorOverload<T>; } &&
requires { requires SD_BUS_ERROR_TYPE_CONTRACT::IsTriviallyConstructible<T>; };
注释:
- 编译期约束类型必须满足契约
2.3 Implementation(实现)
class SD_BUS_ERROR_TYPE {
public:
operator const char*() const { return nullptr; } // 返回错误描述
private:
friend class SD_BUS_ERROR_TYPE_CONTRACT;
};
注释:
- 提供
operator const char*用于获取错误信息- 通过友元关系让 Contract 可以访问内部
3⃣ SD_BUS_ERROR_STRUCT 封装
3.1 Contract(契约)
class SD_BUS_ERROR_STRUCT_CONTRACT {
public:
template <typename T, typename = int>
static constexpr bool HasFieldName = false;
template <typename T>
static constexpr bool HasFieldName<T, decltype((void) T::name, 0)> = true;
template<typename T>
static constexpr bool IsTypeNameStr = std::is_same<decltype(T::name), std::string>::value;
template <typename T, typename = int>
static constexpr bool HasFieldDescription = false;
template <typename T>
static constexpr bool HasFieldDescription<T, decltype((void) T::description,0)> = true;
template<typename T>
static constexpr bool IsTypeDescriptionStr =
std::is_same<decltype(T::description), std::string>::value;
};
注释:
- 检查类型是否有
name和description字段- 确保字段类型是
std::string
3.2 Concept(概念)
template <typename T>
concept SD_BUS_ERROR_STRUCT_CONCEPT =
requires {
requires SD_BUS_ERROR_STRUCT_CONTRACT::HasFieldName<T>;
requires SD_BUS_ERROR_STRUCT_CONTRACT::IsTypeNameStr<T>;
requires SD_BUS_ERROR_STRUCT_CONTRACT::HasFieldDescription<T>;
requires SD_BUS_ERROR_STRUCT_CONTRACT::IsTypeDescriptionStr<T>;
};
注释:
- 编译期检查类型是否满足
name/description字段要求
3.3 Implementation(实现)
创建 sd_bus_error 结构
template <SD_BUS_ERROR_TYPE_CONCEPT T>
[[nodiscard("Return value must be placed inside variable")]]
constexpr auto make_sdbus_error_structure() {
return (sd_bus_error){ .name = T{}, .message = T{}, ._need_free = int{} };
}
注释:
- 返回一个
sd_bus_error结构- 使用模板类型 T 初始化 name 和 message
_need_free标志资源是否需要释放
封装错误结构体
template<SD_BUS_ERROR_TYPE_CONCEPT T = SD_BUS_ERROR_TYPE>
struct SD_BUS_ERROR_STRUCT_INNER {
SD_BUS_ERROR_STRUCT_INNER() : name(""), description("") {}
SD_BUS_ERROR_STRUCT_INNER(const std::string& name, const std::string& description)
: name(std::move(name)), description(std::move(description)) {}
virtual ~SD_BUS_ERROR_STRUCT_INNER() { sd_bus_error_free(&sd_bus_error); }
std::string name;
std::string description;
sd_bus_error sd_bus_error = make_sdbus_error_structure<T>();
};
注释:
- 构造函数可自定义
name和description- 析构函数释放 sd_bus_error
- 内部保持
sd_bus_error与 CCI 类型安全绑定
4⃣ 总结
4.1 核心思路
- Contract(契约):
- 定义必须存在的函数、字段、类型特征
- 检查资源管理规则(如智能指针、可构造性)
- Concept(概念):
- 在编译期约束模板参数
- 确保类型满足 Contract
- Implementation(实现):
- 提供实际类型、函数、资源管理
- 自动管理生命周期(如
sd_bus_message_unref,sd_bus_error_free)
公式化表示:
Implementation⊆Concept⊆Contract \text{Implementation} \subseteq \text{Concept} \subseteq \text{Contract} Implementation⊆Concept⊆Contract
- Contract → Concept → Implementation
- 通过智能指针和模板概念保证类型安全和资源管理
1⃣ SD_BUS_ERROR 封装
1.1 SD_BUS_ERROR_STRUCT 实现
template <SD_BUS_ERROR_STRUCT_CONCEPT T>
struct SD_BUS_ERROR_STRUCT {
SD_BUS_ERROR_STRUCT() : t(T()) {} // 默认构造内部错误结构
SD_BUS_ERROR_STRUCT(const std::string& name, const std::string& description)
: t(T(name, description)) {} // 使用 name/description 构造内部结构
[[nodiscard("Overloaded operator-> returns inner instance of sd_bus_error.")]]
auto operator->() { return &t.sd_bus_error; } // 返回 sd_bus_error*,方便访问
auto operator()() { return &t.sd_bus_error; } // 可调用形式返回 sd_bus_error*
T t; // 内部存储的错误结构
};
注释:
t是内部封装的SD_BUS_ERROR_STRUCT_INNER对象operator->和operator()提供对底层sd_bus_error的访问- 使用模板概念约束类型安全
1.2 类型别名与概念
typedef SD_BUS_ERROR_STRUCT<SD_BUS_ERROR_STRUCT_INNER<>> SD_BUS_ERROR;
template<typename T>
concept SD_BUS_ERROR_CONCEPT = std::is_same<T, SD_BUS_ERROR>::value;
注释:
- 将
SD_BUS_ERROR_STRUCT_INNER<>封装为默认SD_BUS_ERROR类型- 编译期检查类型是否为
SD_BUS_ERROR
2⃣ SERVICE_NAME 封装(服务名验证)
2.1 Contract(契约)
constexpr int SERVICE_NAME_MAX_LENGTH = 30; // 服务名最大长度
constexpr const char* service_name_starts_with = "com"; // 服务名必须以 "com" 开头
struct SERVICE_NAME_CONTRACT {
template<size_t T>
constexpr static bool IsMaxLength = T < SERVICE_NAME_MAX_LENGTH;
template<bool T>
constexpr static bool StartsWith = T;
};
注释:
CONTRACT 定义服务名必须满足的条件
- 长度小于 30
- 前缀为
"com"
2.2 Contract 工具函数
// 检查长度
template<size_t N>
static constexpr size_t SERVICE_NAME_IS_MAX_LENGTH(const char (&str)[N]) {
return N;
}
// 检查前缀
template<std::size_t N>
static constexpr auto SERVICE_NAME_STARTS_WITH(const char (&str)[N]) {
auto subspan = std::span{str}.first(3); // 取前三个字符
int i = 0;
for (auto const& elem : subspan)
if (elem != service_name_starts_with[i++]) return false;
return true;
}
注释:
SERVICE_NAME_IS_MAX_LENGTH返回字符串长度SERVICE_NAME_STARTS_WITH检查是否以"com"开头
2.3 Concept(概念)
template<size_t T, bool U>
concept SERVICE_NAME_CONCEPT =
requires { requires SERVICE_NAME_CONTRACT::IsMaxLength<T>; } &&
requires { requires SERVICE_NAME_CONTRACT::StartsWith<U>; };
注释:
- 模板参数
T和U分别对应长度和前缀检查- 编译期确保服务名满足 CONTRACT
2.4 Implementation(实现)
template<size_t T, bool U>
requires SERVICE_NAME_CONCEPT<T, U>
[[nodiscard]] constexpr auto SERVICE_NAME_INNER(const char (&str)[T]) {
return SERVICE_NAME_TYPE{str}; // 返回封装的服务名类型
}
#define SERVICE_NAME(T) \
SERVICE_NAME_INNER<SERVICE_NAME_IS_MAX_LENGTH(T), SERVICE_NAME_STARTS_WITH(T)>(T)
注释:
- 使用宏
SERVICE_NAME(T)快速创建经过编译期检查的服务名SERVICE_NAME_TYPE是封装的服务名类型(可进一步封装接口)
3⃣ 总结
3.1 核心逻辑
- SD_BUS_ERROR
- Contract:检查结构和操作符
- Concept:模板约束类型
- Implementation:封装内部结构、提供访问
sd_bus_error的操作符
- SERVICE_NAME
- Contract:定义最大长度和前缀条件
- Concept:模板约束编译期检查
- Implementation:封装服务名,使用宏创建
3.2 公式化理解
- 错误类型封装:
SD_BUS_ERROR=SD_BUS_ERROR_STRUCT⟨SD_BUS_ERROR_STRUCT_INNER⟩ \text{SD\_BUS\_ERROR} = \text{SD\_BUS\_ERROR\_STRUCT} \langle \text{SD\_BUS\_ERROR\_STRUCT\_INNER} \rangle SD_BUS_ERROR=SD_BUS_ERROR_STRUCT⟨SD_BUS_ERROR_STRUCT_INNER⟩ - 服务名验证:
SERVICE_NAME⊆CONCEPT⊆CONTRACT,length<30,prefix="com" \text{SERVICE\_NAME} \subseteq \text{CONCEPT} \subseteq \text{CONTRACT}, \quad \text{length} < 30, \quad \text{prefix} = "com" SERVICE_NAME⊆CONCEPT⊆CONTRACT,length<30,prefix="com"
3.3 优势
- 通过 CCI 模式,保证:
- 类型安全
- 资源自动管理(sd_bus_error、sd_bus_message)
- 编译期检查服务名合法性
1⃣ SERVICE_NAME 实现
template<size_t T, bool U>
requires SERVICE_NAME_CONCEPT<T, U>
[[nodiscard]] constexpr auto SERVICE_NAME_INNER(const char (&str)[T]) {
return SERVICE_NAME_TYPE{str}; // 返回封装后的服务名对象
}
#define SERVICE_NAME(T) \
SERVICE_NAME_INNER<SERVICE_NAME_IS_MAX_LENGTH(T), SERVICE_NAME_STARTS_WITH(T)>(T)
template<typename T>
concept SERVICE_NAME_TYPE_CONCEPT = std::is_same<T, SERVICE_NAME_TYPE>::value;
注释:
- 宏
SERVICE_NAME(T)用于编译期验证服务名SERVICE_NAME_TYPE_CONCEPT确保类型安全
2⃣ DBUS 类型封装
2.1 DBUS_PATH(对象路径)
struct DBUS_PATH {
template<std::size_t N>
constexpr explicit DBUS_PATH(const char (&value)[N]) : value(value) {}
const char* value; // 保存 D-Bus 对象路径
std::string signature{"o"}; // D-Bus 类型签名 'o' 表示 object path
};
2.2 DBUS_UINT8(无符号 8 位整数)
struct DBUS_UINT8 {
constexpr explicit DBUS_UINT8(uint8_t value) : value(value) {}
uint8_t value; // 保存数值
std::string signature{"y"}; // D-Bus 类型签名 'y' 表示 uint8
};
注释:
- 这些封装用于 编译期类型检查
- 每种类型包含 D-Bus 签名字符串,用于调用时拼接
3⃣ 支持类型概念
// 判断 T 是否属于列表 U 中的任意类型
template<typename T, typename... U>
concept AnyOf = (std::is_same<T, U>::value || ...);
// 检查所有参数都是 DBus 支持类型
template<typename... T>
concept DBusSupportedTypes = (AnyOf<T, DBUS_PATH, DBUS_UINT8> && ...);
注释:
DBusSupportedTypes保证SD_BUS_CALL_METHODC的参数都是合法类型
4⃣ 常量定义(通道索引)
#define RED_CHANNEL DBUS_UINT8{0}
#define GREEN_CHANNEL DBUS_UINT8{1}
#define BLUE_CHANNEL DBUS_UINT8{2}
注释:
- 用于选择图像通道
- 方便调用时传递类型安全参数
5⃣ SD_BUS_CALL_METHODC 重构
5.1 函数模板定义
template<typename... DBUS_SUPPORTED_TYPES>
requires DBusSupportedTypes<DBUS_SUPPORTED_TYPES...>
[[nodiscard]] constexpr inline auto SD_BUS_CALL_METHODC(
SD_BUS_TYPE_CONCEPT auto const& bus,
SERVICE_NAME_TYPE_CONCEPT auto const& service_name,
OBJECT_PATH_TYPE_CONCEPT auto const& object_path,
INTERFACE_NAME_TYPE_CONCEPT auto const& interface_name,
METHOD_NAME_CONCEPT auto const& method_name,
SD_BUS_ERROR_CONCEPT auto& error,
SD_BUS_MESSAGE_CONCEPT auto& m,
DBUS_SUPPORTED_TYPES const&... args
);
注释:
bus:D-Bus 总线对象service_name:服务名对象(编译期验证)object_path:D-Bus 对象路径interface_name、method_name:D-Bus 接口和方法名error:错误对象m:消息对象args:D-Bus 参数,保证类型安全
5.2 调用实现
sd_bus_call_methodC(
bus,
service_name.str, // 服务名字符串
object_path.str, // 对象路径字符串
interface_name.str, // 接口名字符串
method_name.str, // 方法名字符串
error(),
m(),
(Cargs.signature + ...).c_str(), // 拼接参数签名
args.value... // 展开参数值
);
注释:
(Cargs.signature + ...).c_str():将所有参数类型签名拼接成字符串args.value...:展开传入的参数值- 编译期保证所有类型合法,运行时直接调用
sd_bus_call_methodC
6⃣ 总结
6.1 核心设计
- SERVICE_NAME:编译期验证服务名长度和前缀
- DBUS_PATH / DBUS_UINT8:封装 D-Bus 参数类型,包含类型签名
- DBusSupportedTypes:模板概念,保证函数模板参数合法
- SD_BUS_CALL_METHODC:类型安全、可扩展的 D-Bus 方法调用封装
6.2 公式化理解
- 参数类型约束:
args∈DBUS_PATH,DBUS_UINT8 \text{args} \in { \text{DBUS\_PATH}, \text{DBUS\_UINT8} } args∈DBUS_PATH,DBUS_UINT8 - D-Bus 调用模板:
SD_BUS_CALL_METHODC(bus,service_name,object_path,interface,method,error,message,args…) \text{SD\_BUS\_CALL\_METHODC}(bus, service\_name, object\_path, interface, method, error, message, args\ldots) SD_BUS_CALL_METHODC(bus,service_name,object_path,interface,method,error,message,args…) - 参数签名拼接:
signature=∑iargsi.signature signature = \sum_{i} args_i.signature signature=i∑argsi.signature
6.3 优势
- 编译期类型安全:避免传入错误类型
- 自动拼接签名:简化 D-Bus 调用
- 与 CCI 模式兼容:Contract → Concept → Implementation
1⃣ D-Bus 调用对比
1.1 传统调用
sd_bus_call_method(
bus,
service_name,
object_path,
interface_name,
method_name,
&error,
&m,
"ooy", // 参数类型签名: object, object, uint8
file,
save_path,
channel
);
注释:
- 每次调用都要显式传入类型签名字符串
"ooy"- 参数是原始 C 类型,不保证类型安全
- 汇编量大,因为所有参数和函数调用都保留
1.2 CCI 封装调用
sd_bus_call_method(
bus,
service_name,
object_path,
interface_name,
method_name,
error,
m,
DBUS_PATH{"/home/image/sample"},
DBUS_PATH{"/home/image/red"},
RED_CHANNEL
);
注释:
- 使用类型安全的封装类型
DBUS_PATH和DBUS_UINT8- 签名字符串由模板编译期自动拼接
- 优化后生成的汇编非常简洁:
main:
mov eax, 2
ret
优势:
- 编译期类型检查
- 零运行开销
- 汇编量少(inline + constexpr)
2⃣ PERSON 类设计(使用 CCI 模式)
2.1 需求
- 属性:
name,surname,age,expenses - 每个属性有 getter
name限制:- 长度最小 3,最大 40
- 必须以
'A'开头
2.2 辅助模板工具
逻辑非
template<bool T>
struct logical_not {
static constexpr bool value = !T;
};
字段检查器
class FieldChecker {
public:
template<typename T, typename Field>
static constexpr bool HasField(Field field) {
return std::is_member_object_pointer<decltype(field)>::value;
}
template<typename T, typename... Fields>
static constexpr bool HasFields(Fields... field) {
return (HasField<T>(field) && ...); // 检查所有字段
}
};
函数检查器
class FunctionChecker {
public:
template<typename T, typename Function>
static constexpr bool HasFunction(Function function) {
return std::is_member_function_pointer<decltype(function)>::value;
}
template<typename T, typename... Functions>
static constexpr bool HasFunctions(Functions... function) {
return (HasFunction<T>(function) && ...); // 检查所有方法
}
};
注释:
- 编译期检查类是否包含指定字段和成员函数
2.3 属性类型
struct NAME {
template<std::size_t N>
constexpr explicit NAME(const char (&name)[N]) : name(name) {}
std::string name;
};
struct SURNAME {
template<std::size_t N>
constexpr explicit SURNAME(const char (&surname)[N]) : surname(surname) {}
std::string surname;
};
struct AGE {
constexpr explicit AGE(int age) : age(age) {}
int age;
};
struct EXPENSES {
constexpr explicit EXPENSES(double expenses) : expenses(expenses) {}
double expenses;
};
注释:
- 属性封装为独立类型
- 支持编译期约束
2.4 PERSON Contract(契约)
template<typename T>
class PersonContract {
public:
static constexpr bool FieldsExist =
FieldChecker::HasFields<T>(&T::name, &T::surname, &T::age, &T::expenses);
static constexpr bool FunctionsExist =
FunctionChecker::HasFunctions<T>(&T::getName, &T::getSurname, &T::getAge, &T::getExpenses);
static constexpr bool IsNotDefaultConstructible =
logical_not<std::is_default_constructible<T>::value>::value;
};
注释:
CONTRACT 检查:
- 所有字段存在
- 所有 getter 存在
- 禁止默认构造
2.5 PERSON Concept(概念)
template<typename Person>
concept PersonConcept =
PersonContract<Person>::FieldsExist &&
PersonContract<Person>::FunctionsExist &&
PersonContract<Person>::IsNotDefaultConstructible;
注释:
- 编译期约束模板类型必须符合 CONTRACT
2.6 PERSON Name 类型检查
template<std::size_t N>
static constexpr bool IsMinLength = N > 3;
template<std::size_t N>
static constexpr bool IsMaxLength = N < 40;
template<std::size_t N>
static constexpr std::size_t Length(const char (&str)[N]) { return N; }
template<std::size_t N>
static constexpr bool StartsWith(const char (&str)[N]) { return str[0] == 'A'; }
template<std::size_t N, bool... U>
requires (U && ...)
[[nodiscard]] constexpr auto MAKE_NAME_OBJECT(const char(&str)[N]) { return NAME{str}; }
注释:
- 编译期检查名字长度和首字母
MAKE_NAME_OBJECT生成合法NAME对象
3⃣ 总结
3.1 核心优势
- 类型安全:字段、函数、长度、首字母全部在编译期检查
- 零运行开销:使用
constexpr和模板元编程 - 可扩展:可添加更多属性类型,自动验证
3.2 公式化理解
- 名字合法性:
IsValidName(name)=(Length(name)>3)∧(Length(name)<40)∧(name[0]=′A′) \text{IsValidName}(name) = (\text{Length}(name) > 3) \wedge (\text{Length}(name) < 40) \wedge (name[0] = 'A') IsValidName(name)=(Length(name)>3)∧(Length(name)<40)∧(name[0]=′A′) - PERSON 类概念:
PersonConcept(T)=HasFields(T)∧HasGetters(T)∧¬DefaultConstructible(T) \text{PersonConcept}(T) = \text{HasFields}(T) \wedge \text{HasGetters}(T) \wedge \neg \text{DefaultConstructible}(T) PersonConcept(T)=HasFields(T)∧HasGetters(T)∧¬DefaultConstructible(T) - CCI 模式:
Contract⊆Concept⊆Implementation \text{Contract} \subseteq \text{Concept} \subseteq \text{Implementation} Contract⊆Concept⊆Implementation
1⃣ PERSON 类实现(使用 CCI)
1.1 名字辅助宏
#define MAKE_NAME(T) \
MAKE_NAME_OBJECT<Length(T), IsMinLength<Length(T)>, IsMaxLength<Length(T)>, StartsWith(T)>(T)
注释:
通过宏
MAKE_NAME(T)快速生成经过 编译期验证 的NAME对象检查:
- 长度合法(3 ≤ length ≤ 40)
- 首字母是
'A'
1.2 PERSON 类定义
struct Person {
template<typename NAME, typename SURNAME, typename AGE, typename EXPENSES>
requires PersonTypes::SupportedTypes<NAME, SURNAME, AGE, EXPENSES>
Person(NAME name, SURNAME surname, AGE age, EXPENSES expenses)
: name(name), surname(surname), age(age), expenses(expenses) {}
NAME getName() { return name; }
SURNAME getSurname() { return surname; }
AGE getAge() const { return age; }
EXPENSES getExpenses() { return expenses; }
NAME name;
SURNAME surname;
AGE age;
EXPENSES expenses;
friend class PersonContract<Person>; // Contract 可以访问私有成员
};
注释:
- 构造函数模板保证所有属性类型符合
SupportedTypes- getter 方法保证访问安全
friend允许PersonContract在编译期检查字段
1.3 main 函数示例
int main(int /* argc */, char** /* argv */) {
Person person{
MAKE_NAME("Arian"), // 编译期验证名字
SURNAME{"Ajdari"}, // 姓
AGE{29}, // 年龄
EXPENSES{500.45} // 消费
};
return 0;
}
注释:
MAKE_NAME("Arian")编译期检查长度和首字母- 所有类型都经过模板约束和 Contract 验证
- 最终生成的二进制与传统方法相比没有额外开销
2⃣ CCI 的总结理解
2.1 优势
- 编译期类型安全:部分逻辑从运行期转移到编译期
- 高层抽象封装低层设施:例如将
sd_bus封装为安全、类型化接口 - Contract 可以表达软件文档或 UML 意图:可以作为程序化签名
- 零运行时开销:所有验证在编译期完成
2.2 注意事项
- CCI 不是万能工具,不必在所有地方使用
- CCI 功能也可以通过其他 C++ 技术实现
- 使用权在程序员手中,需要根据场景判断
3⃣ 公式化理解
3.1 名字合法性
IsValidName(name)=(Length(name)≥3)∧(Length(name)≤40)∧(name[0]=′A′) \text{IsValidName}(name) = (\text{Length}(name) \ge 3) \wedge (\text{Length}(name) \le 40) \wedge (name[0] = 'A') IsValidName(name)=(Length(name)≥3)∧(Length(name)≤40)∧(name[0]=′A′)
3.2 PERSON 类概念
PersonConcept(T)=FieldsExist(T)∧FunctionsExist(T)∧¬DefaultConstructible(T) \text{PersonConcept}(T) = \text{FieldsExist}(T) \wedge \text{FunctionsExist}(T) \wedge \neg \text{DefaultConstructible}(T) PersonConcept(T)=FieldsExist(T)∧FunctionsExist(T)∧¬DefaultConstructible(T)
3.3 CCI 模式公式
Contract⊆Concept⊆Implementation \text{Contract} \subseteq \text{Concept} \subseteq \text{Implementation} Contract⊆Concept⊆Implementation
注释:
- Contract 定义规则
- Concept 编译期约束
- Implementation 真正实现功能
总结:
- 使用 CCI,你可以在编译期确保类型和字段合法,减少运行时错误
- 它适合封装 低层设施 或 安全敏感接口
- 最终生成的二进制效率高且类型安全
更多推荐
所有评论(0)