From f8da4083f8016b6ff3db566ee80ac819fe1ae9ae Mon Sep 17 00:00:00 2001 From: FrozenlemonTee <1115306170@qq.com> Date: Sat, 21 Mar 2026 18:12:40 +0800 Subject: [PATCH 01/28] feat: Add compound assignment operations --- src/operations/operators.cppm | 79 ++++++++++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/src/operations/operators.cppm b/src/operations/operators.cppm index 9ecb6e0..3955093 100644 --- a/src/operations/operators.cppm +++ b/src/operations/operators.cppm @@ -10,6 +10,7 @@ import mcpplibs.primitives.operations.impl; import mcpplibs.primitives.primitive.impl; import mcpplibs.primitives.primitive.traits; import mcpplibs.primitives.policy.handler; +import mcpplibs.primitives.underlying.traits; export namespace mcpplibs::primitives::operations { @@ -79,6 +80,52 @@ constexpr auto not_equal(Lhs const &lhs, Rhs const &rhs) return apply(lhs, rhs); } +template +constexpr auto apply_assign(Lhs &lhs, Rhs const &rhs) + -> primitive_dispatch_result_t { + using lhs_value_type = + typename mcpplibs::primitives::traits::primitive_traits::value_type; + using lhs_rep = typename underlying::traits::rep_type; + + auto out = apply(lhs, rhs); + if (!out.has_value()) { + return std::unexpected(out.error()); + } + + auto const assigned_rep = static_cast(out->load()); + lhs.store(underlying::traits::from_rep(assigned_rep)); + 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); +} + } // namespace mcpplibs::primitives::operations export namespace mcpplibs::primitives::operators { @@ -127,4 +174,34 @@ constexpr auto operator!=(Lhs const &lhs, Rhs const &rhs) return operations::not_equal(lhs, rhs); } -} // namespace mcpplibs::primitives::operators \ No newline at end of file +template +constexpr auto operator+=(Lhs &lhs, Rhs const &rhs) + -> operations::primitive_dispatch_result_t { + return operations::add_assign(lhs, rhs); +} + +template +constexpr auto operator-=(Lhs &lhs, Rhs const &rhs) + -> operations::primitive_dispatch_result_t { + return operations::sub_assign(lhs, rhs); +} + +template +constexpr auto operator*=(Lhs &lhs, Rhs const &rhs) + -> operations::primitive_dispatch_result_t { + return operations::mul_assign(lhs, rhs); +} + +template +constexpr auto operator/=(Lhs &lhs, Rhs const &rhs) + -> operations::primitive_dispatch_result_t { + return operations::div_assign(lhs, rhs); +} + +} // namespace mcpplibs::primitives::operators From ad91b2544c3a287788efbefb20a5a078b0f9102f Mon Sep 17 00:00:00 2001 From: FrozenlemonTee <1115306170@qq.com> Date: Sat, 21 Mar 2026 18:12:54 +0800 Subject: [PATCH 02/28] feat: Add unit tests for compound assignment operations --- tests/basic/test_operations.cpp | 41 +++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/tests/basic/test_operations.cpp b/tests/basic/test_operations.cpp index b894775..5a44f61 100644 --- a/tests/basic/test_operations.cpp +++ b/tests/basic/test_operations.cpp @@ -480,6 +480,47 @@ TEST(OperationsTest, OperatorPlusDelegatesToDispatcher) { EXPECT_EQ(result->value(), 15); } +TEST(OperationsTest, CompoundAssignmentOperatorsMutateLhsOnSuccess) { + using namespace mcpplibs::primitives::operators; + using value_t = primitive; + + auto value = value_t{20}; + + auto add_result = (value += value_t{22}); + ASSERT_TRUE(add_result.has_value()); + EXPECT_EQ(value.load(), 42); + EXPECT_EQ(add_result->value(), 42); + + auto sub_result = (value -= value_t{2}); + ASSERT_TRUE(sub_result.has_value()); + EXPECT_EQ(value.load(), 40); + EXPECT_EQ(sub_result->value(), 40); + + auto mul_result = (value *= value_t{3}); + ASSERT_TRUE(mul_result.has_value()); + EXPECT_EQ(value.load(), 120); + EXPECT_EQ(mul_result->value(), 120); + + auto div_result = (value /= value_t{4}); + ASSERT_TRUE(div_result.has_value()); + EXPECT_EQ(value.load(), 30); + EXPECT_EQ(div_result->value(), 30); +} + +TEST(OperationsTest, CompoundAssignmentKeepsLhsWhenOperationFails) { + using namespace mcpplibs::primitives::operators; + using value_t = + primitive; + + auto value = value_t{100}; + + auto result = (value /= value_t{0}); + + ASSERT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), policy::error::kind::divide_by_zero); + EXPECT_EQ(value.load(), 100); +} + TEST(OperationsTest, ThrowErrorPolicyThrowsException) { using value_t = primitive; From a67d35ac774572b2347ff812b7708b5ab3096abb Mon Sep 17 00:00:00 2001 From: FrozenlemonTee <1115306170@qq.com> Date: Sat, 21 Mar 2026 18:22:52 +0800 Subject: [PATCH 03/28] feat: Add ThreeWayCompare operation --- src/operations/impl.cppm | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/operations/impl.cppm b/src/operations/impl.cppm index deecfc6..338a3f3 100644 --- a/src/operations/impl.cppm +++ b/src/operations/impl.cppm @@ -10,6 +10,7 @@ struct Multiplication {}; struct Division {}; struct Equal {}; struct NotEqual {}; +struct ThreeWayCompare {}; template <> struct traits { using op_tag = Addition; @@ -59,4 +60,12 @@ template <> struct traits { static constexpr auto capability_mask = capability::comparison; }; -} // namespace mcpplibs::primitives::operations \ No newline at end of file +template <> struct traits { + using op_tag = ThreeWayCompare; + + static constexpr bool enabled = true; + static constexpr auto arity = dimension::binary; + static constexpr auto capability_mask = capability::comparison; +}; + +} // namespace mcpplibs::primitives::operations From 17b46dd804f6b540fc320b004ec8f96ad1bd7b2f Mon Sep 17 00:00:00 2001 From: FrozenlemonTee <1115306170@qq.com> Date: Sat, 21 Mar 2026 18:23:32 +0800 Subject: [PATCH 04/28] refactor: Binding policy to ThreeWayCompare and impl ThreeWayCompare --- src/operations/invoker.cppm | 86 +++++++++++++++++++++++++++++++++++ src/operations/operators.cppm | 77 +++++++++++++++++++++++++++++++ 2 files changed, 163 insertions(+) diff --git a/src/operations/invoker.cppm b/src/operations/invoker.cppm index 0772329..569d04c 100644 --- a/src/operations/invoker.cppm +++ b/src/operations/invoker.cppm @@ -1,6 +1,7 @@ module; #include +#include #include #include #include @@ -221,6 +222,61 @@ constexpr auto compare_not_equal(T lhs, T rhs) -> policy::value::decision { "common type"); } +template +constexpr auto compare_three_way(T lhs, T rhs) -> policy::value::decision { + policy::value::decision out{}; + if constexpr (!(requires { T{0}; T{1}; T{2}; T{3}; })) { + return make_error( + policy::error::kind::unspecified, + "three-way comparison codes are not representable for common type"); + } + + if constexpr (std::same_as, bool>) { + return make_error( + policy::error::kind::unspecified, + "three-way comparison is not representable for bool common type"); + } else if constexpr (requires { lhs <=> rhs; }) { + auto const cmp = lhs <=> rhs; + out.has_value = true; + + if (cmp < 0) { + out.value = T{0}; + return out; + } + if (cmp > 0) { + out.value = T{2}; + return out; + } + + if constexpr (std::same_as, + std::partial_ordering>) { + if (cmp == std::partial_ordering::unordered) { + out.value = T{3}; + return out; + } + } + + out.value = T{1}; + return out; + } else if constexpr (requires { lhs < rhs; lhs > rhs; }) { + out.has_value = true; + if (lhs < rhs) { + out.value = T{0}; + return out; + } + if (lhs > rhs) { + out.value = T{2}; + return out; + } + out.value = T{1}; + return out; + } + + return make_error(policy::error::kind::unspecified, + "three-way comparison not supported for negotiated " + "common type"); +} + template constexpr auto unchecked_add(T lhs, T rhs) -> policy::value::decision { policy::value::decision out{}; @@ -626,6 +682,36 @@ struct op_binding { } }; +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value::decision { + return details::compare_three_way(lhs, rhs); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value::decision { + return details::compare_three_way(lhs, rhs); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value::decision { + return details::compare_three_way(lhs, rhs); + } +}; + template concept op_binding_available = requires { requires operation; diff --git a/src/operations/operators.cppm b/src/operations/operators.cppm index 3955093..c3d6789 100644 --- a/src/operations/operators.cppm +++ b/src/operations/operators.cppm @@ -1,6 +1,9 @@ module; +#include #include +#include +#include export module mcpplibs.primitives.operations.operators; @@ -14,6 +17,47 @@ import mcpplibs.primitives.underlying.traits; export namespace mcpplibs::primitives::operations { +namespace details { +template struct three_way_ordering { + using type = std::strong_ordering; +}; + +template +struct three_way_ordering< + CommonRep, + std::void_t() <=> + std::declval())>> { + using type = std::remove_cvref_t() <=> + std::declval())>; +}; + +template +using three_way_ordering_t = typename three_way_ordering::type; + +template +constexpr auto decode_three_way_code(CommonRep const &code) -> Ordering { + if (code == static_cast(0)) { + return Ordering::less; + } + if (code == static_cast(2)) { + return Ordering::greater; + } + + if constexpr (std::is_same_v) { + if (code == static_cast(3)) { + return std::partial_ordering::unordered; + } + return std::partial_ordering::equivalent; + } + + if constexpr (std::is_same_v) { + return std::strong_ordering::equal; + } + + return Ordering::equivalent; +} +} // namespace details + template using primitive_dispatch_result_t = std::expected< @@ -22,6 +66,14 @@ using primitive_dispatch_result_t = std::expected< typename mcpplibs::primitives::traits::primitive_traits::policies>, ErrorPayload>; +template +using three_way_dispatch_result_t = std::expected< + details::three_way_ordering_t< + typename dispatcher_meta::common_rep>, + ErrorPayload>; + template constexpr auto apply(Lhs const &lhs, Rhs const &rhs) @@ -80,6 +132,24 @@ constexpr auto not_equal(Lhs const &lhs, Rhs const &rhs) return apply(lhs, rhs); } +template +constexpr auto three_way_compare(Lhs const &lhs, Rhs const &rhs) + -> three_way_dispatch_result_t { + using common_rep = + typename dispatcher_meta::common_rep; + using ordering = + typename three_way_dispatch_result_t::value_type; + + auto const raw = dispatch(lhs, rhs); + if (!raw.has_value()) { + return std::unexpected(raw.error()); + } + + return details::decode_three_way_code(*raw); +} + template constexpr auto apply_assign(Lhs &lhs, Rhs const &rhs) @@ -174,6 +244,13 @@ constexpr auto operator!=(Lhs const &lhs, Rhs const &rhs) return operations::not_equal(lhs, rhs); } +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 &lhs, Rhs const &rhs) From 6b9ac677cbcbb37d5e689883f67a2144aeb58883 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee <1115306170@qq.com> Date: Sat, 21 Mar 2026 18:23:49 +0800 Subject: [PATCH 05/28] test: Add unit tests for ThreeWayCompare operation --- tests/basic/test_operations.cpp | 51 +++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/tests/basic/test_operations.cpp b/tests/basic/test_operations.cpp index 5a44f61..3e5e7c3 100644 --- a/tests/basic/test_operations.cpp +++ b/tests/basic/test_operations.cpp @@ -1,6 +1,8 @@ #include +#include #include #include +#include #include #include #include @@ -480,6 +482,55 @@ TEST(OperationsTest, OperatorPlusDelegatesToDispatcher) { EXPECT_EQ(result->value(), 15); } +TEST(OperationsTest, ThreeWayCompareReturnsStrongOrderingForIntegers) { + using namespace mcpplibs::primitives::operators; + using value_t = + primitive; + + auto const less = (value_t{3} <=> value_t{7}); + auto const equal = (value_t{5} <=> value_t{5}); + auto const greater = (value_t{9} <=> value_t{1}); + + static_assert( + std::is_same_v); + + ASSERT_TRUE(less.has_value()); + ASSERT_TRUE(equal.has_value()); + ASSERT_TRUE(greater.has_value()); + EXPECT_EQ(*less, std::strong_ordering::less); + EXPECT_EQ(*equal, std::strong_ordering::equal); + EXPECT_EQ(*greater, std::strong_ordering::greater); +} + +TEST(OperationsTest, ThreeWayCompareReturnsPartialOrderingForFloatingPoint) { + using namespace mcpplibs::primitives::operators; + using value_t = + primitive; + + auto const less = (value_t{1.0} <=> value_t{2.0}); + auto const nan = std::numeric_limits::quiet_NaN(); + auto const unordered = (value_t{nan} <=> value_t{1.0}); + + static_assert(std::is_same_v); + + ASSERT_TRUE(less.has_value()); + ASSERT_TRUE(unordered.has_value()); + EXPECT_EQ(*less, std::partial_ordering::less); + EXPECT_EQ(*unordered, std::partial_ordering::unordered); +} + +TEST(OperationsTest, ThreeWayCompareOnBoolReturnsError) { + using namespace mcpplibs::primitives::operators; + using value_t = + primitive; + + auto const result = (value_t{false} <=> value_t{true}); + + ASSERT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), policy::error::kind::unspecified); +} + TEST(OperationsTest, CompoundAssignmentOperatorsMutateLhsOnSuccess) { using namespace mcpplibs::primitives::operators; using value_t = primitive; From 4d69d38a3da74a01b5252344e951ad4cfb805556 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee <1115306170@qq.com> Date: Sat, 21 Mar 2026 20:09:20 +0800 Subject: [PATCH 06/28] test: Add unit tests --- tests/basic/test_operations.cpp | 35 +++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/basic/test_operations.cpp b/tests/basic/test_operations.cpp index 3e5e7c3..994bf2f 100644 --- a/tests/basic/test_operations.cpp +++ b/tests/basic/test_operations.cpp @@ -572,6 +572,41 @@ TEST(OperationsTest, CompoundAssignmentKeepsLhsWhenOperationFails) { EXPECT_EQ(value.load(), 100); } +TEST(OperationsTest, CompoundAssignmentSupportsMixedTypesWithCompatibleTypePolicy) { + using namespace mcpplibs::primitives::operators; + + using lhs_t = primitive; + using rhs_t = primitive; + + auto value = lhs_t{10}; + + auto add_result = (value += rhs_t{32}); + + ASSERT_TRUE(add_result.has_value()); + EXPECT_EQ(value.load(), 42); + EXPECT_EQ(add_result->value(), 42); +} + +TEST(OperationsTest, + CompoundAssignmentKeepsLhsOnMixedTypeOverflowWithCompatibleTypePolicy) { + using namespace mcpplibs::primitives::operators; + + using lhs_t = primitive; + using rhs_t = primitive; + + auto value = lhs_t{static_cast(32000)}; + + auto add_result = (value += rhs_t{1000}); + + ASSERT_FALSE(add_result.has_value()); + EXPECT_EQ(add_result.error(), policy::error::kind::overflow); + EXPECT_EQ(value.load(), static_cast(32000)); +} + TEST(OperationsTest, ThrowErrorPolicyThrowsException) { using value_t = primitive; From 812ba1ac644015adfa8142be2c13c6a6496a454e Mon Sep 17 00:00:00 2001 From: FrozenlemonTee <1115306170@qq.com> Date: Sat, 21 Mar 2026 22:13:30 +0800 Subject: [PATCH 07/28] docs: Update plan for agent --- .github/copilot-instructions.md | 3 +- .../plan-policyBehaviorProtocol.prompt.md | 173 ++++++------------ 2 files changed, 52 insertions(+), 124 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 5c1b51b..2b8024d 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -14,8 +14,7 @@ ## 正在进行中的计划 -- 计划目录:`../.github/prompts/` -- 当前计划:`../.github/prompts/plan-policyBehaviorProtocol.prompt.md` +- 当前计划目录:`../.github/prompts/` ## 说明 diff --git a/.github/prompts/plan-policyBehaviorProtocol.prompt.md b/.github/prompts/plan-policyBehaviorProtocol.prompt.md index 7bc994b..37be9bc 100644 --- a/.github/prompts/plan-policyBehaviorProtocol.prompt.md +++ b/.github/prompts/plan-policyBehaviorProtocol.prompt.md @@ -1,137 +1,66 @@ -## Plan: Policy 行为协议与分发规范 +## Plan: 策略组一致性 + Primitive/Underlying 混合操作(增量) -基于现有 traits/concept 架构,采用“policy handler 协议 + 固定 dispatcher + primitive 路由层”的设计。type 协商前移到编译期静态决议,运行期 dispatcher 固定为三层并按外到内调用:concurrency -> value(已知 common type)-> error。第一阶段覆盖四则运算(加减乘除),错误通道统一为 expected 返回;跨底层类型运算由 type policy 在编译期决定“是否允许 + common type”;value 层负责溢出判定与值调整/错误下放且需要接收 concurrency 层注入;error 层按策略执行最终错误处理。用户扩展边界限定为仅可特化 policy handler,以保持上层接口可复用、低耦合且强约束。 +本计划仅覆盖新增规则与新增重载,不重复已完成的分层协议与分发基建。 -**Steps** -1. 明确协议核心抽象(阻塞后续步骤) -2. 在 policy 层定义行为协议接口(依赖步骤 1) -3. 在 operations 层建立统一分发入口(依赖步骤 2) -4. 在 primitive 层实现运算路由与返回类型统一(依赖步骤 3) -5. 为跨类型运算定义 type policy 协商规则(依赖步骤 4,部分可与步骤 6 并行) -6. 建立编译期约束与诊断信息(依赖步骤 2,可并行于步骤 5) -7. 增加测试矩阵与示例验证(依赖步骤 4、5、6) -8. 文档化协议与扩展边界(依赖步骤 7) +### A. 已确认规则(实现目标) +1. 单个 `primitive` 的策略组规则保持不变 + - 同类别策略标签重复时,编译期报错。 -**Phase 1: 协议基线(接口层)** -1. 在 [src/policy/traits.cppm](src/policy/traits.cppm) 复用 category 与 policy_type 概念,新增“行为协议概念”所需的基础类型约束(仅声明契约,不放具体行为)。 -2. 在 [src/policy/impl.cppm](src/policy/impl.cppm) 保留现有标签与 traits 特化,补充“策略能力声明位”对应的 traits 扩展位(用于编译期判定策略是否实现某 operation)。 -3. 新增 policy 协议模块(建议命名 mcpplibs.primitives.policy.handler),内容包含: -4. `policy_handler` 主模板(默认禁用) -5. `policy_handler_available` 概念(用于约束上层调用) -6. 标准返回别名(统一为 expected 语义) -7. 明确用户扩展点:只允许特化 handler,不开放分发规则特化。 +2. 两个 `primitive` 的策略组合并规则 + - 先按默认策略补全四类策略(`value/type/error/concurrency`)。 + - 补全后两边策略组必须完全一致(忽略声明顺序),否则编译期报错。 -**Phase 2: Operation 分发层(固定三层运行期链路)** -1. 在 [src/operations/impl.cppm](src/operations/impl.cppm) 维持 operation tag + arity 元数据,增加“可分发 operation”约束入口(例如合法 operation 概念)。 -2. 在 [src/operations/operators.cppm](src/operations/operators.cppm) 将 type 协商前移为编译期预处理,并实现运行期固定 dispatcher: -3. concurrency handler:负责并发包装(如加锁/解锁、内存屏障注入;具体机制可后续细化),并向 value 层注入并发执行上下文。 -4. value handler:接收已决议的 common type + concurrency 注入上下文,在 common type 上执行值域与溢出判定,决定“本层修正结果值”或“下放错误处理请求”。 -5. error handler:接收上层错误请求并按策略完成最终处理,统一映射到 expected 错误值。 -6. dispatcher 对外统一返回 expected,且不依赖具体 primitive 别名,仅依赖 traits 协议。 +3. 两个 `primitive` 底层类型不同时的二元操作规则 + - 先满足规则 2。 + - `type::strict`:编译期拒绝。 + - `type::compatible`:两侧 `underlying category` 必须一致,且 `common_rep_traits` 可用且推导结果非 `void`。 + - `type::transparent`:忽略 `category`,但 `common_rep_traits` 必须可用且推导结果非 `void`。 -**Phase 3: Primitive 路由层(上层调用面)** -1. 在 [src/primitive/traits.cppm](src/primitive/traits.cppm) 复用 primitive_traits 与 resolve_policy_t,新增: -2. 跨类型运算协商 trait(由 type policy 决定是否允许、结果 common type) -3. 结果 primitive 重建工具(保持策略传播一致) -4. 在 primitive 对外运算入口中接入 operations dispatcher,覆盖加减乘除四个操作。 -5. 返回类型统一为 expected(成功值为 primitive,失败值为错误域类型)。 +4. 不新增策略标签 + - 维持 `compatible` / `transparent` 现有策略集合,不引入新 policy。 -**Phase 4: 聚合导出与契约稳定** -1. 在 [src/primitives.cppm](src/primitives.cppm) 增加新协议模块与分发层导出,保持聚合入口一致。 -2. 在 [src/policy/utility.cppm](src/policy/utility.cppm) 保持 common policy 选择逻辑不变,仅补充与 handler 可用性相关的辅助别名(若需要)。 +### B. 新增范围:Primitive 与 Underlying 混合作为二元操作数 +1. 在 `apply/operation` 层新增 `underlying` 参与重载 + - 支持 `(primitive, underlying)`。 + - 若是自由函数,必须同时支持 `(underlying, primitive)`(左右位置都可用)。 -**Phase 5: 验证与回归** -1. 在 [tests](tests) 新增 policy 行为协议测试: -2. handler 未实现时应在编译期失败(约束强) -3. 四则运算均返回 expected -4. 相同 type policy 下跨类型协商正确/错误路径正确 -5. 在 [examples/basic.cpp](examples/basic.cpp) 增加最小示例: -6. 默认策略下四则运算 -7. 自定义 policy handler 的单点扩展示例(仅特化 handler) +2. 返回类型约束 + - 算术类型操作结果始终返回 `primitive`(不返回裸 `underlying`)。 +3. 策略选择约束 + - 混合操作中仅有一个 `primitive` 时,`type` 策略取该 `primitive` 的策略。 + - 其余策略(`value/error/concurrency`)同样取该 `primitive`,保持策略来源单一。 -**Interface Contracts(最小签名清单)** -1. Dispatcher 总入口(放在 [src/operations/operators.cppm](src/operations/operators.cppm)): -2. 输入:OperationTag、Lhs Primitive、Rhs Primitive、四类 policy(由 primitive_traits 解析)。 -3. 输出:expected。 -4. 约束:type 协商在编译期完成;运行期调用顺序固定为 concurrency -> value -> error。 -5. 编译期 type 协商合约(静态层,不进入运行期链路): -6. 输入:操作标签、Lhs/Rhs 静态类型信息、type policy。 -7. 输出:type_decision(is_allowed、common_type、diagnostic_id)。 -8. 责任:拒绝非法运算并确定 common_type;其结果作为运行期 value 层输入前提。 -9. concurrency handler 合约(放在 policy handler 模块): -10. 输入:operation_context、下一层 continuation、type_decision。 -11. 输出:concurrency_injection + 下一层 expected 透传能力。 -12. 责任:加锁/解锁、内存屏障、线程可见性包装;向 value 层注入并发执行上下文,不做值域判定。 -13. value handler 合约: -14. 输入:common_type 下的 lhs/rhs、operation_context、concurrency_injection。 -15. 输出:二选一路径: -16. 路径 A:直接给出成功值(已修正或原值)。 -17. 路径 B:下发 error_request(包含错误类别、上下文、候选回退值)。 -18. 责任:执行溢出/下溢/除零等值域检查,并决定是否本层修正。 -19. error handler 合约: -20. 输入:error_request + operation_context。 -21. 输出:expected 的最终错误分支或恢复成功值分支。 -22. 责任:按 error policy 落地处理(例如映射错误域、终止、抛错转译为 expected 错误值)。 -23. 跨层数据结构建议: -24. operation_context:封装 op tag、源 primitive traits、policy 句柄、调试标签。 -25. type_decision:封装 is_allowed、common_type、diagnostic_id。 -26. concurrency_injection:封装 guard_handle、memory_order/屏障策略、可选调度标签。 -27. value_decision:封装 has_value、value、error_request。 -28. error_request:封装 error_kind、reason、operation_context、可选 fallback。 -29. 扩展边界: -30. 用户只可特化三类运行期 handler(concurrency/value/error)与静态 type 协商策略实现,不可替换 dispatcher 顺序、不可改动跨层数据结构主骨架。 +4. 底层类型推导约束 + - 混合操作的 `underlying` 推导与“两侧都是 primitive”的规则一致: + - 走相同的 `type` 协商路径,使用同一套 `common_rep_traits` 可用性与非 `void` 判定。 -**Relevant files** -- [src/policy/traits.cppm](src/policy/traits.cppm) — 复用并扩展 policy 概念边界,承载行为协议约束基础。 -- [src/policy/impl.cppm](src/policy/impl.cppm) — 保持标签定义,补充策略能力元信息挂点。 -- [src/policy/utility.cppm](src/policy/utility.cppm) — 与 common_policies 选择逻辑对齐,必要时提供 handler 检查辅助。 -- [src/operations/impl.cppm](src/operations/impl.cppm) — operation 元数据与分发前置约束。 -- [src/operations/operators.cppm](src/operations/operators.cppm) — dispatcher 核心实现位置。 -- [src/primitive/traits.cppm](src/primitive/traits.cppm) — policy 解析与跨类型协商 trait 的主落点。 -- [src/primitives.cppm](src/primitives.cppm) — 对外聚合导出。 -- [tests](tests) — 编译期约束与行为结果验证。 -- [examples/basic.cpp](examples/basic.cpp) — API 用法与扩展示例。 +### C. 代码改动计划 +1. `src/operations/dispatcher.cppm` + - 增加“跨 primitive 策略组补全后一致”的编译期断言。 + - 增加 `compatible` 下 `category` 一致性判定(与 `common_rep` 可用性判定协同)。 + - 补齐 `common_rep` 非 `void` 的显式断言。 -**Verification** -1. 编译期契约验证: -2. 未提供对应 policy_handler 的 operation 调用必须报清晰静态断言。 -3. type policy 禁止的跨类型运算必须在编译期拒绝。 -4. 固定链路验证: -5. type 协商必须在编译期完成,运行期 dispatcher 必须按 concurrency -> value -> error 顺序调用,禁止跳层与重排。 -6. value 层必须验证“接收 concurrency 注入”这一前提生效。 -7. value 层“值修正”与“错误下放”两条路径都需覆盖测试。 -8. 行为一致性验证: -9. 四则运算对默认策略与自定义策略均返回 expected。 -10. 失败路径(溢出/除零/不兼容类型)按 error policy 语义编码到 expected 错误值。 -11. unchecked_value 路径验证: -12. 不触发 error policy,保持原生算术语义(含 UB 风险)并通过测试明确边界。 -13. 导出稳定性验证: -14. 通过聚合模块导入可直接访问 policy 协议、operation 分发、primitive 运算。 -15. 运行 tests 与示例构建,确认无回归。 +2. `src/operations/operators.cppm` + - 为自由函数 `apply/add/sub/mul/div/equal/not_equal/three_way_compare` 增加混合重载: + - `(primitive, underlying)` 与 `(underlying, primitive)`。 + - 保证非交换操作(如 `sub/div/<=>`)保留正确操作数顺序语义。 -**Decisions** -- 错误通道:默认路径统一返回 expected。 -- dispatcher 形态:type 协商前移到编译期,运行期固定三层链路 concurrency -> value -> error,不开放顺序重排。 -- 跨底层类型运算:由 type policy 在编译期决定可行性与 common type。 -- value 层职责:判定溢出并决定“本层值修正”或“下放 error 层处理”。 -- unchecked_value 语义:不做错误处理,不调用 error policy,行为尽量贴近原生 C/C++(包含 UB 风险)。 -- 扩展边界:仅开放 policy handler 特化,不开放分发规则。 -- 第一阶段覆盖范围:Addition/Subtraction/Multiplication/Division 全部纳入。 -- In scope:policy 行为协议、operation 分发、primitive 路由、测试与文档。 -- Out of scope:并发策略的运行时同步原语实现细节(atomic/lock-free/锁策略具体执行体)。 +3. `src/primitive/impl.cppm`(如需) + - 若存在 primitive access 的自由函数入口,同步补齐混合重载的左右位置版本。 + - 成员函数形式的 access API 不做左右重载扩展。 -## Underlying Bridge Execution Contract (Runtime) +4. `tests/basic/test_operations.cpp` + - 新增混合重载测试: + - `(primitive, underlying)` 与 `(underlying, primitive)` 都可编译并语义正确。 + - 算术结果类型恒为 `primitive`。 + - 策略组不一致时编译期失败。 + - `strict/compatible/transparent` 下的跨底层类型判定符合规则 A-3。 -1. `dispatch` 在 value 阶段前统一执行 `to_rep`,将原始 value 映射到可协商的 `rep_type`。 -2. `type_handler` 的协商对象是 `lhs_rep/rhs_rep`,而非原始 value type。 -3. 若任一输入 `is_valid_rep(...) == false`,立即构造 `runtime_error_kind::domain_error` 并进入 error policy。 -4. 通过校验后执行 `from_rep -> to_rep` 规范化,再进入 value handler 与 op binding。 -5. 当前结果值仍按 `common_rep` 回传;comparison 最小闭环采用 `0/1` 表示(`static_cast(bool)`)。 -6. 本契约不改变 dispatcher 链路顺序,也不改变错误枚举体系。 +### D. 验收标准 +1. 所有新增约束均在编译期可判定,不引入运行期兜底分支。 +2. 混合操作重载完整覆盖左右操作数位置(自由函数)。 +3. 算术混合操作返回类型恒为 `primitive`。 +4. 混合操作的 `type` 策略来源唯一且可预测(取 primitive 操作数)。 +5. 与现有行为无回归,新增正反测试通过。 -**Further Considerations** -1. expected 的错误载体类型建议先统一为轻量错误枚举,再逐步演进到可扩展错误域,以减少首版模板复杂度。 -2. type policy 的 common type 规则建议先采用“显式白名单 + static_assert 诊断”,避免首版引入过宽的隐式提升。 -3. 后续可新增“native 快速路径”作为可选 API:当组合为 primitive 时,提供非 expected 返回通道以最大化贴近原生 C/C++ 性能与行为。 -4. 后续可新增 C API 适配层(extern "C" 薄封装,POD 入参与返回值),内部复用 unchecked/native 路径,优先保障与现有 C 调用约定兼容。 From 4e6713324f3d3f0ea2f0c2444530a5af6df1f7de Mon Sep 17 00:00:00 2001 From: FrozenlemonTee <1115306170@qq.com> Date: Sat, 21 Mar 2026 22:14:22 +0800 Subject: [PATCH 08/28] refactor: Remove common_policies_t --- src/policy/utility.cppm | 87 +---------------------------------------- 1 file changed, 1 insertion(+), 86 deletions(-) diff --git a/src/policy/utility.cppm b/src/policy/utility.cppm index 4ec0eff..e7c1447 100644 --- a/src/policy/utility.cppm +++ b/src/policy/utility.cppm @@ -1,90 +1,5 @@ module; -#include -#include export module mcpplibs.primitives.policy.utility; -import mcpplibs.primitives.policy.traits; -import mcpplibs.primitives.policy.impl; - -namespace mcpplibs::primitives::policy::details { - -template struct contains : std::false_type {}; -template -struct contains - : std::conditional_t, std::true_type, - contains> {}; - -template -struct pick_first_from_priority_impl; - -template -struct pick_first_from_priority_impl< - Default, std::tuple, Ps...> { - using type = std::conditional_t< - contains::value, FirstPriority, - typename pick_first_from_priority_impl< - Default, std::tuple, Ps...>::type>; -}; - -template -struct pick_first_from_priority_impl, Ps...> { - using type = Default; -}; - -} // namespace mcpplibs::primitives::policy::details - -export namespace mcpplibs::primitives::policy { - -// Users can specialize these templates to customize policy selection order. -template struct priority_value { - using type = std::tuple<>; -}; -template struct priority_type { - using type = std::tuple<>; -}; -template struct priority_error { - using type = std::tuple<>; -}; -template struct priority_concurrency { - using type = std::tuple<>; -}; - -template <> struct priority_value { - using type = std::tuple; -}; - -template <> struct priority_type { - using type = std::tuple; -}; - -template <> struct priority_error { - using type = std::tuple; -}; - -template <> struct priority_concurrency { - using type = std::tuple; -}; - -template struct common_policies { - using value_policy = details::pick_first_from_priority_impl< - defaults::value, priority_value<>::type, Ps...>::type; - - using type_policy = details::pick_first_from_priority_impl< - defaults::type, priority_type<>::type, Ps...>::type; - - using error_policy = details::pick_first_from_priority_impl< - defaults::error, priority_error<>::type, Ps...>::type; - - using concurrency_policy = details::pick_first_from_priority_impl< - defaults::concurrency, priority_concurrency<>::type, Ps...>::type; - - using type = - std::tuple; -}; - -template -using common_policies_t = common_policies::type; - -} // namespace mcpplibs::primitives::policy +export namespace mcpplibs::primitives::policy {} // shared policy utilities From 18fa9bd6910adf828096159949893abc6a46cc37 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee <1115306170@qq.com> Date: Sat, 21 Mar 2026 22:26:22 +0800 Subject: [PATCH 09/28] refactor: Move the definition location of `resolve_policy_t` --- src/policy/utility.cppm | 71 ++++++++++++++++++++++++++++++++- src/primitive/impl.cppm | 49 +++-------------------- src/primitive/traits.cppm | 82 ++++----------------------------------- 3 files changed, 84 insertions(+), 118 deletions(-) diff --git a/src/policy/utility.cppm b/src/policy/utility.cppm index e7c1447..c4bde8c 100644 --- a/src/policy/utility.cppm +++ b/src/policy/utility.cppm @@ -1,5 +1,74 @@ module; +#include +#include export module mcpplibs.primitives.policy.utility; -export namespace mcpplibs::primitives::policy {} // shared policy utilities +import mcpplibs.primitives.policy.traits; +import mcpplibs.primitives.policy.impl; + +namespace mcpplibs::primitives::policy::details { +using policy_category = category; + +template +struct count_matching_impl; + +template struct count_matching_impl { + static constexpr std::size_t value = 0; +}; + +template +struct count_matching_impl { + static constexpr std::size_t rest = count_matching_impl::value; + static constexpr bool is_match = + traits::enabled && traits::kind == C; + static constexpr std::size_t value = rest + (is_match ? 1u : 0u); +}; + +template +constexpr std::size_t count_matching_v = + count_matching_impl::value; + +template +struct find_first_impl; + +template struct find_first_impl { + using type = void; +}; + +template +struct find_first_impl { + static constexpr bool is_match = + traits::enabled && traits::kind == C; + using type = std::conditional_t::type>; +}; + +template +using find_first_t = find_first_impl::type; + +template +struct resolve_policy_impl { + static_assert(count_matching_v <= 1, + "Multiple policies provided for the same category"); + + using found = find_first_t; + using type = std::conditional_t< + std::is_same_v, + std::conditional_t< + C == policy_category::value, defaults::value, + std::conditional_t>>, + found>; +}; + +} // namespace mcpplibs::primitives::policy::details + +export namespace mcpplibs::primitives::policy { +using policy_category = category; + +template +using resolve_policy_t = details::resolve_policy_impl::type; +} // namespace mcpplibs::primitives::policy diff --git a/src/primitive/impl.cppm b/src/primitive/impl.cppm index f72a847..705eabf 100644 --- a/src/primitive/impl.cppm +++ b/src/primitive/impl.cppm @@ -1,60 +1,20 @@ module; -#include #include #include -#include export module mcpplibs.primitives.primitive.impl; import mcpplibs.primitives.underlying.traits; import mcpplibs.primitives.policy.traits; -import mcpplibs.primitives.policy.impl; import mcpplibs.primitives.policy.handler; +import mcpplibs.primitives.policy.utility; export namespace mcpplibs::primitives { -namespace details { - -template struct count_concurrency_policies; - -template <> struct count_concurrency_policies<> { - static constexpr std::size_t value = 0; -}; - -template -struct count_concurrency_policies { - static constexpr bool is_match = - policy::traits::kind == policy::category::concurrency; - static constexpr std::size_t value = - count_concurrency_policies::value + (is_match ? 1u : 0u); -}; - -template struct resolve_concurrency_policy; - -template <> struct resolve_concurrency_policy<> { - using type = policy::defaults::concurrency; -}; - -template -struct resolve_concurrency_policy { - using type = std::conditional_t< - policy::traits::kind == policy::category::concurrency, First, - typename resolve_concurrency_policy::type>; -}; - -template -using resolve_concurrency_policy_t = resolve_concurrency_policy::type; - -} // namespace details - template class primitive { public: using value_type = T; using policies = std::tuple; - using concurrency_policy = details::resolve_concurrency_policy_t; - - static_assert(details::count_concurrency_policies::value <= 1, - "Multiple concurrency policies are not allowed"); constexpr explicit primitive(value_type v) noexcept : value_(v) {} @@ -141,13 +101,16 @@ public: } private: + using concurrency_policy_t = policy::resolve_policy_t< + policy::category::concurrency, Policies...>; + using access_handler_t = - policy::concurrency::handler; static constexpr auto require_access_handler_() noexcept -> void { static_assert( - policy::concurrency::handler_access_available, "Selected concurrency policy does not provide primitive " "load/store/CAS support"); diff --git a/src/primitive/traits.cppm b/src/primitive/traits.cppm index 8726d5a..217e98a 100644 --- a/src/primitive/traits.cppm +++ b/src/primitive/traits.cppm @@ -1,87 +1,18 @@ module; #include -#include export module mcpplibs.primitives.primitive.traits; import mcpplibs.primitives.primitive.impl; import mcpplibs.primitives.policy.traits; import mcpplibs.primitives.policy.impl; +import mcpplibs.primitives.policy.utility; import mcpplibs.primitives.underlying.traits; -// Internal implementation details - not exported. -namespace mcpplibs::primitives::traits::details { -using policy_category = policy::category; - -template -struct count_matching_impl; - -template struct count_matching_impl { - static constexpr std::size_t value = 0; -}; - -template -struct count_matching_impl { - static constexpr std::size_t rest = count_matching_impl::value; - static constexpr bool is_match = - policy::traits::enabled && policy::traits::kind == C; - static constexpr std::size_t value = rest + (is_match ? 1u : 0u); -}; - -template -constexpr std::size_t count_matching_v = - count_matching_impl::value; - -template -struct find_first_impl; - -template struct find_first_impl { - using type = void; -}; - -template -struct find_first_impl { - static constexpr bool is_match = - policy::traits::enabled && policy::traits::kind == C; - using type = std::conditional_t::type>; -}; - -template -using find_first_t = find_first_impl::type; - -template -struct resolve_policy_impl { - static_assert(count_matching_v <= 1, - "Multiple policies provided for the same category"); - - using found = find_first_t; - using type = std::conditional_t< - std::is_same_v, - std::conditional_t< - C == policy_category::value, policy::defaults::value, - std::conditional_t< - C == policy_category::type, policy::defaults::type, - std::conditional_t>>, - found>; -}; - -template -using resolve_policy_t = resolve_policy_impl::type; - -} // namespace mcpplibs::primitives::traits::details - // Public API exported from this module. export namespace mcpplibs::primitives::traits { using policy_category = policy::category; -template -using resolve_policy_t = details::resolve_policy_t; - template struct make_primitive; template @@ -102,11 +33,14 @@ template struct primitive_traits> { using value_type = T; using policies = std::tuple; - using value_policy = resolve_policy_t; - using type_policy = resolve_policy_t; - using error_policy = resolve_policy_t; + using value_policy = + policy::resolve_policy_t; + using type_policy = + policy::resolve_policy_t; + using error_policy = + policy::resolve_policy_t; using concurrency_policy = - resolve_policy_t; + policy::resolve_policy_t; }; } // namespace mcpplibs::primitives::traits From e4f600cfd8db4bf2120e637a79e315b26a49c8b5 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee <1115306170@qq.com> Date: Sat, 21 Mar 2026 22:34:34 +0800 Subject: [PATCH 10/28] refactor: Modify namespace --- src/operations/dispatcher.cppm | 6 +++--- src/operations/operators.cppm | 6 +++--- src/primitive/traits.cppm | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/operations/dispatcher.cppm b/src/operations/dispatcher.cppm index cf2c973..155f2cf 100644 --- a/src/operations/dispatcher.cppm +++ b/src/operations/dispatcher.cppm @@ -17,7 +17,7 @@ export namespace mcpplibs::primitives::operations { template concept primitive_instance = requires { - typename primitives::traits::primitive_traits< + typename primitives::meta::traits< std::remove_cvref_t>::value_type; }; @@ -27,8 +27,8 @@ struct dispatcher_meta { using lhs_primitive = std::remove_cvref_t; using rhs_primitive = std::remove_cvref_t; - using lhs_traits = primitives::traits::primitive_traits; - using rhs_traits = primitives::traits::primitive_traits; + using lhs_traits = primitives::meta::traits; + using rhs_traits = primitives::meta::traits; using lhs_value_type = lhs_traits::value_type; using rhs_value_type = rhs_traits::value_type; diff --git a/src/operations/operators.cppm b/src/operations/operators.cppm index c3d6789..8f88ca3 100644 --- a/src/operations/operators.cppm +++ b/src/operations/operators.cppm @@ -61,9 +61,9 @@ constexpr auto decode_three_way_code(CommonRep const &code) -> Ordering { template using primitive_dispatch_result_t = std::expected< - typename mcpplibs::primitives::traits::make_primitive_t< + typename mcpplibs::primitives::meta::make_primitive_t< typename dispatcher_meta::common_rep, - typename mcpplibs::primitives::traits::primitive_traits::policies>, + typename mcpplibs::primitives::meta::traits::policies>, ErrorPayload>; template primitive_dispatch_result_t { using lhs_value_type = - typename mcpplibs::primitives::traits::primitive_traits::value_type; + typename mcpplibs::primitives::meta::traits::value_type; using lhs_rep = typename underlying::traits::rep_type; auto out = apply(lhs, rhs); diff --git a/src/primitive/traits.cppm b/src/primitive/traits.cppm index 217e98a..e9e857b 100644 --- a/src/primitive/traits.cppm +++ b/src/primitive/traits.cppm @@ -10,7 +10,7 @@ import mcpplibs.primitives.policy.utility; import mcpplibs.primitives.underlying.traits; // Public API exported from this module. -export namespace mcpplibs::primitives::traits { +export namespace mcpplibs::primitives::meta { using policy_category = policy::category; template struct make_primitive; @@ -27,10 +27,10 @@ using default_policies = std::tuple; -template struct primitive_traits; +template struct traits; template -struct primitive_traits> { +struct traits> { using value_type = T; using policies = std::tuple; using value_policy = @@ -43,4 +43,4 @@ struct primitive_traits> { policy::resolve_policy_t; }; -} // namespace mcpplibs::primitives::traits +} // namespace mcpplibs::primitives::meta From 85a2a0fce7ce0f3e64460cb1e66981682156b8f2 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee <1115306170@qq.com> Date: Sat, 21 Mar 2026 22:51:47 +0800 Subject: [PATCH 11/28] fix: Fixing the consistency of strategy group negotiation and refining type strategies for the rules of underlying negotiation in double primitive types --- src/operations/dispatcher.cppm | 35 ++++++++++++++++++++++++++++++---- src/policy/impl.cppm | 17 +++++++++++++++-- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/src/operations/dispatcher.cppm b/src/operations/dispatcher.cppm index 155f2cf..9fdaa33 100644 --- a/src/operations/dispatcher.cppm +++ b/src/operations/dispatcher.cppm @@ -36,13 +36,30 @@ struct dispatcher_meta { using lhs_rep = underlying::traits::rep_type; using rhs_rep = underlying::traits::rep_type; - using type_policy = lhs_traits::type_policy; - using value_policy = lhs_traits::value_policy; - using error_policy = lhs_traits::error_policy; - using concurrency_policy = lhs_traits::concurrency_policy; + 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 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; + + static constexpr bool policy_group_consistent = + std::is_same_v && + std::is_same_v && + std::is_same_v && + std::is_same_v; + + using type_policy = lhs_type_policy; + using value_policy = lhs_value_policy; + using error_policy = lhs_error_policy; + using concurrency_policy = lhs_concurrency_policy; using common_rep = policy::type::handler::common_rep; + static constexpr bool common_rep_non_void = !std::is_same_v; static constexpr bool type_ready = policy::type::handler_available; @@ -89,6 +106,10 @@ constexpr auto dispatch(Lhs const &lhs, Rhs const &rhs) policy::value::handler; + static_assert( + meta::policy_group_consistent, + "Primitive policy groups must match after default completion " + "(value/type/error/concurrency)"); static_assert( meta::type_ready, "Missing type_handler specialization for this operation/policy"); @@ -98,6 +119,12 @@ constexpr auto dispatch(Lhs const &lhs, Rhs const &rhs) typename meta::lhs_rep, typename meta::rhs_rep>::allowed, "Type policy rejected this operation for the given operands"); + static_assert( + !policy::type::handler::allowed || + meta::common_rep_non_void, + "Allowed type negotiation must produce a non-void common_rep"); static_assert(op_capability_valid_v, "Operation must declare a non-none capability_mask"); static_assert( diff --git a/src/policy/impl.cppm b/src/policy/impl.cppm index cb173da..e5f39b1 100644 --- a/src/policy/impl.cppm +++ b/src/policy/impl.cppm @@ -184,6 +184,18 @@ 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(); @@ -216,7 +228,8 @@ struct type::handler { static constexpr bool enabled = true; static constexpr bool allowed = std::is_arithmetic_v && std::is_arithmetic_v && - has_common_rep && + details::has_same_underlying_category_v && + details::has_non_void_common_rep_v && !details::rejects_arithmetic_for_boolean_or_character_v; static constexpr unsigned diagnostic_id = @@ -232,7 +245,7 @@ template struct type::handler { static constexpr bool enabled = true; static constexpr bool allowed = - has_common_rep && + details::has_non_void_common_rep_v && !details::rejects_arithmetic_for_boolean_or_character_v; static constexpr unsigned diagnostic_id = allowed ? 0u : 3u; From 2b01b925aba9ba7ff774159c6df6d45e438d6276 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee <1115306170@qq.com> Date: Sat, 21 Mar 2026 22:52:17 +0800 Subject: [PATCH 12/28] test: Add tests for fixing of consistency of strategy group --- tests/basic/test_operations.cpp | 52 ++++++++++++++ tests/basic/test_templates.cpp | 123 ++++++++++++++++++++++++++++++++ 2 files changed, 175 insertions(+) diff --git a/tests/basic/test_operations.cpp b/tests/basic/test_operations.cpp index 994bf2f..6f09232 100644 --- a/tests/basic/test_operations.cpp +++ b/tests/basic/test_operations.cpp @@ -347,6 +347,58 @@ TEST(OperationsTest, StrictTypeAllowsSameTypeAtRuntime) { EXPECT_EQ(result->value(), 42); } +TEST(OperationsTest, DispatcherMetaTracksResolvedPolicyGroupConsistency) { + using aligned_lhs_t = primitive; + using aligned_rhs_t = primitive; + using aligned_meta = operations::dispatcher_meta; + + using mismatch_lhs_t = primitive; + using mismatch_rhs_t = primitive; + using mismatch_meta = + operations::dispatcher_meta; + + static_assert(aligned_meta::policy_group_consistent); + static_assert(!mismatch_meta::policy_group_consistent); + + EXPECT_TRUE(aligned_meta::policy_group_consistent); + EXPECT_FALSE(mismatch_meta::policy_group_consistent); +} + +TEST(OperationsTest, CompatibleTypeRequiresSameUnderlyingCategory) { + using same_category_handler = + policy::type::handler; + using cross_category_handler = + policy::type::handler; + + static_assert(same_category_handler::enabled); + static_assert(same_category_handler::allowed); + static_assert(cross_category_handler::enabled); + static_assert(!cross_category_handler::allowed); + + EXPECT_EQ(cross_category_handler::diagnostic_id, 2u); +} + +TEST(OperationsTest, TransparentTypeIgnoresCategoryWhenCommonRepIsValid) { + using transparent_handler = + policy::type::handler; + + static_assert(transparent_handler::enabled); + static_assert(transparent_handler::allowed); + static_assert( + std::is_same_v); + + EXPECT_EQ(transparent_handler::diagnostic_id, 0u); +} + TEST(OperationsTest, BoolUnderlyingRejectsArithmeticOperationsAtCompileTime) { using value_t = primitive; diff --git a/tests/basic/test_templates.cpp b/tests/basic/test_templates.cpp index 574a38f..6040194 100644 --- a/tests/basic/test_templates.cpp +++ b/tests/basic/test_templates.cpp @@ -150,6 +150,64 @@ struct ExplicitCommonRep { } }; +struct VoidCommonLhs { + int value; + + friend constexpr auto operator+(VoidCommonLhs lhs, VoidCommonLhs rhs) noexcept + -> VoidCommonLhs { + return VoidCommonLhs{lhs.value + rhs.value}; + } + + friend constexpr auto operator-(VoidCommonLhs lhs, VoidCommonLhs rhs) noexcept + -> VoidCommonLhs { + return VoidCommonLhs{lhs.value - rhs.value}; + } + + friend constexpr auto operator*(VoidCommonLhs lhs, VoidCommonLhs rhs) noexcept + -> VoidCommonLhs { + return VoidCommonLhs{lhs.value * rhs.value}; + } + + friend constexpr auto operator/(VoidCommonLhs lhs, VoidCommonLhs rhs) noexcept + -> VoidCommonLhs { + return VoidCommonLhs{lhs.value / rhs.value}; + } + + friend constexpr auto operator==(VoidCommonLhs lhs, + VoidCommonLhs rhs) noexcept -> bool { + return lhs.value == rhs.value; + } +}; + +struct VoidCommonRhs { + int value; + + friend constexpr auto operator+(VoidCommonRhs lhs, VoidCommonRhs rhs) noexcept + -> VoidCommonRhs { + return VoidCommonRhs{lhs.value + rhs.value}; + } + + friend constexpr auto operator-(VoidCommonRhs lhs, VoidCommonRhs rhs) noexcept + -> VoidCommonRhs { + return VoidCommonRhs{lhs.value - rhs.value}; + } + + friend constexpr auto operator*(VoidCommonRhs lhs, VoidCommonRhs rhs) noexcept + -> VoidCommonRhs { + return VoidCommonRhs{lhs.value * rhs.value}; + } + + friend constexpr auto operator/(VoidCommonRhs lhs, VoidCommonRhs rhs) noexcept + -> VoidCommonRhs { + return VoidCommonRhs{lhs.value / rhs.value}; + } + + friend constexpr auto operator==(VoidCommonRhs lhs, + VoidCommonRhs rhs) noexcept -> bool { + return lhs.value == rhs.value; + } +}; + } // namespace template <> struct mcpplibs::primitives::underlying::traits { @@ -319,6 +377,52 @@ struct mcpplibs::primitives::underlying::common_rep_traits struct mcpplibs::primitives::underlying::traits { + using value_type = VoidCommonLhs; + using rep_type = VoidCommonLhs; + + static constexpr bool enabled = true; + static constexpr auto kind = category::integer; + + static constexpr rep_type to_rep(value_type value) noexcept { return value; } + + static constexpr value_type from_rep(rep_type value) noexcept { + return value; + } + + static constexpr bool is_valid_rep(rep_type) noexcept { return true; } +}; + +template <> struct mcpplibs::primitives::underlying::traits { + using value_type = VoidCommonRhs; + using rep_type = VoidCommonRhs; + + static constexpr bool enabled = true; + static constexpr auto kind = category::integer; + + static constexpr rep_type to_rep(value_type value) noexcept { return value; } + + static constexpr value_type from_rep(rep_type value) noexcept { + return value; + } + + static constexpr bool is_valid_rep(rep_type) noexcept { return true; } +}; + +template <> +struct mcpplibs::primitives::underlying::common_rep_traits { + using type = void; + static constexpr bool enabled = true; +}; + +template <> +struct mcpplibs::primitives::underlying::common_rep_traits { + using type = void; + static constexpr bool enabled = true; +}; + TEST(PrimitiveTraitsTest, StandardTypeConcepts) { EXPECT_TRUE((mcpplibs::primitives::std_integer)); EXPECT_TRUE((mcpplibs::primitives::std_integer)); @@ -453,6 +557,25 @@ TEST(PrimitiveTraitsTest, UnderlyingCommonRepCanBeCustomizedViaTraits) { SUCCEED(); } +TEST(PrimitiveTraitsTest, TypePoliciesRequireNonVoidCommonRep) { + using compatible_handler_t = mcpplibs::primitives::policy::type::handler< + mcpplibs::primitives::policy::type::compatible, + mcpplibs::primitives::operations::Addition, VoidCommonLhs, VoidCommonRhs>; + using transparent_handler_t = mcpplibs::primitives::policy::type::handler< + mcpplibs::primitives::policy::type::transparent, + mcpplibs::primitives::operations::Addition, VoidCommonLhs, VoidCommonRhs>; + + static_assert(compatible_handler_t::enabled); + static_assert(transparent_handler_t::enabled); + static_assert(!compatible_handler_t::allowed); + static_assert(!transparent_handler_t::allowed); + static_assert(std::same_as); + static_assert(std::same_as); + + EXPECT_EQ(compatible_handler_t::diagnostic_id, 2u); + EXPECT_EQ(transparent_handler_t::diagnostic_id, 3u); +} + int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); From c1735f4b48c793face63038b8af55192008ca19e Mon Sep 17 00:00:00 2001 From: FrozenlemonTee <1115306170@qq.com> Date: Sat, 21 Mar 2026 23:15:49 +0800 Subject: [PATCH 13/28] fix: Fix overflow issue in apply_assign --- src/operations/operators.cppm | 38 ++++++++++++++++++++++--------- src/policy/impl.cppm | 42 +++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 11 deletions(-) diff --git a/src/operations/operators.cppm b/src/operations/operators.cppm index 8f88ca3..efd70cb 100644 --- a/src/operations/operators.cppm +++ b/src/operations/operators.cppm @@ -13,6 +13,7 @@ import mcpplibs.primitives.operations.impl; import mcpplibs.primitives.primitive.impl; import mcpplibs.primitives.primitive.traits; import mcpplibs.primitives.policy.handler; +import mcpplibs.primitives.policy.impl; import mcpplibs.primitives.underlying.traits; export namespace mcpplibs::primitives::operations { @@ -32,7 +33,7 @@ struct three_way_ordering< }; template -using three_way_ordering_t = typename three_way_ordering::type; +using three_way_ordering_t = three_way_ordering::type; template constexpr auto decode_three_way_code(CommonRep const &code) -> Ordering { @@ -56,14 +57,14 @@ constexpr auto decode_three_way_code(CommonRep const &code) -> Ordering { return Ordering::equivalent; } + } // namespace details template -using primitive_dispatch_result_t = std::expected< - typename mcpplibs::primitives::meta::make_primitive_t< +using primitive_dispatch_result_t = std::expected::common_rep, - typename mcpplibs::primitives::meta::traits::policies>, + typename meta::traits::policies>, ErrorPayload>; template primitive_dispatch_result_t { using result_primitive = - typename primitive_dispatch_result_t::value_type; auto const raw = dispatch(lhs, rhs); @@ -137,10 +138,10 @@ template three_way_dispatch_result_t { using common_rep = - typename dispatcher_meta::common_rep; using ordering = - typename three_way_dispatch_result_t::value_type; + three_way_dispatch_result_t::value_type; auto const raw = dispatch(lhs, rhs); if (!raw.has_value()) { @@ -154,16 +155,31 @@ template constexpr auto apply_assign(Lhs &lhs, Rhs const &rhs) -> primitive_dispatch_result_t { - using lhs_value_type = - typename mcpplibs::primitives::meta::traits::value_type; - using lhs_rep = typename underlying::traits::rep_type; + using lhs_traits = meta::traits; + 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()) { return std::unexpected(out.error()); } - auto const assigned_rep = static_cast(out->load()); + auto const assigned_common = out->load(); + if constexpr (std::same_as && + std::integral && std::integral) { + if (auto const kind = + policy::details::narrow_integral_error(assigned_common); + kind.has_value()) { + return std::unexpected( + policy::details::to_error_payload(*kind)); + } + } + + auto const assigned_rep = static_cast(assigned_common); lhs.store(underlying::traits::from_rep(assigned_rep)); return out; } diff --git a/src/policy/impl.cppm b/src/policy/impl.cppm index e5f39b1..13112f8 100644 --- a/src/policy/impl.cppm +++ b/src/policy/impl.cppm @@ -1,8 +1,11 @@ module; #include +#include #include #include #include +#include +#include #include #include #include @@ -532,6 +535,45 @@ constexpr auto to_error_payload(error::kind kind) -> ErrorPayload { return ErrorPayload{}; } } + +template +constexpr auto narrow_integral_error(SrcRep value) + -> std::optional { + using dest_type = std::remove_cv_t; + using src_type = std::remove_cv_t; + + if constexpr (std::is_signed_v) { + auto const signed_value = static_cast(value); + if constexpr (std::is_signed_v) { + if (signed_value < + static_cast(std::numeric_limits::min())) { + return error::kind::underflow; + } + if (signed_value > + static_cast(std::numeric_limits::max())) { + return error::kind::overflow; + } + return std::nullopt; + } else { + if (signed_value < 0) { + return error::kind::underflow; + } + + if (static_cast(signed_value) > + static_cast(std::numeric_limits::max())) { + return error::kind::overflow; + } + return std::nullopt; + } + } else { + auto const unsigned_value = static_cast(value); + if (unsigned_value > + static_cast(std::numeric_limits::max())) { + return error::kind::overflow; + } + return std::nullopt; + } +} } // namespace details template Date: Sat, 21 Mar 2026 23:35:48 +0800 Subject: [PATCH 14/28] refactor: Refine the normal constructor of primitive --- src/primitive/impl.cppm | 51 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/src/primitive/impl.cppm b/src/primitive/impl.cppm index 705eabf..36a22c1 100644 --- a/src/primitive/impl.cppm +++ b/src/primitive/impl.cppm @@ -7,6 +7,7 @@ export module mcpplibs.primitives.primitive.impl; import mcpplibs.primitives.underlying.traits; import mcpplibs.primitives.policy.traits; import mcpplibs.primitives.policy.handler; +import mcpplibs.primitives.policy.impl; import mcpplibs.primitives.policy.utility; export namespace mcpplibs::primitives { @@ -16,7 +17,55 @@ public: using value_type = T; using policies = std::tuple; - constexpr explicit primitive(value_type v) noexcept : value_(v) {} +private: + using type_policy_t = + policy::resolve_policy_t; + using value_rep_type = underlying::traits::rep_type; + + template + static constexpr bool cross_underlying_constructible_v = [] { + using source_value_type = std::remove_cv_t; + using source_rep_type = + underlying::traits::rep_type; + + if constexpr (std::same_as) { + return false; + } else if constexpr (std::same_as) { + return underlying::traits::kind == + underlying::traits::kind && + has_common_rep && + !std::same_as, void>; + } else if constexpr (std::same_as) { + return has_common_rep && + !std::same_as, void>; + } else { + return false; + } + }(); + + template + static constexpr auto convert_cross_underlying_(U u) noexcept -> value_type { + using source_value_type = std::remove_cv_t; + using source_rep_type = + underlying::traits::rep_type; + + auto const source_rep = underlying::traits::to_rep(u); + auto const target_rep = static_cast( + static_cast(source_rep)); + return underlying::traits::from_rep(target_rep); + } + +public: + template + requires std::same_as + constexpr explicit primitive(U u) noexcept : value_(u) {} + + template + requires(!std::same_as && + cross_underlying_constructible_v) + constexpr explicit primitive(U u) noexcept + : value_(convert_cross_underlying_(u)) {} constexpr primitive(primitive const &other) noexcept { if consteval { From f7bf3ae72cc4fe9bcfeb93c488bf51994a805df1 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee <1115306170@qq.com> Date: Sat, 21 Mar 2026 23:36:13 +0800 Subject: [PATCH 15/28] test: Add unit tests for the normal constructor of primitive --- tests/basic/test_operations.cpp | 37 +++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/basic/test_operations.cpp b/tests/basic/test_operations.cpp index 6f09232..ea484ca 100644 --- a/tests/basic/test_operations.cpp +++ b/tests/basic/test_operations.cpp @@ -12,6 +12,12 @@ import mcpplibs.primitives; using namespace mcpplibs::primitives; +template +concept constructible_from_underlying = + underlying_type && requires(U u) { + Primitive{u}; +}; + TEST(OperationsTest, AddReturnsExpectedPrimitive) { using lhs_t = primitive; using rhs_t = primitive; @@ -347,6 +353,37 @@ TEST(OperationsTest, StrictTypeAllowsSameTypeAtRuntime) { EXPECT_EQ(result->value(), 42); } +TEST(OperationsTest, StrictTypePrimitiveConstructorRejectsCrossUnderlyingType) { + using strict_t = primitive; + + static_assert(constructible_from_underlying); + static_assert(!constructible_from_underlying); + + auto const value = strict_t{42}; + EXPECT_EQ(value.load(), 42); +} + +TEST(OperationsTest, CompatibleTypePrimitiveConstructorAllowsSameCategory) { + using compatible_t = + primitive; + + static_assert(constructible_from_underlying); + static_assert(!constructible_from_underlying); + + auto const value = compatible_t{static_cast(42)}; + EXPECT_EQ(value.load(), 42); +} + +TEST(OperationsTest, TransparentTypePrimitiveConstructorAllowsCrossCategory) { + using transparent_t = + primitive; + + static_assert(constructible_from_underlying); + + auto const value = transparent_t{42.75}; + EXPECT_EQ(value.load(), 42); +} + TEST(OperationsTest, DispatcherMetaTracksResolvedPolicyGroupConsistency) { using aligned_lhs_t = primitive; From c8939a6dfa41b55cfa5c99ed9dd2df90cbbed31c Mon Sep 17 00:00:00 2001 From: FrozenlemonTee <1115306170@qq.com> Date: Sat, 21 Mar 2026 23:48:05 +0800 Subject: [PATCH 16/28] feat: Add overload versions of copy/move semantic functions of primitive --- src/primitive/impl.cppm | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/primitive/impl.cppm b/src/primitive/impl.cppm index 36a22c1..3d94977 100644 --- a/src/primitive/impl.cppm +++ b/src/primitive/impl.cppm @@ -75,6 +75,12 @@ public: } } + template + requires(!std::same_as) + explicit constexpr primitive(primitive const &other) noexcept + requires(cross_underlying_constructible_v) + : value_(convert_cross_underlying_(other.load())) {} + constexpr auto operator=(primitive const &other) noexcept -> primitive & { if (this == &other) { return *this; @@ -88,6 +94,15 @@ public: return *this; } + template + requires(!std::same_as) + constexpr auto + operator=(primitive const &other) noexcept -> primitive & + requires(cross_underlying_constructible_v) { + store(convert_cross_underlying_(other.load())); + return *this; + } + constexpr primitive(primitive &&other) noexcept { if consteval { value_ = other.value_; @@ -96,6 +111,12 @@ public: } } + template + requires(!std::same_as) + explicit constexpr primitive(primitive &&other) noexcept + requires(cross_underlying_constructible_v) + : value_(convert_cross_underlying_(other.load())) {} + constexpr auto operator=(primitive &&other) noexcept -> primitive & { if (this == &other) { return *this; @@ -109,6 +130,15 @@ public: return *this; } + template + requires(!std::same_as) + constexpr auto operator=(primitive &&other) noexcept + -> primitive & + requires(cross_underlying_constructible_v) { + store(convert_cross_underlying_(other.load())); + return *this; + } + constexpr value_type &value() noexcept { return value_; } [[nodiscard]] constexpr value_type const &value() const noexcept { From 51411a08e046348092e2885d1ee681836e72684f Mon Sep 17 00:00:00 2001 From: FrozenlemonTee <1115306170@qq.com> Date: Sat, 21 Mar 2026 23:48:17 +0800 Subject: [PATCH 17/28] feat: Add unit tests for overload versions of copy/move semantic functions of primitive --- tests/basic/test_operations.cpp | 39 +++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tests/basic/test_operations.cpp b/tests/basic/test_operations.cpp index ea484ca..0d0133b 100644 --- a/tests/basic/test_operations.cpp +++ b/tests/basic/test_operations.cpp @@ -384,6 +384,45 @@ TEST(OperationsTest, TransparentTypePrimitiveConstructorAllowsCrossCategory) { EXPECT_EQ(value.load(), 42); } +TEST(OperationsTest, PrimitiveSpecialMembersSupportCrossUnderlyingWithCompatibleType) { + using dst_t = + primitive; + using src_t = + primitive; + + static_assert(std::is_constructible_v); + static_assert(std::is_constructible_v); + static_assert(std::is_assignable_v); + static_assert(std::is_assignable_v); + + auto const source = src_t{40}; + auto copy_constructed = dst_t{source}; + EXPECT_EQ(copy_constructed.load(), 40); + + auto move_constructed = dst_t{src_t{41}}; + EXPECT_EQ(move_constructed.load(), 41); + + auto copy_assigned = dst_t{0}; + copy_assigned = source; + EXPECT_EQ(copy_assigned.load(), 40); + + auto move_assigned = dst_t{0}; + auto move_source = src_t{42}; + move_assigned = std::move(move_source); + EXPECT_EQ(move_assigned.load(), 42); +} + +TEST(OperationsTest, + PrimitiveSpecialMembersRejectCrossUnderlyingWithStrictType) { + using dst_t = primitive; + using src_t = primitive; + + static_assert(!std::is_constructible_v); + static_assert(!std::is_constructible_v); + static_assert(!std::is_assignable_v); + static_assert(!std::is_assignable_v); +} + TEST(OperationsTest, DispatcherMetaTracksResolvedPolicyGroupConsistency) { using aligned_lhs_t = primitive; From 7fd91b86b5cc5c75874aa8f3d9966bf7aa1a6ca8 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee <1115306170@qq.com> Date: Sun, 22 Mar 2026 08:41:00 +0800 Subject: [PATCH 18/28] feat: Add overload versions of store/compare_exchange of primitive --- src/primitive/impl.cppm | 85 ++++++++++++++++++++++++++++------------- 1 file changed, 59 insertions(+), 26 deletions(-) diff --git a/src/primitive/impl.cppm b/src/primitive/impl.cppm index 3d94977..86c3aba 100644 --- a/src/primitive/impl.cppm +++ b/src/primitive/impl.cppm @@ -22,38 +22,48 @@ private: policy::resolve_policy_t; using value_rep_type = underlying::traits::rep_type; - template - static constexpr bool cross_underlying_constructible_v = [] { - using source_value_type = std::remove_cv_t; + template + static constexpr bool policy_allows_underlying_bridge_v = [] { using source_rep_type = - underlying::traits::rep_type; - + underlying::traits>::rep_type; + using target_rep_type = + underlying::traits>::rep_type; if constexpr (std::same_as) { return false; } else if constexpr (std::same_as) { - return underlying::traits::kind == - underlying::traits::kind && - has_common_rep && - !std::same_as, void>; + return underlying::traits>::kind == + underlying::traits>::kind && + has_common_rep && + !std::same_as, void>; } else if constexpr (std::same_as) { - return has_common_rep && - !std::same_as, void>; + return has_common_rep && + !std::same_as, void>; } else { return false; } }(); template - static constexpr auto convert_cross_underlying_(U u) noexcept -> value_type { - using source_value_type = std::remove_cv_t; - using source_rep_type = - underlying::traits::rep_type; + static constexpr bool cross_underlying_constructible_v = + policy_allows_underlying_bridge_v; - auto const source_rep = underlying::traits::to_rep(u); - auto const target_rep = static_cast( + template + static constexpr bool cross_underlying_exchangeable_v = + cross_underlying_constructible_v && + policy_allows_underlying_bridge_v; + + template + static constexpr auto convert_underlying_(Source source) noexcept -> Target { + using source_value_type = std::remove_cv_t; + using source_rep_type = underlying::traits::rep_type; + using target_rep_type = + underlying::traits>::rep_type; + + auto const source_rep = underlying::traits::to_rep(source); + auto const target_rep = static_cast( static_cast(source_rep)); - return underlying::traits::from_rep(target_rep); + return underlying::traits>::from_rep(target_rep); } public: @@ -65,7 +75,7 @@ public: requires(!std::same_as && cross_underlying_constructible_v) constexpr explicit primitive(U u) noexcept - : value_(convert_cross_underlying_(u)) {} + : value_(convert_underlying_(u)) {} constexpr primitive(primitive const &other) noexcept { if consteval { @@ -79,7 +89,7 @@ public: requires(!std::same_as) explicit constexpr primitive(primitive const &other) noexcept requires(cross_underlying_constructible_v) - : value_(convert_cross_underlying_(other.load())) {} + : value_(convert_underlying_(other.load())) {} constexpr auto operator=(primitive const &other) noexcept -> primitive & { if (this == &other) { @@ -99,7 +109,7 @@ public: constexpr auto operator=(primitive const &other) noexcept -> primitive & requires(cross_underlying_constructible_v) { - store(convert_cross_underlying_(other.load())); + store(convert_underlying_(other.load())); return *this; } @@ -115,7 +125,7 @@ public: requires(!std::same_as) explicit constexpr primitive(primitive &&other) noexcept requires(cross_underlying_constructible_v) - : value_(convert_cross_underlying_(other.load())) {} + : value_(convert_underlying_(other.load())) {} constexpr auto operator=(primitive &&other) noexcept -> primitive & { if (this == &other) { @@ -135,7 +145,7 @@ public: constexpr auto operator=(primitive &&other) noexcept -> primitive & requires(cross_underlying_constructible_v) { - store(convert_cross_underlying_(other.load())); + store(convert_underlying_(other.load())); return *this; } @@ -155,7 +165,9 @@ public: return access_handler_t::load(value_); } - constexpr auto store(value_type desired) noexcept -> void { + template + requires std::same_as + constexpr auto store(U desired) noexcept -> void { if consteval { value_ = desired; } else { @@ -164,8 +176,16 @@ public: } } - constexpr auto compare_exchange(value_type &expected, - value_type desired) noexcept -> bool { + template + requires(!std::same_as && + cross_underlying_constructible_v) + constexpr auto store(U desired) noexcept -> void { + store(convert_underlying_(desired)); + } + + template + requires std::same_as + constexpr auto compare_exchange(U &expected, U desired) noexcept -> bool { if consteval { if (value_ != expected) { expected = value_; @@ -179,6 +199,19 @@ public: return access_handler_t::compare_exchange(value_, expected, desired); } + template + requires(!std::same_as && cross_underlying_exchangeable_v) + constexpr auto compare_exchange(U &expected, U desired) noexcept -> bool { + auto expected_native = convert_underlying_(expected); + auto const desired_native = convert_underlying_(desired); + + auto const exchanged = compare_exchange(expected_native, desired_native); + if (!exchanged) { + expected = convert_underlying_(expected_native); + } + return exchanged; + } + private: using concurrency_policy_t = policy::resolve_policy_t< policy::category::concurrency, Policies...>; From 7dbf90a5fa01173fbfa505581d705edb36978e52 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee <1115306170@qq.com> Date: Sun, 22 Mar 2026 08:41:20 +0800 Subject: [PATCH 19/28] test: Add unit tests for overload versions of store/compare_exchange of primitive --- tests/basic/test_operations.cpp | 61 +++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/tests/basic/test_operations.cpp b/tests/basic/test_operations.cpp index 0d0133b..63ced7f 100644 --- a/tests/basic/test_operations.cpp +++ b/tests/basic/test_operations.cpp @@ -18,6 +18,18 @@ concept constructible_from_underlying = Primitive{u}; }; +template +concept storable_from_underlying = + underlying_type && requires(Primitive p, U u) { + p.store(u); +}; + +template +concept cas_from_underlying = + underlying_type && requires(Primitive p, U expected, U desired) { + { p.compare_exchange(expected, desired) } -> std::same_as; +}; + TEST(OperationsTest, AddReturnsExpectedPrimitive) { using lhs_t = primitive; using rhs_t = primitive; @@ -384,6 +396,55 @@ TEST(OperationsTest, TransparentTypePrimitiveConstructorAllowsCrossCategory) { EXPECT_EQ(value.load(), 42); } +TEST(OperationsTest, PrimitiveStoreAndCasRejectCrossUnderlyingWithStrictType) { + using strict_t = primitive; + + static_assert(!storable_from_underlying); + static_assert(!cas_from_underlying); +} + +TEST(OperationsTest, PrimitiveStoreAndCasAllowCompatibleSameCategory) { + using compatible_t = + primitive; + + static_assert(storable_from_underlying); + static_assert(cas_from_underlying); + static_assert(!storable_from_underlying); + static_assert(!cas_from_underlying); + + auto value = compatible_t{10}; + value.store(static_cast(12)); + EXPECT_EQ(value.load(), 12); + + short expected = 12; + EXPECT_TRUE(value.compare_exchange(expected, static_cast(20))); + EXPECT_EQ(value.load(), 20); + + expected = 11; + EXPECT_FALSE(value.compare_exchange(expected, static_cast(30))); + EXPECT_EQ(expected, 20); +} + +TEST(OperationsTest, PrimitiveStoreAndCasAllowTransparentCrossCategory) { + using transparent_t = + primitive; + + static_assert(storable_from_underlying); + static_assert(cas_from_underlying); + + auto value = transparent_t{10}; + value.store(42.75); + EXPECT_EQ(value.load(), 42); + + double expected = 42.0; + EXPECT_TRUE(value.compare_exchange(expected, 99.5)); + EXPECT_EQ(value.load(), 99); + + expected = 3.0; + EXPECT_FALSE(value.compare_exchange(expected, 1.0)); + EXPECT_EQ(expected, 99.0); +} + TEST(OperationsTest, PrimitiveSpecialMembersSupportCrossUnderlyingWithCompatibleType) { using dst_t = primitive; From 691a913971e4ec789986612a3d9914e11c13a902 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee <1115306170@qq.com> Date: Sun, 22 Mar 2026 11:08:46 +0800 Subject: [PATCH 20/28] refactor: Explicitly disable the underlying traits of primitives --- src/primitive/impl.cppm | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/primitive/impl.cppm b/src/primitive/impl.cppm index 86c3aba..d19dda0 100644 --- a/src/primitive/impl.cppm +++ b/src/primitive/impl.cppm @@ -272,3 +272,20 @@ using F80 = primitive; } // namespace types } // namespace mcpplibs::primitives + +export template +struct mcpplibs::primitives::underlying::traits> +{ + using value_type = void; + using rep_type = void; + + static constexpr bool enabled = false; + + static constexpr auto kind = static_cast(-1); + + template static constexpr rep_type to_rep(U) noexcept = delete; + + template static constexpr value_type from_rep(U) noexcept = delete; + + template static constexpr bool is_valid_rep(U) noexcept = delete; +}; From 9e60886f88fc03a48aee1b119a8e2a1dae4bf0a4 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee <1115306170@qq.com> Date: Sun, 22 Mar 2026 11:10:17 +0800 Subject: [PATCH 21/28] feat: Add binary operation/apply cross-type overload of the binary version --- src/operations/operators.cppm | 273 ++++++++++++++++++++++++++++++++++ 1 file changed, 273 insertions(+) diff --git a/src/operations/operators.cppm b/src/operations/operators.cppm index efd70cb..f5aeb21 100644 --- a/src/operations/operators.cppm +++ b/src/operations/operators.cppm @@ -1,6 +1,7 @@ module; #include +#include #include #include #include @@ -67,6 +68,21 @@ using primitive_dispatch_result_t = std::expected::policies>, ErrorPayload>; +template +concept underlying_operand = underlying_type>; + +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 = + primitive_dispatch_result_t, + ErrorPayload>; + template using three_way_dispatch_result_t = std::expected< @@ -75,6 +91,25 @@ 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, + ErrorPayload>; + +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 constexpr auto apply(Lhs const &lhs, Rhs const &rhs) @@ -91,6 +126,25 @@ constexpr auto apply(Lhs const &lhs, Rhs const &rhs) return result_primitive{*raw}; } +template +constexpr auto apply(Lhs const &lhs, Rhs const &rhs) + -> mixed_primitive_dispatch_result_t { + using bridge_rhs_t = mixed_bridge_primitive_t; + auto const bridge_rhs = bridge_rhs_t{rhs}; + return apply(lhs, bridge_rhs); +} + +template +constexpr auto apply(Lhs const &lhs, Rhs const &rhs) + -> flipped_mixed_primitive_dispatch_result_t { + using bridge_lhs_t = mixed_bridge_primitive_t; + auto const bridge_lhs = bridge_lhs_t{lhs}; + return apply(bridge_lhs, rhs); +} + template constexpr auto add(Lhs const &lhs, Rhs const &rhs) @@ -98,6 +152,21 @@ constexpr auto add(Lhs const &lhs, Rhs const &rhs) 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 { + return apply(lhs, rhs); +} + template constexpr auto sub(Lhs const &lhs, Rhs const &rhs) @@ -105,6 +174,21 @@ constexpr auto sub(Lhs const &lhs, Rhs const &rhs) 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 { + return apply(lhs, rhs); +} + template constexpr auto mul(Lhs const &lhs, Rhs const &rhs) @@ -112,6 +196,22 @@ constexpr auto mul(Lhs const &lhs, Rhs const &rhs) return apply(lhs, rhs); } +template +constexpr auto mul(Lhs const &lhs, Rhs const &rhs) + -> mixed_primitive_dispatch_result_t { + return apply(lhs, rhs); +} + +template +constexpr auto mul(Lhs const &lhs, Rhs const &rhs) + -> flipped_mixed_primitive_dispatch_result_t { + return apply(lhs, rhs); +} + template constexpr auto div(Lhs const &lhs, Rhs const &rhs) @@ -119,6 +219,21 @@ constexpr auto div(Lhs const &lhs, Rhs const &rhs) return apply(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 { + return apply(lhs, rhs); +} + template constexpr auto equal(Lhs const &lhs, Rhs const &rhs) @@ -126,6 +241,21 @@ constexpr auto equal(Lhs const &lhs, Rhs const &rhs) 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 { + return apply(lhs, rhs); +} + template constexpr auto not_equal(Lhs const &lhs, Rhs const &rhs) @@ -133,6 +263,21 @@ constexpr auto not_equal(Lhs const &lhs, Rhs const &rhs) 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 { + return apply(lhs, rhs); +} + template constexpr auto three_way_compare(Lhs const &lhs, Rhs const &rhs) @@ -151,6 +296,24 @@ 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 { + using bridge_rhs_t = mixed_bridge_primitive_t; + auto const bridge_rhs = bridge_rhs_t{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 { + using bridge_lhs_t = mixed_bridge_primitive_t; + auto const bridge_lhs = bridge_lhs_t{lhs}; + return three_way_compare(bridge_lhs, rhs); +} + template constexpr auto apply_assign(Lhs &lhs, Rhs const &rhs) @@ -223,6 +386,22 @@ constexpr auto operator+(Lhs const &lhs, Rhs const &rhs) return operations::add(lhs, rhs); } +template +constexpr auto operator+(Lhs const &lhs, Rhs const &rhs) + -> operations::mixed_primitive_dispatch_result_t { + return operations::add(lhs, rhs); +} + +template +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 constexpr auto operator-(Lhs const &lhs, Rhs const &rhs) @@ -231,6 +410,22 @@ constexpr auto operator-(Lhs const &lhs, Rhs const &rhs) return operations::sub(lhs, rhs); } +template +constexpr auto operator-(Lhs const &lhs, Rhs const &rhs) + -> operations::mixed_primitive_dispatch_result_t { + return operations::sub(lhs, rhs); +} + +template +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 constexpr auto operator*(Lhs const &lhs, Rhs const &rhs) @@ -239,6 +434,22 @@ constexpr auto operator*(Lhs const &lhs, Rhs const &rhs) return operations::mul(lhs, rhs); } +template +constexpr auto operator*(Lhs const &lhs, Rhs const &rhs) + -> operations::mixed_primitive_dispatch_result_t { + return operations::mul(lhs, rhs); +} + +template +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 constexpr auto operator/(Lhs const &lhs, Rhs const &rhs) @@ -246,6 +457,22 @@ constexpr auto operator/(Lhs const &lhs, Rhs const &rhs) return operations::div(lhs, rhs); } +template +constexpr auto operator/(Lhs const &lhs, Rhs const &rhs) + -> operations::mixed_primitive_dispatch_result_t { + return operations::div(lhs, rhs); +} + +template +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 constexpr auto operator==(Lhs const &lhs, Rhs const &rhs) @@ -253,6 +480,22 @@ constexpr auto operator==(Lhs const &lhs, Rhs const &rhs) return operations::equal(lhs, rhs); } +template +constexpr auto operator==(Lhs const &lhs, Rhs const &rhs) + -> operations::mixed_primitive_dispatch_result_t { + return operations::equal(lhs, rhs); +} + +template +constexpr auto operator==(Lhs const &lhs, Rhs const &rhs) + -> operations::flipped_mixed_primitive_dispatch_result_t { + return operations::equal(lhs, rhs); +} + template constexpr auto operator!=(Lhs const &lhs, Rhs const &rhs) @@ -260,6 +503,22 @@ constexpr auto operator!=(Lhs const &lhs, Rhs const &rhs) return operations::not_equal(lhs, rhs); } +template +constexpr auto operator!=(Lhs const &lhs, Rhs const &rhs) + -> operations::mixed_primitive_dispatch_result_t { + return operations::not_equal(lhs, rhs); +} + +template +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 constexpr auto operator<=>(Lhs const &lhs, Rhs const &rhs) @@ -267,6 +526,20 @@ constexpr auto operator<=>(Lhs const &lhs, Rhs const &rhs) return operations::three_way_compare(lhs, rhs); } +template +constexpr auto operator<=>(Lhs const &lhs, Rhs const &rhs) + -> operations::mixed_three_way_dispatch_result_t { + return operations::three_way_compare(lhs, rhs); +} + +template +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 constexpr auto operator+=(Lhs &lhs, Rhs const &rhs) From ed9e87e4369a3108e1c5995dd5c1477c77f8a580 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee <1115306170@qq.com> Date: Sun, 22 Mar 2026 11:10:34 +0800 Subject: [PATCH 22/28] feat: Add unit tests for binary operation/apply cross-type overload of the binary version --- tests/basic/test_operations.cpp | 97 +++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/tests/basic/test_operations.cpp b/tests/basic/test_operations.cpp index 63ced7f..75e97f5 100644 --- a/tests/basic/test_operations.cpp +++ b/tests/basic/test_operations.cpp @@ -644,6 +644,103 @@ TEST(OperationsTest, PrimitiveAliasMixesWithBuiltinArithmeticExplicitly) { EXPECT_EQ(result->value(), 43); } +TEST(OperationsTest, MixedBinaryOperationsSupportUnderlyingOnBothSides) { + using value_t = primitive; + + auto const lhs = value_t{40}; + short const rhs = 2; + + auto const apply_lr = operations::apply(lhs, rhs); + auto const apply_rl = operations::apply(rhs, lhs); + auto const add_lr = operations::add(lhs, rhs); + auto const add_rl = operations::add(rhs, lhs); + auto const sub_lr = operations::sub(lhs, rhs); + auto const sub_rl = operations::sub(rhs, lhs); + auto const div_lr = operations::div(lhs, rhs); + auto const div_rl = operations::div(rhs, lhs); + auto const cmp_lr = operations::three_way_compare(lhs, rhs); + auto const cmp_rl = operations::three_way_compare(rhs, lhs); + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + + ASSERT_TRUE(apply_lr.has_value()); + ASSERT_TRUE(apply_rl.has_value()); + ASSERT_TRUE(add_lr.has_value()); + ASSERT_TRUE(add_rl.has_value()); + ASSERT_TRUE(sub_lr.has_value()); + ASSERT_TRUE(sub_rl.has_value()); + ASSERT_TRUE(div_lr.has_value()); + ASSERT_TRUE(div_rl.has_value()); + ASSERT_TRUE(cmp_lr.has_value()); + ASSERT_TRUE(cmp_rl.has_value()); + + EXPECT_EQ(apply_lr->value(), 42); + EXPECT_EQ(apply_rl->value(), 42); + EXPECT_EQ(add_lr->value(), 42); + EXPECT_EQ(add_rl->value(), 42); + EXPECT_EQ(sub_lr->value(), 38); + EXPECT_EQ(sub_rl->value(), -38); + EXPECT_EQ(div_lr->value(), 20); + EXPECT_EQ(div_rl->value(), 0); + EXPECT_EQ(*cmp_lr, std::strong_ordering::greater); + EXPECT_EQ(*cmp_rl, std::strong_ordering::less); +} + +TEST(OperationsTest, MixedFrameworkOperatorsSupportUnderlyingOnBothSides) { + using namespace mcpplibs::primitives::operators; + using value_t = primitive; + + auto const lhs = value_t{40}; + short const rhs = 2; + + auto const add_lr = lhs + rhs; + auto const add_rl = rhs + lhs; + auto const sub_lr = lhs - rhs; + auto const sub_rl = rhs - lhs; + auto const eq_lr = lhs == static_cast(40); + auto const eq_rl = static_cast(40) == lhs; + auto const ne_lr = lhs != static_cast(41); + auto const ne_rl = static_cast(41) != lhs; + auto const cmp_lr = lhs <=> rhs; + auto const cmp_rl = rhs <=> lhs; + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + + ASSERT_TRUE(add_lr.has_value()); + ASSERT_TRUE(add_rl.has_value()); + ASSERT_TRUE(sub_lr.has_value()); + ASSERT_TRUE(sub_rl.has_value()); + ASSERT_TRUE(eq_lr.has_value()); + ASSERT_TRUE(eq_rl.has_value()); + ASSERT_TRUE(ne_lr.has_value()); + ASSERT_TRUE(ne_rl.has_value()); + ASSERT_TRUE(cmp_lr.has_value()); + ASSERT_TRUE(cmp_rl.has_value()); + + EXPECT_EQ(add_lr->value(), 42); + EXPECT_EQ(add_rl->value(), 42); + EXPECT_EQ(sub_lr->value(), 38); + EXPECT_EQ(sub_rl->value(), -38); + EXPECT_EQ(eq_lr->value(), 1); + EXPECT_EQ(eq_rl->value(), 1); + EXPECT_EQ(ne_lr->value(), 1); + EXPECT_EQ(ne_rl->value(), 1); + EXPECT_EQ(*cmp_lr, std::strong_ordering::greater); + EXPECT_EQ(*cmp_rl, std::strong_ordering::less); +} + TEST(OperationsTest, OperatorEqualDelegatesToDispatcher) { using namespace mcpplibs::primitives::operators; using value_t = primitive Date: Sun, 22 Mar 2026 11:30:32 +0800 Subject: [PATCH 23/28] fix: Fix compile errors in ex03 --- examples/ex03_value_policy.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/examples/ex03_value_policy.cpp b/examples/ex03_value_policy.cpp index 0ce7f52..e420aa6 100644 --- a/examples/ex03_value_policy.cpp +++ b/examples/ex03_value_policy.cpp @@ -29,16 +29,16 @@ int main() { policy::error::expected>; // Same overflow input for all three policies. - auto const lhs_checked = checked_t{65530}; - auto const rhs_checked = checked_t{20}; + auto const lhs_checked = checked_t{static_cast(65530)}; + auto const rhs_checked = checked_t{static_cast(20)}; auto const checked_result = operations::add(lhs_checked, rhs_checked); - auto const lhs_unchecked = unchecked_t{65530}; - auto const rhs_unchecked = unchecked_t{20}; + auto const lhs_unchecked = unchecked_t{static_cast(65530)}; + auto const rhs_unchecked = unchecked_t{static_cast(20)}; auto const unchecked_result = operations::add(lhs_unchecked, rhs_unchecked); - auto const lhs_saturating = saturating_t{65530}; - auto const rhs_saturating = saturating_t{20}; + auto const lhs_saturating = saturating_t{static_cast(65530)}; + auto const rhs_saturating = saturating_t{static_cast(20)}; auto const saturating_result = operations::add(lhs_saturating, rhs_saturating); @@ -48,11 +48,13 @@ int main() { std::cerr << "checked policy should report overflow\n"; return 1; } - if (!unchecked_result.has_value() || unchecked_result->value() != 14) { + if (!unchecked_result.has_value() || + unchecked_result->value() != static_cast(14)) { std::cerr << "unchecked policy should wrap\n"; return 1; } - if (!saturating_result.has_value() || saturating_result->value() != 65535) { + if (!saturating_result.has_value() || + saturating_result->value() != static_cast(65535)) { std::cerr << "saturating policy should clamp\n"; return 1; } From a86d30eed6106ffcaf97bc8a4481b12c7d55240f Mon Sep 17 00:00:00 2001 From: FrozenlemonTee <1115306170@qq.com> Date: Sun, 22 Mar 2026 12:37:40 +0800 Subject: [PATCH 24/28] fix: Implement safe numeric casting and error handling for numeric conversions Signed-off-by: FrozenlemonTee <1115306170@qq.com> --- src/operations/operators.cppm | 7 +++-- src/policy/impl.cppm | 59 +++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/src/operations/operators.cppm b/src/operations/operators.cppm index f5aeb21..3c04ca1 100644 --- a/src/operations/operators.cppm +++ b/src/operations/operators.cppm @@ -333,16 +333,17 @@ constexpr auto apply_assign(Lhs &lhs, Rhs const &rhs) auto const assigned_common = out->load(); if constexpr (std::same_as && - std::integral && std::integral) { + std::integral) { if (auto const kind = - policy::details::narrow_integral_error(assigned_common); + policy::details::narrow_numeric_error(assigned_common); kind.has_value()) { return std::unexpected( policy::details::to_error_payload(*kind)); } } - auto const assigned_rep = static_cast(assigned_common); + auto const assigned_rep = + policy::details::safe_numeric_cast(assigned_common); lhs.store(underlying::traits::from_rep(assigned_rep)); return out; } diff --git a/src/policy/impl.cppm b/src/policy/impl.cppm index 13112f8..ecd090a 100644 --- a/src/policy/impl.cppm +++ b/src/policy/impl.cppm @@ -1,5 +1,6 @@ module; #include +#include #include #include #include @@ -574,6 +575,64 @@ constexpr auto narrow_integral_error(SrcRep value) return std::nullopt; } } + +template +constexpr auto narrow_numeric_error(SrcRep value) + -> std::optional { + using dest_type = std::remove_cv_t; + using src_type = std::remove_cv_t; + + if constexpr (std::integral && std::integral) { + return narrow_integral_error(value); + } else if constexpr (std::integral && + std::floating_point) { + if (std::isnan(value)) { + return error::kind::domain_error; + } + if (std::isinf(value)) { + return value < static_cast(0) ? error::kind::underflow + : error::kind::overflow; + } + + auto const normalized = static_cast(value); + auto const min_value = + static_cast(std::numeric_limits::lowest()); + auto const max_value = + static_cast(std::numeric_limits::max()); + + if (normalized < min_value) { + return error::kind::underflow; + } + if (normalized > max_value) { + return error::kind::overflow; + } + return std::nullopt; + } else { + static_cast(value); + return std::nullopt; + } +} + +template +constexpr auto safe_numeric_cast(SrcRep value) noexcept -> DestRep { + using dest_type = std::remove_cv_t; + using src_type = std::remove_cv_t; + + if constexpr (std::integral && std::floating_point) { + if (auto const kind = narrow_numeric_error(value); + kind.has_value()) { + if (*kind == error::kind::overflow) { + return std::numeric_limits::max(); + } + if (*kind == error::kind::underflow) { + return std::numeric_limits::lowest(); + } + return dest_type{}; + } + } + + return static_cast(value); +} } // namespace details template Date: Sun, 22 Mar 2026 12:37:52 +0800 Subject: [PATCH 25/28] fix: Enhance numeric casting with safe casting policy for improved safety Signed-off-by: FrozenlemonTee <1115306170@qq.com> --- src/primitive/impl.cppm | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/primitive/impl.cppm b/src/primitive/impl.cppm index d19dda0..730d712 100644 --- a/src/primitive/impl.cppm +++ b/src/primitive/impl.cppm @@ -1,6 +1,7 @@ module; #include #include +#include export module mcpplibs.primitives.primitive.impl; @@ -61,8 +62,9 @@ private: underlying::traits>::rep_type; auto const source_rep = underlying::traits::to_rep(source); - auto const target_rep = static_cast( - static_cast(source_rep)); + auto const target_rep = + policy::details::safe_numeric_cast( + static_cast(source_rep)); return underlying::traits>::from_rep(target_rep); } From e30cc4c401be892dd14db7781255dc7aa9d83bd4 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee <1115306170@qq.com> Date: Sun, 22 Mar 2026 13:00:23 +0800 Subject: [PATCH 26/28] fix: Add backward-compatible aliases for existing downstream users Signed-off-by: FrozenlemonTee <1115306170@qq.com> --- src/primitive/traits.cppm | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/primitive/traits.cppm b/src/primitive/traits.cppm index e9e857b..e1fb5b4 100644 --- a/src/primitive/traits.cppm +++ b/src/primitive/traits.cppm @@ -44,3 +44,19 @@ struct traits> { }; } // namespace mcpplibs::primitives::meta + +// Backward-compatible aliases for existing downstream users. +export namespace mcpplibs::primitives::traits { +using policy_category [[deprecated]] = meta::policy_category; + +template +using make_primitive [[deprecated]] = meta::make_primitive; + +template +using make_primitive_t [[deprecated]] = meta::make_primitive_t; + +using default_policies [[deprecated]] = meta::default_policies; + +template +using primitive_traits [[deprecated]] = meta::traits; +} // namespace mcpplibs::primitives::traits From ef46e6461f8f77fe186a9b59528459c901f6a658 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee <1115306170@qq.com> Date: Sun, 22 Mar 2026 13:00:44 +0800 Subject: [PATCH 27/28] test: Add tests for handling out-of-range floating-point inputs in primitive types Signed-off-by: FrozenlemonTee <1115306170@qq.com> --- tests/basic/test_operations.cpp | 39 +++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tests/basic/test_operations.cpp b/tests/basic/test_operations.cpp index 75e97f5..a2cd1ff 100644 --- a/tests/basic/test_operations.cpp +++ b/tests/basic/test_operations.cpp @@ -445,6 +445,22 @@ TEST(OperationsTest, PrimitiveStoreAndCasAllowTransparentCrossCategory) { EXPECT_EQ(expected, 99.0); } +TEST(OperationsTest, PrimitiveTransparentStoreClampsOutOfRangeFloatingInput) { + using transparent_t = + primitive; + + auto value = transparent_t{0}; + + value.store(std::numeric_limits::infinity()); + EXPECT_EQ(value.load(), std::numeric_limits::max()); + + value.store(-std::numeric_limits::infinity()); + EXPECT_EQ(value.load(), std::numeric_limits::lowest()); + + value.store(std::numeric_limits::quiet_NaN()); + EXPECT_EQ(value.load(), 0); +} + TEST(OperationsTest, PrimitiveSpecialMembersSupportCrossUnderlyingWithCompatibleType) { using dst_t = primitive; @@ -858,6 +874,29 @@ TEST(OperationsTest, CompoundAssignmentKeepsLhsWhenOperationFails) { EXPECT_EQ(value.load(), 100); } +TEST(OperationsTest, + CompoundAssignmentCheckedRejectsFloatingToIntegralOutOfRange) { + using lhs_t = primitive; + using rhs_t = + primitive; + + auto value = lhs_t{7}; + + auto const overflow_result = + operations::add_assign(value, rhs_t{std::numeric_limits::max()}); + ASSERT_FALSE(overflow_result.has_value()); + EXPECT_EQ(overflow_result.error(), policy::error::kind::overflow); + EXPECT_EQ(value.load(), 7); + + auto const domain_result = operations::add_assign( + value, rhs_t{std::numeric_limits::quiet_NaN()}); + ASSERT_FALSE(domain_result.has_value()); + EXPECT_EQ(domain_result.error(), policy::error::kind::domain_error); + EXPECT_EQ(value.load(), 7); +} + TEST(OperationsTest, CompoundAssignmentSupportsMixedTypesWithCompatibleTypePolicy) { using namespace mcpplibs::primitives::operators; From 49794cd208e12c6d94ccaf1847f970b0dafdc974 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee <1115306170@qq.com> Date: Sun, 22 Mar 2026 13:01:03 +0800 Subject: [PATCH 28/28] fix: Update underlying traits to use appropriate representative types and ensure legacy aliases remain available Signed-off-by: FrozenlemonTee <1115306170@qq.com> --- tests/basic/test_templates.cpp | 45 ++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/tests/basic/test_templates.cpp b/tests/basic/test_templates.cpp index 6040194..9a5c9d9 100644 --- a/tests/basic/test_templates.cpp +++ b/tests/basic/test_templates.cpp @@ -379,15 +379,17 @@ struct mcpplibs::primitives::underlying::common_rep_traits struct mcpplibs::primitives::underlying::traits { using value_type = VoidCommonLhs; - using rep_type = VoidCommonLhs; + using rep_type = int; static constexpr bool enabled = true; static constexpr auto kind = category::integer; - static constexpr rep_type to_rep(value_type value) noexcept { return value; } + static constexpr rep_type to_rep(value_type value) noexcept { + return value.value; + } static constexpr value_type from_rep(rep_type value) noexcept { - return value; + return VoidCommonLhs{value}; } static constexpr bool is_valid_rep(rep_type) noexcept { return true; } @@ -395,30 +397,30 @@ template <> struct mcpplibs::primitives::underlying::traits { template <> struct mcpplibs::primitives::underlying::traits { using value_type = VoidCommonRhs; - using rep_type = VoidCommonRhs; + using rep_type = short; static constexpr bool enabled = true; static constexpr auto kind = category::integer; - static constexpr rep_type to_rep(value_type value) noexcept { return value; } + static constexpr rep_type to_rep(value_type value) noexcept { + return static_cast(value.value); + } static constexpr value_type from_rep(rep_type value) noexcept { - return value; + return VoidCommonRhs{value}; } static constexpr bool is_valid_rep(rep_type) noexcept { return true; } }; template <> -struct mcpplibs::primitives::underlying::common_rep_traits { +struct mcpplibs::primitives::underlying::common_rep_traits { using type = void; static constexpr bool enabled = true; }; template <> -struct mcpplibs::primitives::underlying::common_rep_traits { +struct mcpplibs::primitives::underlying::common_rep_traits { using type = void; static constexpr bool enabled = true; }; @@ -460,6 +462,24 @@ TEST(PrimitiveTraitsTest, UnderlyingTraitsDefaultsAndCustomRegistration) { (mcpplibs::primitives::underlying::traits::enabled)); } +TEST(PrimitiveTraitsTest, LegacyPrimitiveTraitsNamespaceAliasesRemainAvailable) { + using value_t = mcpplibs::primitives::primitive< + int, mcpplibs::primitives::policy::error::expected>; + using legacy_traits_t = + mcpplibs::primitives::traits::primitive_traits; + using meta_traits_t = mcpplibs::primitives::meta::traits; + + static_assert(std::same_as); + static_assert(std::same_as); + static_assert(std::same_as< + mcpplibs::primitives::traits::make_primitive_t< + int, typename legacy_traits_t::policies>, + value_t>); + SUCCEED(); +} + TEST(PrimitiveTraitsTest, UnderlyingTypeRequiresValidRepTypeAndCategoryConsistency) { EXPECT_TRUE((mcpplibs::primitives::underlying::traits::enabled)); @@ -558,6 +578,11 @@ TEST(PrimitiveTraitsTest, UnderlyingCommonRepCanBeCustomizedViaTraits) { } TEST(PrimitiveTraitsTest, TypePoliciesRequireNonVoidCommonRep) { + static_assert(std::is_arithmetic_v< + mcpplibs::primitives::underlying::traits::rep_type>); + static_assert(std::is_arithmetic_v< + mcpplibs::primitives::underlying::traits::rep_type>); + using compatible_handler_t = mcpplibs::primitives::policy::type::handler< mcpplibs::primitives::policy::type::compatible, mcpplibs::primitives::operations::Addition, VoidCommonLhs, VoidCommonRhs>;