diff --git a/.agents/docs/plans/implementation-plan.md b/.agents/docs/plans/implementation-plan.md new file mode 100644 index 0000000..55ea982 --- /dev/null +++ b/.agents/docs/plans/implementation-plan.md @@ -0,0 +1,82 @@ +# Primitives 下一步实现计划清单(2026-03-23) + +## 总体原则 + +- [ ] 先定义稳定 API 边界,再落地实现 +- [ ] 每个能力都配套 tests + examples + docs +- [ ] 高风险转换默认禁止隐式触发,只允许显式 API + +## M1. C API 互操作层(双向) + +### 工作清单 + +- [ ] 定义稳定 C ABI(命名规范、导出宏、版本策略) +- [ ] 设计双向能力:C 调用 primitives;primitives 适配并调用 C API +- [ ] 统一错误模型(error code + 可选 message buffer) +- [ ] 提供最小可用头文件:`include/mcpplibs/primitives/c_api.h` +- [ ] 增加 C/C++ 混合构建测试(C 编译器 + C++ 链接) +- [ ] 增加示例:`examples/c/basic.c`、`examples/cpp/use_c_api.cpp` + +### 验收标准 + +- [ ] 纯 C 工程可链接并完成基础能力调用 +- [ ] C++ 对外部 C API 的适配调用行为可控、无未定义行为 + +## M2. Concept/Traits 体系增强与元信息 API + +### 工作清单 + +- [ ] 扩展 concept 分层(category/representation/policy-capability) +- [ ] 完善 `traits` 元信息(`kind`、`rep_type`、limits、policy tags) +- [ ] 提供检测类 API(可转换性/是否有损/错误模型能力) +- [ ] 统一 `constexpr` 查询入口,减少分散 traits 访问 +- [ ] 增加编译期测试矩阵(`static_assert` 覆盖) + +### 验收标准 + +- [ ] 上层模块仅依赖公开 concept/traits,不依赖 `details::*` +- [ ] 元信息可支撑转换层与算法层的约束判定 + +## M3. 显式转换层(任意策略组适用) + +### 工作清单 + +- [ ] 设计统一接口族(建议):`explicit_cast`、`try_cast`、`checked_cast` +- [ ] 支持任意策略组组合,不绑定特定策略实现 +- [ ] 风险可见化(截断/溢出/精度损失)并可程序化读取 +- [ ] 定义失败语义(错误码或 expected 风格,按策略可配置) +- [ ] 建立转换矩阵测试(同类/跨类/跨策略组) + +### 验收标准 + +- [ ] 所有跨类高风险转换必须走显式 API +- [ ] 风险信息可在编译期或运行期被确定性获取 + +## M4. 算法层(以 max/min 为起点) + +### 工作清单 + +- [ ] 实现 `max`/`min`,并预留 `clamp`/`compare` 扩展位 +- [ ] 算法统一依赖 M2+M3 的公开接口,不绕过转换层 +- [ ] 支持同类与受约束的异类输入 +- [ ] 在可行范围保持 `constexpr`/`noexcept` 特性 +- [ ] 增加边界测试(极值、NaN、有符号/无符号混合) + +### 验收标准 + +- [ ] 算法行为与策略约束一致 +- [ ] 风险路径始终显式、可审计 + +## 建议推进顺序 + +1. M2(先夯实约束与元信息基础) +2. M1(建立跨语言边界) +3. M3(收敛转换风险) +4. M4(复用基础能力实现算法) + +## 总体完成跟踪 + +- [ ] M1 完成 +- [ ] M2 完成 +- [ ] M3 完成 +- [ ] M4 完成 diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 2b8024d..016ae56 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -14,7 +14,7 @@ ## 正在进行中的计划 -- 当前计划目录:`../.github/prompts/` +- 当前计划目录:`../.agents/docs/plans/` ## 说明 diff --git a/src/operations/dispatcher.cppm b/src/operations/dispatcher.cppm index 9fdaa33..7947265 100644 --- a/src/operations/dispatcher.cppm +++ b/src/operations/dispatcher.cppm @@ -15,20 +15,15 @@ import mcpplibs.primitives.underlying; export namespace mcpplibs::primitives::operations { -template -concept primitive_instance = requires { - typename primitives::meta::traits< - std::remove_cvref_t>::value_type; -}; - -template struct dispatcher_meta { using lhs_primitive = std::remove_cvref_t; using rhs_primitive = std::remove_cvref_t; - using lhs_traits = primitives::meta::traits; - using rhs_traits = primitives::meta::traits; + using lhs_traits = meta::traits; + using rhs_traits = meta::traits; using lhs_value_type = lhs_traits::value_type; using rhs_value_type = rhs_traits::value_type; @@ -36,15 +31,15 @@ struct dispatcher_meta { using lhs_rep = underlying::traits::rep_type; using rhs_rep = underlying::traits::rep_type; - using lhs_type_policy = typename lhs_traits::type_policy; - using lhs_value_policy = typename lhs_traits::value_policy; - using lhs_error_policy = typename lhs_traits::error_policy; - using lhs_concurrency_policy = typename lhs_traits::concurrency_policy; + using lhs_type_policy = lhs_traits::type_policy; + using lhs_value_policy = lhs_traits::value_policy; + using lhs_error_policy = lhs_traits::error_policy; + using lhs_concurrency_policy = lhs_traits::concurrency_policy; - using rhs_type_policy = typename rhs_traits::type_policy; - using rhs_value_policy = typename rhs_traits::value_policy; - using rhs_error_policy = typename rhs_traits::error_policy; - using rhs_concurrency_policy = typename rhs_traits::concurrency_policy; + using rhs_type_policy = rhs_traits::type_policy; + using rhs_value_policy = rhs_traits::value_policy; + using rhs_error_policy = rhs_traits::error_policy; + using rhs_concurrency_policy = rhs_traits::concurrency_policy; static constexpr bool policy_group_consistent = std::is_same_v && @@ -85,7 +80,8 @@ struct dispatcher_meta { ErrorPayload>; }; -template using dispatch_result_t = std::expected< typename dispatcher_meta::common_rep, @@ -93,7 +89,8 @@ using dispatch_result_t = std::expected< // Dispatcher pipeline: compile-time negotiation plus runtime chain // (concurrency -> value -> error) through selected policy handlers. -template constexpr auto dispatch(Lhs const &lhs, Rhs const &rhs) -> dispatch_result_t { diff --git a/src/operations/invoker.cppm b/src/operations/invoker.cppm index 7c84208..87c4258 100644 --- a/src/operations/invoker.cppm +++ b/src/operations/invoker.cppm @@ -17,9 +17,8 @@ import mcpplibs.primitives.policy.handler; import mcpplibs.primitives.policy.impl; import mcpplibs.primitives.policy.traits; -export namespace mcpplibs::primitives::operations::runtime { -namespace details { +namespace mcpplibs::primitives::operations::runtime::details { template constexpr auto make_error(policy::error::kind kind, char const *reason, @@ -781,18 +780,22 @@ constexpr auto make_div_zero(char const *reason) return make_error(policy::error::kind::divide_by_zero, reason); } -constexpr auto apply_runtime_fence(bool enabled, - std::memory_order order) noexcept -> void { +constexpr auto apply_runtime_fence(const bool enabled, + const std::memory_order order) noexcept -> void { if (!enabled) { return; } - if (!std::is_constant_evaluated()) { + if !consteval { std::atomic_thread_fence(order); } } -} // namespace details +} // namespace mcpplibs::primitives::operations::runtime::details + + + +export namespace mcpplibs::primitives::operations::runtime { template struct op_binding { diff --git a/src/operations/operators.cppm b/src/operations/operators.cppm index 67f8b40..86bc2a6 100644 --- a/src/operations/operators.cppm +++ b/src/operations/operators.cppm @@ -17,7 +17,10 @@ import mcpplibs.primitives.policy.handler; import mcpplibs.primitives.policy.impl; import mcpplibs.primitives.underlying.traits; -export namespace mcpplibs::primitives::operations { +namespace mcpplibs::primitives::operations { + +template +concept underlying_operand = underlying_type>; namespace details { template struct three_way_ordering { @@ -61,21 +64,22 @@ constexpr auto decode_three_way_code(CommonRep const &code) -> Ordering { } // namespace details -template using primitive_dispatch_result_t = std::expected::common_rep, typename meta::traits::policies>, ErrorPayload>; -template -concept underlying_operand = underlying_type>; - -template +template using mixed_bridge_primitive_t = meta::make_primitive_t< std::remove_cvref_t, typename meta::traits::policies>; -template using mixed_primitive_dispatch_result_t = @@ -83,7 +87,7 @@ using mixed_primitive_dispatch_result_t = mixed_bridge_primitive_t, ErrorPayload>; -template using three_way_dispatch_result_t = std::expected< details::three_way_ordering_t< @@ -91,7 +95,7 @@ using three_way_dispatch_result_t = std::expected< ErrorPayload>::common_rep>, ErrorPayload>; -template using mixed_three_way_dispatch_result_t = three_way_dispatch_result_t; template using flipped_mixed_primitive_dispatch_result_t = primitive_dispatch_result_t< OpTag, mixed_bridge_primitive_t, Primitive, ErrorPayload>; -template using flipped_mixed_three_way_dispatch_result_t = three_way_dispatch_result_t< mixed_bridge_primitive_t, Primitive, ErrorPayload>; -template using unary_dispatch_result_t = primitive_dispatch_result_t; -template constexpr auto apply_assign(Lhs &lhs, Rhs const &rhs) -> primitive_dispatch_result_t; -template constexpr auto apply(Lhs const &lhs, Rhs const &rhs) -> primitive_dispatch_result_t { @@ -136,14 +140,14 @@ constexpr auto apply(Lhs const &lhs, Rhs const &rhs) return result_primitive{*raw}; } -template constexpr auto apply(Operand const &operand) -> unary_dispatch_result_t { return apply(operand, operand); } -template constexpr auto apply(Lhs const &lhs, Rhs const &rhs) -> mixed_primitive_dispatch_result_t { @@ -152,7 +156,7 @@ constexpr auto apply(Lhs const &lhs, Rhs const &rhs) return apply(lhs, bridge_rhs); } -template constexpr auto apply(Lhs const &lhs, Rhs const &rhs) -> flipped_mixed_primitive_dispatch_result_t +template constexpr auto increment(Operand &operand) -> unary_dispatch_result_t { return apply_assign(operand, operand); } -template +template constexpr auto decrement(Operand &operand) -> unary_dispatch_result_t { return apply_assign(operand, operand); } -template +template constexpr auto bit_not(Operand const &operand) -> unary_dispatch_result_t { return apply(operand); } -template +template constexpr auto unary_plus(Operand const &operand) -> unary_dispatch_result_t { return apply(operand); } -template +template constexpr auto unary_minus(Operand const &operand) -> unary_dispatch_result_t { return apply(operand); } // Binary arithmetic operations -template constexpr auto add(Lhs const &lhs, Rhs const &rhs) -> primitive_dispatch_result_t { return apply(lhs, rhs); } -template constexpr auto add(Lhs const &lhs, Rhs const &rhs) -> mixed_primitive_dispatch_result_t { return apply(lhs, rhs); } -template constexpr auto add(Lhs const &lhs, Rhs const &rhs) -> flipped_mixed_primitive_dispatch_result_t(lhs, rhs); } -template constexpr auto sub(Lhs const &lhs, Rhs const &rhs) -> primitive_dispatch_result_t { return apply(lhs, rhs); } -template constexpr auto sub(Lhs const &lhs, Rhs const &rhs) -> mixed_primitive_dispatch_result_t { return apply(lhs, rhs); } -template constexpr auto sub(Lhs const &lhs, Rhs const &rhs) -> flipped_mixed_primitive_dispatch_result_t(lhs, rhs); } -template constexpr auto mul(Lhs const &lhs, Rhs const &rhs) -> primitive_dispatch_result_t { return apply(lhs, rhs); } -template constexpr auto mul(Lhs const &lhs, Rhs const &rhs) -> mixed_primitive_dispatch_result_t(lhs, rhs); } -template constexpr auto mul(Lhs const &lhs, Rhs const &rhs) -> flipped_mixed_primitive_dispatch_result_t(lhs, rhs); } -template constexpr auto div(Lhs const &lhs, Rhs const &rhs) -> primitive_dispatch_result_t { return apply(lhs, rhs); } -template constexpr auto mod(Lhs const &lhs, Rhs const &rhs) -> primitive_dispatch_result_t { return apply(lhs, rhs); } -template constexpr auto mod(Lhs const &lhs, Rhs const &rhs) -> mixed_primitive_dispatch_result_t { return apply(lhs, rhs); } -template constexpr auto mod(Lhs const &lhs, Rhs const &rhs) -> flipped_mixed_primitive_dispatch_result_t constexpr auto shift_left(Lhs const &lhs, Rhs const &rhs) -> primitive_dispatch_result_t { return apply(lhs, rhs); } -template constexpr auto shift_left(Lhs const &lhs, Rhs const &rhs) -> mixed_primitive_dispatch_result_t { return apply(lhs, rhs); } -template constexpr auto shift_left(Lhs const &lhs, Rhs const &rhs) -> flipped_mixed_primitive_dispatch_result_t(lhs, rhs); } -template constexpr auto shift_right(Lhs const &lhs, Rhs const &rhs) -> primitive_dispatch_result_t { return apply(lhs, rhs); } -template constexpr auto shift_right(Lhs const &lhs, Rhs const &rhs) -> mixed_primitive_dispatch_result_t { return apply(lhs, rhs); } -template constexpr auto shift_right(Lhs const &lhs, Rhs const &rhs) -> flipped_mixed_primitive_dispatch_result_t(lhs, rhs); } -template constexpr auto bit_and(Lhs const &lhs, Rhs const &rhs) -> primitive_dispatch_result_t { return apply(lhs, rhs); } -template constexpr auto bit_and(Lhs const &lhs, Rhs const &rhs) -> mixed_primitive_dispatch_result_t { return apply(lhs, rhs); } -template constexpr auto bit_and(Lhs const &lhs, Rhs const &rhs) -> flipped_mixed_primitive_dispatch_result_t(lhs, rhs); } -template constexpr auto bit_or(Lhs const &lhs, Rhs const &rhs) -> primitive_dispatch_result_t { return apply(lhs, rhs); } -template constexpr auto bit_or(Lhs const &lhs, Rhs const &rhs) -> mixed_primitive_dispatch_result_t { return apply(lhs, rhs); } -template constexpr auto bit_or(Lhs const &lhs, Rhs const &rhs) -> flipped_mixed_primitive_dispatch_result_t(lhs, rhs); } -template constexpr auto bit_xor(Lhs const &lhs, Rhs const &rhs) -> primitive_dispatch_result_t { return apply(lhs, rhs); } -template constexpr auto bit_xor(Lhs const &lhs, Rhs const &rhs) -> mixed_primitive_dispatch_result_t { return apply(lhs, rhs); } -template constexpr auto bit_xor(Lhs const &lhs, Rhs const &rhs) -> flipped_mixed_primitive_dispatch_result_t(lhs, rhs); } -template constexpr auto div(Lhs const &lhs, Rhs const &rhs) -> mixed_primitive_dispatch_result_t { return apply(lhs, rhs); } -template constexpr auto div(Lhs const &lhs, Rhs const &rhs) -> flipped_mixed_primitive_dispatch_result_t(lhs, rhs); } -template constexpr auto equal(Lhs const &lhs, Rhs const &rhs) -> primitive_dispatch_result_t { return apply(lhs, rhs); } -template constexpr auto equal(Lhs const &lhs, Rhs const &rhs) -> mixed_primitive_dispatch_result_t { return apply(lhs, rhs); } -template constexpr auto equal(Lhs const &lhs, Rhs const &rhs) -> flipped_mixed_primitive_dispatch_result_t(lhs, rhs); } -template constexpr auto not_equal(Lhs const &lhs, Rhs const &rhs) -> primitive_dispatch_result_t { return apply(lhs, rhs); } -template constexpr auto not_equal(Lhs const &lhs, Rhs const &rhs) -> mixed_primitive_dispatch_result_t { return apply(lhs, rhs); } -template constexpr auto not_equal(Lhs const &lhs, Rhs const &rhs) -> flipped_mixed_primitive_dispatch_result_t(lhs, rhs); } -template constexpr auto three_way_compare(Lhs const &lhs, Rhs const &rhs) -> three_way_dispatch_result_t { @@ -480,7 +484,7 @@ constexpr auto three_way_compare(Lhs const &lhs, Rhs const &rhs) return details::decode_three_way_code(*raw); } -template constexpr auto three_way_compare(Lhs const &lhs, Rhs const &rhs) -> mixed_three_way_dispatch_result_t { @@ -489,7 +493,7 @@ constexpr auto three_way_compare(Lhs const &lhs, Rhs const &rhs) return three_way_compare(lhs, bridge_rhs); } -template constexpr auto three_way_compare(Lhs const &lhs, Rhs const &rhs) -> flipped_mixed_three_way_dispatch_result_t { @@ -498,7 +502,7 @@ constexpr auto three_way_compare(Lhs const &lhs, Rhs const &rhs) return three_way_compare(bridge_lhs, rhs); } -template constexpr auto apply_assign(Lhs &lhs, Rhs const &rhs) -> primitive_dispatch_result_t { @@ -506,9 +510,6 @@ constexpr auto apply_assign(Lhs &lhs, Rhs const &rhs) using lhs_value_type = lhs_traits::value_type; using lhs_value_policy = lhs_traits::value_policy; using lhs_rep = underlying::traits::rep_type; - using out_primitive = primitive_dispatch_result_t::value_type; - using common_rep = meta::traits::value_type; auto out = apply(lhs, rhs); if (!out.has_value()) { @@ -532,70 +533,70 @@ constexpr auto apply_assign(Lhs &lhs, Rhs const &rhs) return out; } -template constexpr auto add_assign(Lhs &lhs, Rhs const &rhs) -> primitive_dispatch_result_t { return apply_assign(lhs, rhs); } -template constexpr auto sub_assign(Lhs &lhs, Rhs const &rhs) -> primitive_dispatch_result_t { return apply_assign(lhs, rhs); } -template constexpr auto mul_assign(Lhs &lhs, Rhs const &rhs) -> primitive_dispatch_result_t { return apply_assign(lhs, rhs); } -template constexpr auto div_assign(Lhs &lhs, Rhs const &rhs) -> primitive_dispatch_result_t { return apply_assign(lhs, rhs); } -template constexpr auto mod_assign(Lhs &lhs, Rhs const &rhs) -> primitive_dispatch_result_t { return apply_assign(lhs, rhs); } -template constexpr auto shift_left_assign(Lhs &lhs, Rhs const &rhs) -> primitive_dispatch_result_t { return apply_assign(lhs, rhs); } -template constexpr auto shift_right_assign(Lhs &lhs, Rhs const &rhs) -> primitive_dispatch_result_t { return apply_assign(lhs, rhs); } -template constexpr auto bit_and_assign(Lhs &lhs, Rhs const &rhs) -> primitive_dispatch_result_t { return apply_assign(lhs, rhs); } -template constexpr auto bit_or_assign(Lhs &lhs, Rhs const &rhs) -> primitive_dispatch_result_t { return apply_assign(lhs, rhs); } -template constexpr auto bit_xor_assign(Lhs &lhs, Rhs const &rhs) -> primitive_dispatch_result_t { @@ -607,13 +608,13 @@ constexpr auto bit_xor_assign(Lhs &lhs, Rhs const &rhs) export namespace mcpplibs::primitives::operators { // Unary operators -template +template constexpr auto operator++(Operand &operand) -> operations::unary_dispatch_result_t { return operations::increment(operand); } -template +template constexpr auto operator++(Operand &operand, int) -> operations::unary_dispatch_result_t { auto const before = operand; @@ -624,13 +625,13 @@ constexpr auto operator++(Operand &operand, int) return before; } -template +template constexpr auto operator--(Operand &operand) -> operations::unary_dispatch_result_t { return operations::decrement(operand); } -template +template constexpr auto operator--(Operand &operand, int) -> operations::unary_dispatch_result_t { auto const before = operand; @@ -641,33 +642,33 @@ constexpr auto operator--(Operand &operand, int) return before; } -template +template constexpr auto operator+(Operand const &operand) -> operations::unary_dispatch_result_t { return operations::unary_plus(operand); } -template +template constexpr auto operator-(Operand const &operand) -> operations::unary_dispatch_result_t { return operations::unary_minus(operand); } -template +template constexpr auto operator~(Operand const &operand) -> operations::unary_dispatch_result_t { return operations::bit_not(operand); } // Binary arithmetic operators -template +template constexpr auto operator+(Lhs const &lhs, Rhs const &rhs) -> operations::primitive_dispatch_result_t { return operations::add(lhs, rhs); } -template constexpr auto operator+(Lhs const &lhs, Rhs const &rhs) -> operations::mixed_primitive_dispatch_result_t + meta::primitive_type Rhs> constexpr auto operator+(Lhs const &lhs, Rhs const &rhs) -> operations::flipped_mixed_primitive_dispatch_result_t< operations::Addition, Lhs, Rhs> { return operations::add(lhs, rhs); } -template +template constexpr auto operator-(Lhs const &lhs, Rhs const &rhs) -> operations::primitive_dispatch_result_t { return operations::sub(lhs, rhs); } -template constexpr auto operator-(Lhs const &lhs, Rhs const &rhs) -> operations::mixed_primitive_dispatch_result_t + meta::primitive_type Rhs> constexpr auto operator-(Lhs const &lhs, Rhs const &rhs) -> operations::flipped_mixed_primitive_dispatch_result_t< operations::Subtraction, Lhs, Rhs> { return operations::sub(lhs, rhs); } -template +template constexpr auto operator*(Lhs const &lhs, Rhs const &rhs) -> operations::primitive_dispatch_result_t { return operations::mul(lhs, rhs); } -template constexpr auto operator*(Lhs const &lhs, Rhs const &rhs) -> operations::mixed_primitive_dispatch_result_t + meta::primitive_type Rhs> constexpr auto operator*(Lhs const &lhs, Rhs const &rhs) -> operations::flipped_mixed_primitive_dispatch_result_t< operations::Multiplication, Lhs, Rhs> { return operations::mul(lhs, rhs); } -template +template constexpr auto operator/(Lhs const &lhs, Rhs const &rhs) -> operations::primitive_dispatch_result_t { return operations::div(lhs, rhs); } -template constexpr auto operator/(Lhs const &lhs, Rhs const &rhs) -> operations::mixed_primitive_dispatch_result_t + meta::primitive_type Rhs> constexpr auto operator/(Lhs const &lhs, Rhs const &rhs) -> operations::flipped_mixed_primitive_dispatch_result_t< operations::Division, Lhs, Rhs> { return operations::div(lhs, rhs); } -template +template constexpr auto operator%(Lhs const &lhs, Rhs const &rhs) -> operations::primitive_dispatch_result_t { return operations::mod(lhs, rhs); } -template constexpr auto operator%(Lhs const &lhs, Rhs const &rhs) -> operations::mixed_primitive_dispatch_result_t + meta::primitive_type Rhs> constexpr auto operator%(Lhs const &lhs, Rhs const &rhs) -> operations::flipped_mixed_primitive_dispatch_result_t< operations::Modulus, Lhs, Rhs> { @@ -778,14 +779,14 @@ constexpr auto operator%(Lhs const &lhs, Rhs const &rhs) } // Binary bitwise operators -template +template constexpr auto operator<<(Lhs const &lhs, Rhs const &rhs) -> operations::primitive_dispatch_result_t { return operations::shift_left(lhs, rhs); } -template constexpr auto operator<<(Lhs const &lhs, Rhs const &rhs) -> operations::mixed_primitive_dispatch_result_t + meta::primitive_type Rhs> constexpr auto operator<<(Lhs const &lhs, Rhs const &rhs) -> operations::flipped_mixed_primitive_dispatch_result_t< operations::LeftShift, Lhs, Rhs> { return operations::shift_left(lhs, rhs); } -template +template constexpr auto operator>>(Lhs const &lhs, Rhs const &rhs) -> operations::primitive_dispatch_result_t { return operations::shift_right(lhs, rhs); } -template constexpr auto operator>>(Lhs const &lhs, Rhs const &rhs) -> operations::mixed_primitive_dispatch_result_t>(Lhs const &lhs, Rhs const &rhs) } template + meta::primitive_type Rhs> constexpr auto operator>>(Lhs const &lhs, Rhs const &rhs) -> operations::flipped_mixed_primitive_dispatch_result_t< operations::RightShift, Lhs, Rhs> { return operations::shift_right(lhs, rhs); } -template +template constexpr auto operator&(Lhs const &lhs, Rhs const &rhs) -> operations::primitive_dispatch_result_t { return operations::bit_and(lhs, rhs); } -template constexpr auto operator&(Lhs const &lhs, Rhs const &rhs) -> operations::mixed_primitive_dispatch_result_t + meta::primitive_type Rhs> constexpr auto operator&(Lhs const &lhs, Rhs const &rhs) -> operations::flipped_mixed_primitive_dispatch_result_t< operations::BitwiseAnd, Lhs, Rhs> { return operations::bit_and(lhs, rhs); } -template +template constexpr auto operator|(Lhs const &lhs, Rhs const &rhs) -> operations::primitive_dispatch_result_t { return operations::bit_or(lhs, rhs); } -template constexpr auto operator|(Lhs const &lhs, Rhs const &rhs) -> operations::mixed_primitive_dispatch_result_t + meta::primitive_type Rhs> constexpr auto operator|(Lhs const &lhs, Rhs const &rhs) -> operations::flipped_mixed_primitive_dispatch_result_t< operations::BitwiseOr, Lhs, Rhs> { return operations::bit_or(lhs, rhs); } -template +template constexpr auto operator^(Lhs const &lhs, Rhs const &rhs) -> operations::primitive_dispatch_result_t { return operations::bit_xor(lhs, rhs); } -template constexpr auto operator^(Lhs const &lhs, Rhs const &rhs) -> operations::mixed_primitive_dispatch_result_t + meta::primitive_type Rhs> constexpr auto operator^(Lhs const &lhs, Rhs const &rhs) -> operations::flipped_mixed_primitive_dispatch_result_t< operations::BitwiseXor, Lhs, Rhs> { @@ -895,14 +896,14 @@ constexpr auto operator^(Lhs const &lhs, Rhs const &rhs) // Binary comparison operators -template +template constexpr auto operator==(Lhs const &lhs, Rhs const &rhs) -> operations::primitive_dispatch_result_t { return operations::equal(lhs, rhs); } -template constexpr auto operator==(Lhs const &lhs, Rhs const &rhs) -> operations::mixed_primitive_dispatch_result_t + meta::primitive_type Rhs> constexpr auto operator==(Lhs const &lhs, Rhs const &rhs) -> operations::flipped_mixed_primitive_dispatch_result_t { return operations::equal(lhs, rhs); } -template +template constexpr auto operator!=(Lhs const &lhs, Rhs const &rhs) -> operations::primitive_dispatch_result_t { return operations::not_equal(lhs, rhs); } -template constexpr auto operator!=(Lhs const &lhs, Rhs const &rhs) -> operations::mixed_primitive_dispatch_result_t + meta::primitive_type Rhs> constexpr auto operator!=(Lhs const &lhs, Rhs const &rhs) -> operations::flipped_mixed_primitive_dispatch_result_t< operations::NotEqual, Lhs, Rhs> { return operations::not_equal(lhs, rhs); } -template +template constexpr auto operator<=>(Lhs const &lhs, Rhs const &rhs) -> operations::three_way_dispatch_result_t { return operations::three_way_compare(lhs, rhs); } -template constexpr auto operator<=>(Lhs const &lhs, Rhs const &rhs) -> operations::mixed_three_way_dispatch_result_t { @@ -956,82 +957,83 @@ constexpr auto operator<=>(Lhs const &lhs, Rhs const &rhs) } template + meta::primitive_type Rhs> constexpr auto operator<=>(Lhs const &lhs, Rhs const &rhs) -> operations::flipped_mixed_three_way_dispatch_result_t { return operations::three_way_compare(lhs, rhs); } -template +template constexpr auto operator+=(Lhs &lhs, Rhs const &rhs) -> operations::primitive_dispatch_result_t { return operations::add_assign(lhs, rhs); } -template +template constexpr auto operator-=(Lhs &lhs, Rhs const &rhs) -> operations::primitive_dispatch_result_t { return operations::sub_assign(lhs, rhs); } -template +template constexpr auto operator*=(Lhs &lhs, Rhs const &rhs) -> operations::primitive_dispatch_result_t { return operations::mul_assign(lhs, rhs); } -template +template constexpr auto operator/=(Lhs &lhs, Rhs const &rhs) -> operations::primitive_dispatch_result_t { return operations::div_assign(lhs, rhs); } -template +template constexpr auto operator%=(Lhs &lhs, Rhs const &rhs) -> operations::primitive_dispatch_result_t { return operations::mod_assign(lhs, rhs); } -template +template constexpr auto operator<<=(Lhs &lhs, Rhs const &rhs) -> operations::primitive_dispatch_result_t { return operations::shift_left_assign(lhs, rhs); } -template +template constexpr auto operator>>=(Lhs &lhs, Rhs const &rhs) -> operations::primitive_dispatch_result_t { return operations::shift_right_assign(lhs, rhs); } -template +template constexpr auto operator&=(Lhs &lhs, Rhs const &rhs) -> operations::primitive_dispatch_result_t { return operations::bit_and_assign(lhs, rhs); } -template +template constexpr auto operator|=(Lhs &lhs, Rhs const &rhs) -> operations::primitive_dispatch_result_t { return operations::bit_or_assign(lhs, rhs); } -template +template constexpr auto operator^=(Lhs &lhs, Rhs const &rhs) -> operations::primitive_dispatch_result_t { return operations::bit_xor_assign(lhs, rhs); } } // namespace mcpplibs::primitives::operators + diff --git a/src/policy/impl.cppm b/src/policy/impl.cppm index ecd090a..df1299e 100644 --- a/src/policy/impl.cppm +++ b/src/policy/impl.cppm @@ -19,6 +19,79 @@ import mcpplibs.primitives.policy.traits; import mcpplibs.primitives.policy.handler; import mcpplibs.primitives.underlying.traits; +namespace mcpplibs::primitives::policy::details { + +template > +struct atomic_ref_alignment_compatible : std::false_type {}; + +template +struct atomic_ref_alignment_compatible + : std::bool_constant<(alignof(T) >= + std::atomic_ref::required_alignment)> {}; + +template +inline constexpr bool atomic_ref_compatible_v = + atomic_ref_alignment_compatible::value; + +template constexpr void assert_atomic_ref_compatible() { + static_assert(std::is_trivially_copyable_v, + "concurrency::handler atomic access requires trivially " + "copyable CommonRep"); + static_assert( + atomic_ref_alignment_compatible::value, + "concurrency::handler atomic access requires alignof(CommonRep) to " + "satisfy std::atomic_ref::required_alignment"); +} + +template +concept has_underlying_rep_not_equal = + underlying_type && + requires(T const &lhs, T const &rhs) { + { + underlying::traits>::to_rep(lhs) != + underlying::traits>::to_rep(rhs) + } -> std::convertible_to; + }; + +template +inline constexpr bool none_compare_exchange_available_v = + has_underlying_rep_not_equal; + +template +inline constexpr bool is_arithmetic_operation_v = + operations::op_has_capability_v; + +template +inline constexpr bool is_boolean_or_character_v = std_bool || std_char; + +template +inline constexpr bool rejects_arithmetic_for_boolean_or_character_v = + is_arithmetic_operation_v && + (is_boolean_or_character_v || is_boolean_or_character_v); + +template +inline constexpr bool has_non_void_common_rep_v = + has_common_rep && + !std::same_as, void>; + +template +inline constexpr bool has_same_underlying_category_v = + underlying::traits>::enabled && + underlying::traits>::enabled && + (underlying::traits>::kind == + underlying::traits>::kind); + +template +auto atomic_ref_load(T const &value, std::memory_order order) noexcept -> T { + assert_atomic_ref_compatible(); + // libc++ rejects std::atomic_ref; load through a non-mutating view. + auto &mutable_value = const_cast(value); + std::atomic_ref ref(mutable_value); + return ref.load(order); +} + +} // namespace mcpplibs::primitives::policy::details + export namespace mcpplibs::primitives::policy { namespace value { @@ -138,79 +211,6 @@ using error = error::throwing; using concurrency = concurrency::none; } // namespace defaults -namespace details { - -template > -struct atomic_ref_alignment_compatible : std::false_type {}; - -template -struct atomic_ref_alignment_compatible - : std::bool_constant<(alignof(T) >= - std::atomic_ref::required_alignment)> {}; - -template -inline constexpr bool atomic_ref_compatible_v = - atomic_ref_alignment_compatible::value; - -template constexpr void assert_atomic_ref_compatible() { - static_assert(std::is_trivially_copyable_v, - "concurrency::handler atomic access requires trivially " - "copyable CommonRep"); - static_assert( - atomic_ref_alignment_compatible::value, - "concurrency::handler atomic access requires alignof(CommonRep) to " - "satisfy std::atomic_ref::required_alignment"); -} - -template -concept has_underlying_rep_not_equal = - underlying_type && - requires(T const &lhs, T const &rhs) { - { - underlying::traits>::to_rep(lhs) != - underlying::traits>::to_rep(rhs) - } -> std::convertible_to; - }; - -template -inline constexpr bool none_compare_exchange_available_v = - has_underlying_rep_not_equal; - -template -inline constexpr bool is_arithmetic_operation_v = - operations::op_has_capability_v; - -template -inline constexpr bool is_boolean_or_character_v = std_bool || std_char; - -template -inline constexpr bool rejects_arithmetic_for_boolean_or_character_v = - is_arithmetic_operation_v && - (is_boolean_or_character_v || is_boolean_or_character_v); - -template -inline constexpr bool has_non_void_common_rep_v = - has_common_rep && - !std::same_as, void>; - -template -inline constexpr bool has_same_underlying_category_v = - underlying::traits>::enabled && - underlying::traits>::enabled && - (underlying::traits>::kind == - underlying::traits>::kind); - -template -auto atomic_ref_load(T const &value, std::memory_order order) noexcept -> T { - assert_atomic_ref_compatible(); - // libc++ rejects std::atomic_ref; load through a non-mutating view. - auto &mutable_value = const_cast(value); - std::atomic_ref ref(mutable_value); - return ref.load(order); -} - -} // namespace details - // Default protocol specializations. template struct type::handler { diff --git a/src/primitive/traits.cppm b/src/primitive/traits.cppm index e1fb5b4..8ecb4fe 100644 --- a/src/primitive/traits.cppm +++ b/src/primitive/traits.cppm @@ -1,5 +1,6 @@ module; #include +#include export module mcpplibs.primitives.primitive.traits; @@ -9,6 +10,26 @@ import mcpplibs.primitives.policy.impl; import mcpplibs.primitives.policy.utility; import mcpplibs.primitives.underlying.traits; +namespace mcpplibs::primitives::meta::details { + +template struct primitive_traits_impl; + +template +struct primitive_traits_impl> { + using value_type = T; + using policies = std::tuple; + using value_policy = + policy::resolve_policy_t; + using type_policy = + policy::resolve_policy_t; + using error_policy = + policy::resolve_policy_t; + using concurrency_policy = + policy::resolve_policy_t; +}; + +} // namespace mcpplibs::primitives::meta::details + // Public API exported from this module. export namespace mcpplibs::primitives::meta { using policy_category = policy::category; @@ -27,22 +48,46 @@ using default_policies = std::tuple; -template struct traits; +template +using traits = details::primitive_traits_impl>; -template -struct traits> { - using value_type = T; - using policies = std::tuple; - using value_policy = - policy::resolve_policy_t; - using type_policy = - policy::resolve_policy_t; - using error_policy = - policy::resolve_policy_t; - using concurrency_policy = - policy::resolve_policy_t; +template +concept primitive_type = requires { + typename traits::value_type; + typename traits::policies; + typename traits::value_policy; + typename traits::type_policy; + typename traits::error_policy; + typename traits::concurrency_policy; }; +template +concept boolean = + primitive_type && + (underlying::traits::value_type>::kind == + underlying::category::boolean); + +template +concept character = + primitive_type && + (underlying::traits::value_type>::kind == + underlying::category::character); + +template +concept integer = + primitive_type && + (underlying::traits::value_type>::kind == + underlying::category::integer); + +template +concept floating = + primitive_type && + (underlying::traits::value_type>::kind == + underlying::category::floating); + +template +concept numeric = integer || floating; + } // namespace mcpplibs::primitives::meta // Backward-compatible aliases for existing downstream users. diff --git a/tests/basic/test_underlying.cpp b/tests/basic/test_underlying.cpp index 9a5c9d9..8ab7540 100644 --- a/tests/basic/test_underlying.cpp +++ b/tests/basic/test_underlying.cpp @@ -480,6 +480,63 @@ TEST(PrimitiveTraitsTest, LegacyPrimitiveTraitsNamespaceAliasesRemainAvailable) SUCCEED(); } +TEST(PrimitiveTraitsTest, MetaTraitsExposeValueTypeAndPrimitiveMetadata) { + using value_t = mcpplibs::primitives::primitive< + short, mcpplibs::primitives::policy::value::checked, + mcpplibs::primitives::policy::type::compatible, + mcpplibs::primitives::policy::error::expected, + mcpplibs::primitives::policy::concurrency::fenced>; + using traits_t = mcpplibs::primitives::meta::traits; + + static_assert( + std::same_as); + static_assert(std::same_as); + static_assert(std::same_as); + static_assert(std::same_as); + static_assert(std::same_as); + static_assert(std::same_as); + + SUCCEED(); +} + +TEST(PrimitiveTraitsTest, MetaPrimitiveConceptsMatchUnderlyingCategory) { + using boolean_t = mcpplibs::primitives::primitive; + using character_t = mcpplibs::primitives::primitive; + using integer_t = mcpplibs::primitives::primitive; + using floating_t = mcpplibs::primitives::primitive; + + static_assert(mcpplibs::primitives::meta::primitive_type); + static_assert(mcpplibs::primitives::meta::primitive_type); + static_assert(!mcpplibs::primitives::meta::primitive_type); + + static_assert(mcpplibs::primitives::meta::boolean); + static_assert(!mcpplibs::primitives::meta::boolean); + + static_assert(mcpplibs::primitives::meta::character); + static_assert(!mcpplibs::primitives::meta::character); + + static_assert(mcpplibs::primitives::meta::integer); + static_assert(!mcpplibs::primitives::meta::integer); + + static_assert(mcpplibs::primitives::meta::floating); + static_assert(!mcpplibs::primitives::meta::floating); + + static_assert(mcpplibs::primitives::meta::numeric); + static_assert(mcpplibs::primitives::meta::numeric); + static_assert(!mcpplibs::primitives::meta::numeric); + static_assert(!mcpplibs::primitives::meta::numeric); + static_assert(!mcpplibs::primitives::meta::numeric); + + EXPECT_TRUE((mcpplibs::primitives::meta::boolean)); + EXPECT_TRUE((mcpplibs::primitives::meta::character)); + EXPECT_TRUE((mcpplibs::primitives::meta::integer)); + EXPECT_TRUE((mcpplibs::primitives::meta::floating)); +} + TEST(PrimitiveTraitsTest, UnderlyingTypeRequiresValidRepTypeAndCategoryConsistency) { EXPECT_TRUE((mcpplibs::primitives::underlying::traits::enabled));