如何为C++20的connection_c概念定义更优雅灵活的读写接口?
问题描述
原始的C++20连接概念定义如下:
template <typename t> concept connection_c = requires(t p_t) { requires std::copy_constructible<t>; { p_t.write(std::declval<const uint8_t *>(), std::declval<const uint8_t *>()) } -> std::same_as<communication_status_t>; { p_t.read(std::declval<uint8_t *>(), std::declval<uint8_t *>(), std::declval<uint8_t **>()) } -> std::same_as<communication_status_t>; };
希望让write和read方法的定义更优雅灵活,避免显式依赖const uint8_t*或uint8_t*类型。
尝试过这些思路但未成功:
- 给
write/read定义模板参数,发现不可行; - 考虑用
std::convertible_to<uint8_t>,但没掌握正确语法,也不确定是否适用; - 尝试迭代器相关概念(比如
input_iterator、output_iterator),但不知道怎么实现或是否可行。
之后自己实现了两种改进方案:
第一种方案:
template <typename t, typename t_byte> concept connection_c = requires(t p_t) { requires std::copy_constructible<t> ; { p_t.id() } -> std::same_as<tnctt::id>; { p_t.write(std::declval<const t_byte *>(), std::declval<const t_byte *>()) } -> std::same_as<communication_status_t>; { p_t.read(std::declval<t_byte *>(), std::declval<t_byte *>(), std::declval<t_byte **>()) } -> std::same_as<communication_status_t>; };
第二种优化后的方案:
template <typename t> concept one_byte_c = (sizeof(t) == 1); template <typename t, typename t_byte> concept connection_c = requires(t p_t) { requires std::copy_constructible<t> && std::move_constructible<t> && !std::default_initializable<t>; requires one_byte_c<t_byte>; { p_t.write(std::declval<const t_byte *>(), std::declval<const t_byte *>()) } -> std::same_as<communication_status_t>; { p_t.read(std::declval<t_byte *>(), std::declval<t_byte *>(), std::declval<t_byte **>()) } -> std::same_as<communication_status_t>; };
请问还有其他可行的实现方式吗?
可行的替代实现方式
1. 使用迭代器概念替代指针
直接利用C++20的迭代器概念,让write和read支持符合要求的迭代器类型,不局限于指针,既灵活又贴合标准库设计风格:
#include <iterator> template <typename t> concept connection_c = requires(t p_t) { requires std::copy_constructible<t>; // write接受输入迭代器范围 { p_t.write(std::declval<std::input_iterator auto>(), std::declval<std::input_iterator auto>()) } -> std::same_as<communication_status_t>; // read接受输出迭代器和指向迭代器的指针(用于返回实际读取位置) { p_t.read(std::declval<std::output_iterator auto>(), std::declval<std::output_iterator auto>(), std::declval<std::output_iterator auto*>()) } -> std::same_as<communication_status_t>; };
这种方式下,任何满足input_iterator的类型(比如const uint8_t*、std::vector<uint8_t>::const_iterator)都能传给write;read则接受output_iterator类型(比如uint8_t*、std::back_insert_iterator<std::vector<uint8_t>>),灵活性大幅提升。
2. 让概念自动推导字节类型
无需显式指定t_byte模板参数,让概念自动推导write/read方法的参数类型,同时约束该类型为单字节:
template <typename t> concept connection_c = requires(t p_t) { requires std::copy_constructible<t> && std::move_constructible<t> && !std::default_initializable<t>; // 推导write的参数类型并约束为单字节 requires requires(const t& conn, const auto* first, const auto* last) { { conn.write(first, last) } -> std::same_as<communication_status_t>; requires sizeof(std::remove_const_t<decltype(*first)>) == 1; }; // 推导read的参数类型并约束为单字节,同时保证输出指针类型匹配 requires requires(t& conn, auto* first, auto* last, auto** out_last) { { conn.read(first, last, out_last) } -> std::same_as<communication_status_t>; requires sizeof(decltype(*first)) == 1; requires std::same_as<decltype(*out_last), decltype(first)>; }; };
这个版本不需要额外模板参数,概念会自动检查write和read的参数是否是指向单字节类型的指针,同时验证返回值符合要求。
3. 基于std::byte兼容其他单字节类型
优先使用std::byte作为基础类型,同时兼容其他单字节类型(比如uint8_t、int8_t),结合std::convertible_to和单字节约束:
#include <cstddef> template <typename t> concept connection_c = requires(t p_t) { requires std::copy_constructible<t> && std::move_constructible<t> && !std::default_initializable<t>; // write接受指向可转换为std::byte的单字节类型的指针 { p_t.write(std::declval<const std::convertible_to<std::byte> auto*>(), std::declval<const std::convertible_to<std::byte> auto*>()) } -> std::same_as<communication_status_t>; // read接受指向可转换为std::byte的单字节类型的指针 { p_t.read(std::declval<std::convertible_to<std::byte> auto*>(), std::declval<std::convertible_to<std::byte> auto*>(), std::declval<std::convertible_to<std::byte> auto**>()) } -> std::same_as<communication_status_t>; };
这种方式既保证类型的单字节特性,又利用std::convertible_to<std::byte>确保类型能和标准字节类型互操作。
内容的提问来源于stack exchange,提问作者canellas
相关产品推荐
相关产品推荐

