From 5de343cdaa3150f5a9fa66443941fc1cc9e261ec Mon Sep 17 00:00:00 2001 From: FrozenLemonTee Date: Mon, 16 Mar 2026 22:12:59 +0800 Subject: [PATCH 01/57] refactor: Remove unit tests and examples to baseline --- examples/basic.cpp | 54 +-------- tests/basic/test_operations.cpp | 187 +------------------------------- 2 files changed, 7 insertions(+), 234 deletions(-) diff --git a/examples/basic.cpp b/examples/basic.cpp index 83b2790..42abff4 100644 --- a/examples/basic.cpp +++ b/examples/basic.cpp @@ -1,50 +1,6 @@ -#include -#include +/* + * Example temporarily disabled while operations module is rebuilt. + * Previous demo code intentionally removed from compilation path. + */ -import mcpplibs.primitives; - -int main() { - using namespace mcpplibs::primitives; - using namespace mcpplibs::primitives::policy; - using namespace mcpplibs::primitives::operators; - using namespace mcpplibs::primitives::types; - - // Operators - I32<> a{3}; - I32<> b{4}; - auto c = a + b; // primitive - std::cout << "3 + 4 = " << static_cast(c) << "\n"; - - // Error handling - I32 lhs{1}; - I32 rhs{std::numeric_limits::max()}; - - try { - auto res = lhs + rhs; - } catch (const std::exception &e) { - std::cout << "An exception occurred: " << e.what() << "\n"; - } - - I64 lhs2{1}; - I64 rhs2{std::numeric_limits::max()}; - - auto res2 = lhs2 + rhs2; - - // Saturating - I32 s1{std::numeric_limits::max()}; - I32 s2{1}; - auto sat = s1 + s2; // saturating -> stays max - std::cout << "saturating max + 1 = " << static_cast(sat) - << "\n"; - - // Mixed-type addition - using expected_type = I64; - expected_type L1{5}; - I32 L2{6}; - auto mix = L1 + L2; // common_type -> I64 - std::cout << "5 + 6 = " << mix.value() << "\n"; - std::cout << std::boolalpha; - std::cout << "Does type of mix is I64: " << std::same_as << std::endl; - - return 0; -} +int main() { return 0; } diff --git a/tests/basic/test_operations.cpp b/tests/basic/test_operations.cpp index a74c136..44e3fb7 100644 --- a/tests/basic/test_operations.cpp +++ b/tests/basic/test_operations.cpp @@ -1,185 +1,2 @@ -#include -#include -#include - -import mcpplibs.primitives; - -using namespace mcpplibs::primitives; - -namespace { - -struct debug_value_policy {}; -struct debug_type_policy {}; -struct debug_error_policy {}; -struct debug_concurrency_policy {}; - -} // namespace - -template <> -struct policy::traits { - using policy_type = debug_value_policy; - static constexpr bool enabled = true; - static constexpr auto kind = category::value; -}; - -template <> -struct policy::traits { - using policy_type = debug_type_policy; - static constexpr bool enabled = true; - static constexpr auto kind = category::type; -}; - -template <> -struct policy::traits { - using policy_type = debug_error_policy; - static constexpr bool enabled = true; - static constexpr auto kind = category::error; -}; - -template <> -struct policy::traits { - using policy_type = debug_concurrency_policy; - static constexpr bool enabled = true; - static constexpr auto kind = - category::concurrency; -}; - -template -struct operations::value_policy_behavior< - debug_value_policy, OpTag> { - template - static constexpr T add(T lhs, T rhs) noexcept { - return static_cast(lhs + rhs + 100); - } - - template - static constexpr T sub(T lhs, T rhs) noexcept { - return static_cast(lhs - rhs - 100); - } - - template - static constexpr T mul(T lhs, T rhs) noexcept { - return static_cast(lhs * rhs * 2); - } -}; - -template -struct operations::type_policy_behavior< - debug_type_policy, OpTag> { - template - using result_type = long long; - - template - static constexpr Out cast_lhs(In const &value) noexcept { - return static_cast(value + 1); - } - - template - static constexpr Out cast_rhs(In const &value) noexcept { - return static_cast(value + 2); - } -}; - -template -struct operations::error_policy_behavior< - debug_error_policy, OpTag> { - template - static constexpr Result evaluate(Fn &&fn) noexcept(noexcept(fn())) { - return static_cast(fn() + static_cast(1000)); - } -}; - -template -struct operations::concurrency_policy_behavior< - debug_concurrency_policy, OpTag> { - template - static constexpr auto execute(Fn &&fn) noexcept(noexcept(fn())) - -> decltype(fn()) { - return fn() + 7; - } -}; - - - -template <> -struct operations::value_policy_behavior< - debug_value_policy, operations::add_tag> { - template - static constexpr T add(T lhs, T rhs) noexcept { - return static_cast(lhs + rhs + 200); - } - - template - static constexpr T sub(T lhs, T rhs) noexcept { - return static_cast(lhs - rhs - 100); - } - - template - static constexpr T mul(T lhs, T rhs) noexcept { - return static_cast(lhs * rhs * 2); - } -}; - -TEST(OperationsTest, UnderlyingOperationsWork) { - static_assert(operations::binary_operation); - static_assert(operations::binary_operation); - static_assert(operations::binary_operation); - - EXPECT_EQ(operations::add(2, 3), 5); - EXPECT_EQ(operations::sub(9, 4), 5); - EXPECT_EQ(operations::mul(3, 4), 12); -} - -TEST(OperationsTest, PrimitiveOperationsWork) { - using namespace mcpplibs::primitives::types; - - using lhs_t = I8<>; - using rhs_t = I8<>; - - lhs_t lhs{10}; - rhs_t rhs{5}; - - auto added = operations::add(lhs, rhs); - auto subed = operations::sub(lhs, rhs); - auto muled = operations::mul(lhs, rhs); - - EXPECT_EQ(added.value(), 15); - EXPECT_EQ(subed.value(), 5); - EXPECT_EQ(muled.value(), 50); -} - -TEST(OperationsTest, PolicyBehaviorTraitsCoverAllPolicyCategories) { - EXPECT_EQ((operations::add.operator()< - debug_value_policy, debug_type_policy, debug_error_policy, - debug_concurrency_policy>(2, 3)), - 1215); - EXPECT_EQ((operations::sub.operator()< - debug_value_policy, debug_type_policy, debug_error_policy, - debug_concurrency_policy>(9, 4)), - 911); - EXPECT_EQ((operations::mul.operator()< - debug_value_policy, debug_type_policy, debug_error_policy, - debug_concurrency_policy>(3, 4)), - 1055); -} - - -TEST(OperationsTest, BuiltinPolicyTagsAreSpecialized) { - static_assert(std::is_same_v< - operations::result_t, - int>); - static_assert(std::is_same_v< - operations::result_t, - double>); - - constexpr int kMax = std::numeric_limits::max(); - constexpr int kMin = std::numeric_limits::min(); - - EXPECT_EQ((operations::add.operator()(kMax, 1)), - kMax); - EXPECT_EQ((operations::sub.operator()(kMin, 1)), - kMin); - EXPECT_EQ((operations::mul.operator()(kMax, 2)), - kMax); -} +// Operations tests are intentionally removed during the operations module +// rewrite. From b07074e8a7c912481d090c116a541e0df2bc397d Mon Sep 17 00:00:00 2001 From: FrozenLemonTee Date: Mon, 16 Mar 2026 22:13:15 +0800 Subject: [PATCH 02/57] refactor: Remove src code to baseline --- src/operations/impl.cppm | 603 --------------------------------- src/operations/operations.cppm | 6 +- src/operations/operators.cppm | 140 +------- src/operations/traits.cppm | 165 --------- 4 files changed, 2 insertions(+), 912 deletions(-) diff --git a/src/operations/impl.cppm b/src/operations/impl.cppm index 647f1b4..9dc82dc 100644 --- a/src/operations/impl.cppm +++ b/src/operations/impl.cppm @@ -1,606 +1,3 @@ module; -#include -#include -#include -#include -#include -#include -#include export module mcpplibs.primitives.operations.impl; - -import mcpplibs.primitives.operations.traits; -import mcpplibs.primitives.policy; -import mcpplibs.primitives.primitive; -import mcpplibs.primitives.underlying; - -export namespace mcpplibs::primitives::operations { - -namespace details { - -template -struct is_expected : std::false_type {}; - -template -struct is_expected> : std::true_type {}; - -template -inline constexpr bool is_expected_v = is_expected::value; - -template constexpr bool add_overflow(T lhs, T rhs) noexcept { - if constexpr (!std::is_integral_v) { - return false; - } else if constexpr (std::is_signed_v) { - using limits = std::numeric_limits; - return (rhs > 0 && lhs > limits::max() - rhs) || - (rhs < 0 && lhs < limits::min() - rhs); - } else { - using limits = std::numeric_limits; - return lhs > limits::max() - rhs; - } -} - -template constexpr bool sub_overflow(T lhs, T rhs) noexcept { - if constexpr (!std::is_integral_v) { - return false; - } else if constexpr (std::is_signed_v) { - using limits = std::numeric_limits; - return (rhs < 0 && lhs > limits::max() + rhs * static_cast(-1)) || - (rhs > 0 && lhs < limits::min() + rhs); - } else { - return lhs < rhs; - } -} - -template constexpr bool mul_overflow(T lhs, T rhs) noexcept { - if constexpr (!std::is_integral_v) { - return false; - } else if (lhs == 0 || rhs == 0) { - return false; - } else if constexpr (std::is_signed_v) { - using limits = std::numeric_limits; - if (lhs == -1) { - return rhs == limits::min(); - } - if (rhs == -1) { - return lhs == limits::min(); - } - if (lhs > 0) { - return (rhs > 0) ? lhs > limits::max() / rhs : rhs < limits::min() / lhs; - } - return (rhs > 0) ? lhs < limits::min() / rhs - : lhs != 0 && rhs < limits::max() / lhs; - } else { - using limits = std::numeric_limits; - return lhs > limits::max() / rhs; - } -} - -template constexpr T saturating_add(T lhs, T rhs) noexcept { - if constexpr (!std::is_integral_v) { - return static_cast(lhs + rhs); - } else { - using limits = std::numeric_limits; - if (!add_overflow(lhs, rhs)) { - return static_cast(lhs + rhs); - } - if constexpr (std::is_signed_v) { - return rhs >= 0 ? limits::max() : limits::min(); - } else { - return limits::max(); - } - } -} - -template constexpr T saturating_sub(T lhs, T rhs) noexcept { - if constexpr (!std::is_integral_v) { - return static_cast(lhs - rhs); - } else { - using limits = std::numeric_limits; - if (!sub_overflow(lhs, rhs)) { - return static_cast(lhs - rhs); - } - if constexpr (std::is_signed_v) { - return rhs > 0 ? limits::min() : limits::max(); - } else { - return limits::min(); - } - } -} - -template constexpr T saturating_mul(T lhs, T rhs) noexcept { - if constexpr (!std::is_integral_v) { - return static_cast(lhs * rhs); - } else { - using limits = std::numeric_limits; - if (!mul_overflow(lhs, rhs)) { - return static_cast(lhs * rhs); - } - if constexpr (std::is_signed_v) { - return ((lhs < 0) ^ (rhs < 0)) ? limits::min() : limits::max(); - } else { - return limits::max(); - } - } -} - -} // namespace details - -// ===== Built-in value policy behaviors ===== - -template -struct value_policy_behavior { - template static T add(T lhs, T rhs) { - if (details::add_overflow(lhs, rhs)) { - throw std::overflow_error("overflow in add"); - } - return static_cast(lhs + rhs); - } - - template static T sub(T lhs, T rhs) { - if (details::sub_overflow(lhs, rhs)) { - throw std::overflow_error("overflow in sub"); - } - return static_cast(lhs - rhs); - } - - template static T mul(T lhs, T rhs) { - if (details::mul_overflow(lhs, rhs)) { - throw std::overflow_error("overflow in mul"); - } - return static_cast(lhs * rhs); - } -}; - -template -struct value_policy_behavior { - template static constexpr T add(T lhs, T rhs) noexcept { - return static_cast(lhs + rhs); - } - - template static constexpr T sub(T lhs, T rhs) noexcept { - return static_cast(lhs - rhs); - } - - template static constexpr T mul(T lhs, T rhs) noexcept { - return static_cast(lhs * rhs); - } -}; - -template -struct value_policy_behavior { - template static constexpr T add(T lhs, T rhs) noexcept { - return details::saturating_add(lhs, rhs); - } - - template static constexpr T sub(T lhs, T rhs) noexcept { - return details::saturating_sub(lhs, rhs); - } - - template static constexpr T mul(T lhs, T rhs) noexcept { - return details::saturating_mul(lhs, rhs); - } -}; - -// ===== Built-in type policy behaviors ===== - -template -struct type_policy_behavior { - template using result_type = std::remove_cv_t; - - template - static constexpr Out cast_lhs(In const &value) noexcept { - return static_cast(value); - } - - template - static constexpr Out cast_rhs(In const &value) noexcept { - static_assert(std::same_as, std::remove_cv_t>, - "strict_type requires both operands to have same type"); - return static_cast(value); - } -}; - -template -struct type_policy_behavior { - template - using result_type = std::common_type_t; - - template - static constexpr Out cast_lhs(In const &value) noexcept { - return static_cast(value); - } - - template - static constexpr Out cast_rhs(In const &value) noexcept { - static_assert(underlying::traits>::kind == - underlying::traits>::kind, - "category_compatible_type requires same underlying category"); - return static_cast(value); - } -}; - -template -struct type_policy_behavior { - template - using result_type = std::common_type_t; - - template - static constexpr Out cast_lhs(In const &value) noexcept { - return static_cast(value); - } - - template - static constexpr Out cast_rhs(In const &value) noexcept { - return static_cast(value); - } -}; - -// ===== Built-in error policy behaviors ===== - -template -struct error_policy_behavior { - template - static constexpr Result evaluate(Fn &&fn) noexcept(noexcept(fn())) { - return static_cast(fn()); - } -}; - -template -struct error_policy_behavior { - template - static constexpr Result evaluate(Fn &&fn) noexcept(noexcept(fn())) { - if constexpr (details::is_expected_v) { - return Result{std::in_place, std::forward(fn)()}; - } else { - return static_cast(fn()); - } - } -}; - -template -struct error_policy_behavior { - template - static Result evaluate(Fn &&fn) noexcept { - if constexpr (noexcept(fn())) { - return static_cast(fn()); - } else { - try { - return static_cast(fn()); - } catch (...) { - std::terminate(); - } - } - } -}; - -// ===== Built-in concurrency policy behaviors ===== - -template -struct concurrency_policy_behavior { - template - static constexpr auto execute(Fn &&fn) noexcept(noexcept(fn())) - -> decltype(fn()) { - return fn(); - } -}; - -template -struct concurrency_policy_behavior { - template - static auto execute(Fn &&fn) noexcept(noexcept(fn())) -> decltype(fn()) { - std::atomic_signal_fence(std::memory_order_seq_cst); - auto result = fn(); - std::atomic_signal_fence(std::memory_order_seq_cst); - return result; - } -}; - -// default underlying operations - -template -struct traits { - static constexpr bool enabled = true; - static constexpr bool noexcept_invocable = true; - - using value_policy = resolved_value_policy_t; - using type_policy = resolved_type_policy_t; - using error_policy = resolved_error_policy_t; - using concurrency_policy = resolved_concurrency_policy_t; - - using result_type = - type_policy_behavior::template result_type; - - static constexpr result_type invoke(L const &lhs, R const &rhs) noexcept { - return concurrency_policy_behavior< - concurrency_policy, add_tag>::execute([&]() constexpr noexcept { - return error_policy_behavior::template evaluate< - result_type>([&]() constexpr noexcept { - return value_policy_behavior::template add< - result_type>( - type_policy_behavior::template cast_lhs(lhs), - type_policy_behavior::template cast_rhs(rhs)); - }); - }); - } -}; - -template -struct traits { - static constexpr bool enabled = true; - static constexpr bool noexcept_invocable = true; - - using value_policy = resolved_value_policy_t; - using type_policy = resolved_type_policy_t; - using error_policy = resolved_error_policy_t; - using concurrency_policy = resolved_concurrency_policy_t; - - using result_type = - type_policy_behavior::template result_type; - - static constexpr result_type invoke(L const &lhs, R const &rhs) noexcept { - return concurrency_policy_behavior< - concurrency_policy, sub_tag>::execute([&]() constexpr noexcept { - return error_policy_behavior::template evaluate< - result_type>([&]() constexpr noexcept { - return value_policy_behavior::template sub< - result_type>( - type_policy_behavior::template cast_lhs(lhs), - type_policy_behavior::template cast_rhs(rhs)); - }); - }); - } -}; - -template -struct traits { - static constexpr bool enabled = true; - static constexpr bool noexcept_invocable = true; - - using value_policy = resolved_value_policy_t; - using type_policy = resolved_type_policy_t; - using error_policy = resolved_error_policy_t; - using concurrency_policy = resolved_concurrency_policy_t; - - using result_type = - type_policy_behavior::template result_type; - - static constexpr result_type invoke(L const &lhs, R const &rhs) noexcept { - return concurrency_policy_behavior< - concurrency_policy, mul_tag>::execute([&]() constexpr noexcept { - return error_policy_behavior::template evaluate< - result_type>([&]() constexpr noexcept { - return value_policy_behavior::template mul< - result_type>( - type_policy_behavior::template cast_lhs(lhs), - type_policy_behavior::template cast_rhs(rhs)); - }); - }); - } -}; - -// primitive operations (keep lhs policy set by default) -template -struct traits, primitive, - Policies...> { - static constexpr bool enabled = true; - static constexpr bool noexcept_invocable = true; - - using value_policy = resolved_value_policy_t; - using type_policy = resolved_type_policy_t; - using error_policy = resolved_error_policy_t; - using concurrency_policy = resolved_concurrency_policy_t; - - using rep_type = - type_policy_behavior::template result_type; - using raw_result_type = primitive; - using result_type = std::conditional_t< - std::is_same_v, - std::expected, raw_result_type>; - - static result_type invoke(primitive const &lhs, - primitive const &rhs) { - // Prepare casted representations - rep_type lhs_rep = - type_policy_behavior::template cast_lhs( - lhs.value()); - rep_type rhs_rep = - type_policy_behavior::template cast_rhs( - rhs.value()); - - // If value policy is checked, handle overflow according to error policy. - if constexpr (std::is_same_v) { - if (details::add_overflow(lhs_rep, rhs_rep)) { - if constexpr (std::is_same_v) { - throw std::overflow_error("overflow in add"); - } else if constexpr (std::is_same_v) { - std::terminate(); - } else if constexpr (std::is_same_v) { - return std::unexpected(std::overflow_error("overflow in add")); - } - } - } - - const auto rep = concurrency_policy_behavior::execute([&]() { - return error_policy_behavior::template evaluate([&]() { - return value_policy_behavior::template add(lhs_rep, - rhs_rep); - }); - }); - - if constexpr (std::is_same_v) { - return raw_result_type{rep}; - } else { - return raw_result_type{rep}; - } - } -}; - -template -struct traits, primitive, - Policies...> { - static constexpr bool enabled = true; - static constexpr bool noexcept_invocable = true; - - using value_policy = resolved_value_policy_t; - using type_policy = resolved_type_policy_t; - using error_policy = resolved_error_policy_t; - using concurrency_policy = resolved_concurrency_policy_t; - - using rep_type = - type_policy_behavior::template result_type; - using raw_result_type = primitive; - using result_type = std::conditional_t< - std::is_same_v, - std::expected, raw_result_type>; - - static result_type invoke(primitive const &lhs, - primitive const &rhs) { - rep_type lhs_rep = - type_policy_behavior::template cast_lhs( - lhs.value()); - rep_type rhs_rep = - type_policy_behavior::template cast_rhs( - rhs.value()); - - if constexpr (std::is_same_v) { - if (details::sub_overflow(lhs_rep, rhs_rep)) { - if constexpr (std::is_same_v) { - throw std::overflow_error("overflow in sub"); - } else if constexpr (std::is_same_v) { - std::terminate(); - } else if constexpr (std::is_same_v) { - return std::unexpected(std::overflow_error("overflow in sub")); - } - } - } - - const auto rep = concurrency_policy_behavior::execute([&]() { - return error_policy_behavior::template evaluate([&]() { - return value_policy_behavior::template sub< - rep_type>(lhs_rep, rhs_rep); - }); - }); - - if constexpr (std::is_same_v) { - return raw_result_type{rep}; - } else { - return raw_result_type{rep}; - } - } -}; - -template -struct traits, primitive, - Policies...> { - static constexpr bool enabled = true; - static constexpr bool noexcept_invocable = true; - - using value_policy = resolved_value_policy_t; - using type_policy = resolved_type_policy_t; - using error_policy = resolved_error_policy_t; - using concurrency_policy = resolved_concurrency_policy_t; - - using rep_type = - type_policy_behavior::template result_type; - using raw_result_type = primitive; - using result_type = std::conditional_t< - std::is_same_v, - std::expected, raw_result_type>; - - static result_type invoke(primitive const &lhs, - primitive const &rhs) { - rep_type lhs_rep = - type_policy_behavior::template cast_lhs( - lhs.value()); - rep_type rhs_rep = - type_policy_behavior::template cast_rhs( - rhs.value()); - - if constexpr (std::is_same_v) { - if (details::mul_overflow(lhs_rep, rhs_rep)) { - if constexpr (std::is_same_v) { - throw std::overflow_error("overflow in mul"); - } else if constexpr (std::is_same_v) { - std::terminate(); - } else if constexpr (std::is_same_v) { - return std::unexpected(std::overflow_error("overflow in mul")); - } - } - } - - const auto rep = concurrency_policy_behavior::execute([&]() { - return error_policy_behavior::template evaluate([&]() { - return value_policy_behavior::template mul< - rep_type>(lhs_rep, rhs_rep); - }); - }); - - if constexpr (std::is_same_v) { - return raw_result_type{rep}; - } else { - return raw_result_type{rep}; - } - } -}; - -struct add_fn { - template - requires binary_operation - auto operator()(L &&lhs, R &&rhs) const - -> result_t { - using impl = traits, std::remove_cvref_t, - Policies...>; - return impl::invoke(lhs, rhs); - } -}; - -struct sub_fn { - template - requires binary_operation - auto operator()(L &&lhs, R &&rhs) const - -> result_t { - using impl = traits, std::remove_cvref_t, - Policies...>; - return impl::invoke(lhs, rhs); - } -}; - -struct mul_fn { - template - requires binary_operation - auto operator()(L &&lhs, R &&rhs) const - -> result_t { - using impl = traits, std::remove_cvref_t, - Policies...>; - return impl::invoke(lhs, rhs); - } -}; - -inline constexpr add_fn add{}; -inline constexpr sub_fn sub{}; -inline constexpr mul_fn mul{}; - -} // namespace mcpplibs::primitives::operations diff --git a/src/operations/operations.cppm b/src/operations/operations.cppm index f7efa82..af7236a 100644 --- a/src/operations/operations.cppm +++ b/src/operations/operations.cppm @@ -1,7 +1,3 @@ module; -export module mcpplibs.primitives.operations; - -export import mcpplibs.primitives.operations.traits; -export import mcpplibs.primitives.operations.impl; -export import mcpplibs.primitives.operations.operators; \ No newline at end of file +export module mcpplibs.primitives.operations; \ No newline at end of file diff --git a/src/operations/operators.cppm b/src/operations/operators.cppm index e7e9219..6454f63 100644 --- a/src/operations/operators.cppm +++ b/src/operations/operators.cppm @@ -1,141 +1,3 @@ module; -#include -export module mcpplibs.primitives.operations.operators; -import mcpplibs.primitives.underlying; -import mcpplibs.primitives.policy; -import mcpplibs.primitives.primitive; -import mcpplibs.primitives.operations.impl; - -export namespace mcpplibs::primitives::operators { -template - requires std_numeric -constexpr auto operator~(const primitive &p) { - // Placeholder: unary bitwise NOT not yet forwarded to operations layer. - // TODO: forward to operations::bit_not when implemented. - return primitives::primitive(~p.value()); -} - -template - requires std_numeric -constexpr auto operator+(const primitive &p) { - // Placeholder: unary plus not yet forwarded to operations layer. - // TODO: forward to operations::unary_plus when implemented. - return primitives::primitive(+p.value()); -} - -template - requires std_numeric -constexpr auto operator-(const primitive &p) { - // Placeholder: unary minus not yet forwarded to operations layer. - // TODO: forward to operations::unary_neg when implemented. - return primitives::primitive(-p.value()); -} - -template - requires std_numeric && std_numeric -constexpr auto operator+(const primitive &lhs, - const primitive &rhs) { - return operations::add(lhs, rhs); -} - -template - requires std_numeric && std_numeric -constexpr auto operator-(const primitive &lhs, - const primitive &rhs) { - return operations::sub(lhs, rhs); -} - -template -constexpr auto operator*(const primitive &lhs, - const primitive &rhs) { - return operations::mul(lhs, rhs); -} - -template -constexpr auto operator/(const primitive &lhs, - const primitive &rhs) { - // Placeholder: division not yet forwarded to operations layer. - // TODO: forward to operations::div when implemented (handle policies). - using result_type = std::common_type_t; - return primitives::primitive( - static_cast(lhs.value()) / - static_cast(rhs.value())); -} - -template -constexpr auto operator%(const primitive &lhs, - const primitive &rhs) { - // Placeholder: modulo not yet forwarded to operations layer. - // TODO: forward to operations::mod when implemented (handle policies). - using result_type = std::common_type_t; - return primitives::primitive( - static_cast(lhs.value()) % - static_cast(rhs.value())); -} - -template -constexpr auto operator<<(const primitive &lhs, - const primitive &rhs) { - // Placeholder: left shift not yet forwarded to operations layer. - // TODO: forward to operations::shl when implemented (handle policies). - using result_type = std::common_type_t; - return primitives::primitive( - static_cast(lhs.value()) - << static_cast(rhs.value())); -} - -template -constexpr auto operator>>(const primitive &lhs, - const primitive &rhs) { - // Placeholder: right shift not yet forwarded to operations layer. - // TODO: forward to operations::shr when implemented (handle policies). - using result_type = std::common_type_t; - return primitives::primitive( - static_cast(lhs.value()) >> - static_cast(rhs.value())); -} - -template -constexpr auto operator&(const primitive &lhs, - const primitive &rhs) { - // Placeholder: bitwise AND not yet forwarded to operations layer. - // TODO: forward to operations::bit_and when implemented (handle policies). - using result_type = std::common_type_t; - return primitives::primitive( - static_cast(lhs.value()) & - static_cast(rhs.value())); -} - -template -constexpr auto operator|(const primitive &lhs, - const primitive &rhs) { - // Placeholder: bitwise OR not yet forwarded to operations layer. - // TODO: forward to operations::bit_or when implemented (handle policies). - using result_type = std::common_type_t; - return primitives::primitive( - static_cast(lhs.value()) | - static_cast(rhs.value())); -} - -template -constexpr auto operator^(const primitive &lhs, - const primitive &rhs) { - // Placeholder: bitwise XOR not yet forwarded to operations layer. - // TODO: forward to operations::bit_xor when implemented (handle policies). - using result_type = std::common_type_t; - return primitives::primitive( - static_cast(lhs.value()) ^ - static_cast(rhs.value())); -} -} // namespace mcpplibs::primitives::operators \ No newline at end of file +export module mcpplibs.primitives.operations.operators; \ No newline at end of file diff --git a/src/operations/traits.cppm b/src/operations/traits.cppm index ac18323..9261055 100644 --- a/src/operations/traits.cppm +++ b/src/operations/traits.cppm @@ -1,168 +1,3 @@ module; -#include -#include -#include export module mcpplibs.primitives.operations.traits; - -import mcpplibs.primitives.policy; -import mcpplibs.primitives.primitive; -import mcpplibs.primitives.underlying; - -export namespace mcpplibs::primitives::operations { - -struct add_tag {}; -struct sub_tag {}; -struct mul_tag {}; - -template -struct traits { - static constexpr bool enabled = false; - static constexpr bool noexcept_invocable = false; - using result_type = void; -}; - -template -concept binary_operation = - traits, std::remove_cvref_t, - Policies...>::enabled; - -template -concept nothrow_binary_operation = - binary_operation && - traits, std::remove_cvref_t, - Policies...>::noexcept_invocable; - -template -using result_t = - traits, std::remove_cvref_t, - Policies...>::result_type; - -namespace details { - -template -struct first_value_policy_impl { - using type = Fallback; -}; - -template -struct first_value_policy_impl { - using type = std::conditional_t< - policy::value_policy, First, - typename first_value_policy_impl::type>; -}; - -template -struct first_type_policy_impl { - using type = Fallback; -}; - -template -struct first_type_policy_impl { - using type = std::conditional_t< - policy::type_policy, First, - typename first_type_policy_impl::type>; -}; - -template -struct first_error_policy_impl { - using type = Fallback; -}; - -template -struct first_error_policy_impl { - using type = std::conditional_t< - policy::error_policy, First, - typename first_error_policy_impl::type>; -}; - -template -struct first_concurrency_policy_impl { - using type = Fallback; -}; - -template -struct first_concurrency_policy_impl { - using type = std::conditional_t< - policy::concurrency_policy, First, - typename first_concurrency_policy_impl::type>; -}; - -} // namespace details - -template -struct value_policy_behavior { - template - static constexpr T add(T lhs, T rhs) noexcept { - return static_cast(lhs + rhs); - } - - template - static constexpr T sub(T lhs, T rhs) noexcept { - return static_cast(lhs - rhs); - } - - template - static constexpr T mul(T lhs, T rhs) noexcept { - return static_cast(lhs * rhs); - } -}; - -template -struct type_policy_behavior { - template - using result_type = std::common_type_t; - - template - static constexpr Out cast_lhs(In const &value) noexcept { - return static_cast(value); - } - - template - static constexpr Out cast_rhs(In const &value) noexcept { - return static_cast(value); - } -}; - -template -struct error_policy_behavior { - template - static constexpr Result evaluate(Fn &&fn) noexcept(noexcept(fn())) { - return static_cast(std::forward(fn)()); - } -}; - -template -struct concurrency_policy_behavior { - template - static constexpr auto execute(Fn &&fn) noexcept(noexcept(fn())) - -> decltype(fn()) { - return std::forward(fn)(); - } -}; - -template -using resolved_value_policy_t = - details::first_value_policy_impl< - std::tuple_element_t<0, policy::common_policies_t>, - Policies...>::type; - -template -using resolved_type_policy_t = - details::first_type_policy_impl< - std::tuple_element_t<1, policy::common_policies_t>, - Policies...>::type; - -template -using resolved_error_policy_t = - details::first_error_policy_impl< - std::tuple_element_t<2, policy::common_policies_t>, - Policies...>::type; - -template -using resolved_concurrency_policy_t = - details::first_concurrency_policy_impl< - std::tuple_element_t<3, policy::common_policies_t>, - Policies...>::type; - -} // namespace mcpplibs::primitives::operations From e80c657b659c89e4bd34a4cc798a43a2bce96d02 Mon Sep 17 00:00:00 2001 From: FrozenLemonTee Date: Mon, 16 Mar 2026 23:26:11 +0800 Subject: [PATCH 03/57] refactor: Add operations::traits to define operations --- src/operations/impl.cppm | 41 ++++++++++++++++++++++++++++++++++++-- src/operations/traits.cppm | 30 ++++++++++++++++++++++++++-- 2 files changed, 67 insertions(+), 4 deletions(-) diff --git a/src/operations/impl.cppm b/src/operations/impl.cppm index 9dc82dc..9c41236 100644 --- a/src/operations/impl.cppm +++ b/src/operations/impl.cppm @@ -1,3 +1,40 @@ -module; - export module mcpplibs.primitives.operations.impl; + +import mcpplibs.primitives.operations.traits; + +export namespace mcpplibs::primitives::operations { + +struct Addition {}; +struct Subtraction {}; +struct Multiplication {}; +struct Division {}; + +template <> struct traits { + using op_tag = Addition; + + static constexpr bool enabled = true; + static constexpr auto arity = dimension::binary; +}; + +template <> struct traits { + using op_tag = Subtraction; + + static constexpr bool enabled = true; + static constexpr auto arity = dimension::binary; +}; + +template <> struct traits { + using op_tag = Multiplication; + + static constexpr bool enabled = true; + static constexpr auto arity = dimension::binary; +}; + +template <> struct traits { + using op_tag = Division; + + static constexpr bool enabled = true; + static constexpr auto arity = dimension::binary; +}; + +} // namespace mcpplibs::primitives::operations \ No newline at end of file diff --git a/src/operations/traits.cppm b/src/operations/traits.cppm index 9261055..bfdfe07 100644 --- a/src/operations/traits.cppm +++ b/src/operations/traits.cppm @@ -1,3 +1,29 @@ -module; - export module mcpplibs.primitives.operations.traits; + +export namespace mcpplibs::primitives::operations { + +enum class dimension : unsigned char { + unary = 1, + binary = 2, +}; + +template struct traits { + using op_tag = OpTag; + + // Operation metadata hooks (default-disabled). + static constexpr bool enabled = false; + static constexpr auto arity = static_cast(0); +}; + +template +concept operation = traits::enabled; + +template +concept unary_operation = + operation && traits::arity == dimension::unary; + +template +concept binary_operation = + operation && traits::arity == dimension::binary; + +} // namespace mcpplibs::primitives::operations \ No newline at end of file From 36e8d79b2c9f0ac9a408b1ca1e2262bdd7e141e6 Mon Sep 17 00:00:00 2001 From: FrozenLemonTee Date: Mon, 16 Mar 2026 23:49:21 +0800 Subject: [PATCH 04/57] refactor: Move concepts to traits.cppm --- src/policy/impl.cppm | 16 ---------------- src/policy/traits.cppm | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/policy/impl.cppm b/src/policy/impl.cppm index 1891055..d72e3a3 100644 --- a/src/policy/impl.cppm +++ b/src/policy/impl.cppm @@ -72,20 +72,4 @@ template <> struct traits { static constexpr auto kind = category::concurrency; }; -template -concept policy_type = traits

::enabled; - -template -concept value_policy = policy_type

&& (traits

::kind == category::value); - -template -concept type_policy = policy_type

&& (traits

::kind == category::type); - -template -concept error_policy = policy_type

&& (traits

::kind == category::error); - -template -concept concurrency_policy = - policy_type

&& (traits

::kind == category::concurrency); - } // namespace mcpplibs::primitives::policy diff --git a/src/policy/traits.cppm b/src/policy/traits.cppm index 6b2af3d..e3c6f26 100644 --- a/src/policy/traits.cppm +++ b/src/policy/traits.cppm @@ -33,6 +33,22 @@ template struct traits { static constexpr auto kind = static_cast(-1); }; +template +concept policy_type = traits

::enabled; + +template +concept value_policy = policy_type

&& (traits

::kind == category::value); + +template +concept type_policy = policy_type

&& (traits

::kind == category::type); + +template +concept error_policy = policy_type

&& (traits

::kind == category::error); + +template +concept concurrency_policy = + policy_type

&& (traits

::kind == category::concurrency); + // Customizable priority hooks — users can specialize these templates to // change how the library decides which policy wins when multiple are // provided. Specializations live in implementation or user code. From e6b96686301e06f5687193fb6dcdbec2c06a74ce Mon Sep 17 00:00:00 2001 From: FrozenLemonTee Date: Mon, 16 Mar 2026 23:57:58 +0800 Subject: [PATCH 05/57] refactor: Optimize the internal structure of the policy module --- src/policy/impl.cppm | 20 ++++++++ src/policy/policy.cppm | 1 + src/policy/traits.cppm | 110 ---------------------------------------- src/policy/utility.cppm | 90 ++++++++++++++++++++++++++++++++ 4 files changed, 111 insertions(+), 110 deletions(-) create mode 100644 src/policy/utility.cppm diff --git a/src/policy/impl.cppm b/src/policy/impl.cppm index d72e3a3..ed7731b 100644 --- a/src/policy/impl.cppm +++ b/src/policy/impl.cppm @@ -6,6 +6,21 @@ import mcpplibs.primitives.policy.traits; export namespace mcpplibs::primitives::policy { +struct checked_value {}; +struct unchecked_value {}; +struct saturating_value {}; + +struct strict_type {}; +struct category_compatible_type {}; +struct transparent_type {}; + +struct throw_error {}; +struct expected_error {}; +struct terminate_error {}; + +struct single_thread {}; +struct atomic {}; + template <> struct traits { using policy_type = checked_value; static constexpr bool enabled = true; @@ -72,4 +87,9 @@ template <> struct traits { static constexpr auto kind = category::concurrency; }; +using default_value = checked_value; +using default_type = strict_type; +using default_error = throw_error; +using default_concurrency = single_thread; + } // namespace mcpplibs::primitives::policy diff --git a/src/policy/policy.cppm b/src/policy/policy.cppm index 1b1af3f..adc3f05 100644 --- a/src/policy/policy.cppm +++ b/src/policy/policy.cppm @@ -5,3 +5,4 @@ export module mcpplibs.primitives.policy; // Entry module for policy: export traits and impl submodules. export import mcpplibs.primitives.policy.traits; export import mcpplibs.primitives.policy.impl; +export import mcpplibs.primitives.policy.utility; diff --git a/src/policy/traits.cppm b/src/policy/traits.cppm index e3c6f26..c491a75 100644 --- a/src/policy/traits.cppm +++ b/src/policy/traits.cppm @@ -1,6 +1,4 @@ module; -#include -#include export module mcpplibs.primitives.policy.traits; @@ -11,22 +9,6 @@ export namespace mcpplibs::primitives::policy { enum class category { value, type, error, concurrency }; -// Built-in policy tags (defined here so `common_policies` can live in traits). -struct checked_value {}; -struct unchecked_value {}; -struct saturating_value {}; - -struct strict_type {}; -struct category_compatible_type {}; -struct transparent_type {}; - -struct throw_error {}; -struct expected_error {}; -struct terminate_error {}; - -struct single_thread {}; -struct atomic {}; - template struct traits { using policy_type = void; static constexpr bool enabled = false; @@ -49,96 +31,4 @@ template concept concurrency_policy = policy_type

&& (traits

::kind == category::concurrency); -// Customizable priority hooks — users can specialize these templates to -// change how the library decides which policy wins when multiple are -// provided. Specializations live in implementation or user code. -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<>; -}; - -// Library default priority ordering for built-in tags. Users can specialize -// these hooks in their own code to change global priority ordering. -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; -}; - -// Default hooks for library defaults. Implementations should specialize -// these in the implementation submodule to bind to concrete tags. -// Library defaults (built-in tags). -using default_value = checked_value; -using default_type = strict_type; -using default_error = throw_error; -using default_concurrency = single_thread; - -// Internal helpers used by the merging algorithm. -namespace 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 details - -// Primary merging metafunction. Consumers can use `common_policies_t<...>`. -template struct common_policies { - using value_policy = details::pick_first_from_priority_impl< - default_value, priority_value<>::type, Ps...>::type; - - using type_policy = details::pick_first_from_priority_impl< - default_type, priority_type<>::type, Ps...>::type; - - using error_policy = details::pick_first_from_priority_impl< - default_error, priority_error<>::type, Ps...>::type; - - using concurrency_policy = details::pick_first_from_priority_impl< - default_concurrency, priority_concurrency<>::type, Ps...>::type; - - using type = - std::tuple; -}; - -template -using common_policies_t = common_policies::type; - } // namespace mcpplibs::primitives::policy diff --git a/src/policy/utility.cppm b/src/policy/utility.cppm new file mode 100644 index 0000000..37bb9fb --- /dev/null +++ b/src/policy/utility.cppm @@ -0,0 +1,90 @@ +module; +#include +#include + +export module mcpplibs.primitives.policy.utility; + +import mcpplibs.primitives.policy.traits; +import mcpplibs.primitives.policy.impl; + +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; +}; + +namespace 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 details + +template struct common_policies { + using value_policy = details::pick_first_from_priority_impl< + default_value, priority_value<>::type, Ps...>::type; + + using type_policy = details::pick_first_from_priority_impl< + default_type, priority_type<>::type, Ps...>::type; + + using error_policy = details::pick_first_from_priority_impl< + default_error, priority_error<>::type, Ps...>::type; + + using concurrency_policy = details::pick_first_from_priority_impl< + default_concurrency, priority_concurrency<>::type, Ps...>::type; + + using type = + std::tuple; +}; + +template +using common_policies_t = common_policies::type; + +} // namespace mcpplibs::primitives::policy From 294ca0060941821dc01b6bcc8dafd525c020ff94 Mon Sep 17 00:00:00 2001 From: FrozenLemonTee Date: Mon, 16 Mar 2026 23:59:31 +0800 Subject: [PATCH 06/57] refactor: Make details not exported --- src/policy/utility.cppm | 55 +++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/src/policy/utility.cppm b/src/policy/utility.cppm index 37bb9fb..448fa23 100644 --- a/src/policy/utility.cppm +++ b/src/policy/utility.cppm @@ -7,6 +7,34 @@ 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. @@ -40,33 +68,6 @@ template <> struct priority_concurrency { using type = std::tuple; }; -namespace 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 details - template struct common_policies { using value_policy = details::pick_first_from_priority_impl< default_value, priority_value<>::type, Ps...>::type; From 895d0e021ab5374c6bd23674bc66917d3ccb88f7 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 11:58:10 +0800 Subject: [PATCH 07/57] docs: Add plans to copilot instructions --- .github/copilot-instructions.md | 5 + .../plan-policyBehaviorProtocol.prompt.md | 123 ++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 .github/prompts/plan-policyBehaviorProtocol.prompt.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index b3989d8..5c1b51b 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -12,6 +12,11 @@ - 技能目录: `../.agents/skills/` - mcpp-style-ref: `../.agents/skills/mcpp-style-ref/SKILL.md` +## 正在进行中的计划 + +- 计划目录:`../.github/prompts/` +- 当前计划:`../.github/prompts/plan-policyBehaviorProtocol.prompt.md` + ## 说明 - 新增或调整 Copilot 约束时,优先更新 `.agents/docs` 和 `.agents/skills`。 diff --git a/.github/prompts/plan-policyBehaviorProtocol.prompt.md b/.github/prompts/plan-policyBehaviorProtocol.prompt.md new file mode 100644 index 0000000..3cc2ae8 --- /dev/null +++ b/.github/prompts/plan-policyBehaviorProtocol.prompt.md @@ -0,0 +1,123 @@ +## Plan: Policy 行为协议与分发规范 + +基于现有 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) + +**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,不开放分发规则特化。 + +**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 协议。 + +**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,失败值为错误域类型)。 + +**Phase 4: 聚合导出与契约稳定** +1. 在 [src/primitives.cppm](src/primitives.cppm) 增加新协议模块与分发层导出,保持聚合入口一致。 +2. 在 [src/policy/utility.cppm](src/policy/utility.cppm) 保持 common policy 选择逻辑不变,仅补充与 handler 可用性相关的辅助别名(若需要)。 + +**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) + + +**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 顺序、不可改动跨层数据结构主骨架。 + +**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 用法与扩展示例。 + +**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. 导出稳定性验证: +12. 通过聚合模块导入可直接访问 policy 协议、operation 分发、primitive 运算。 +13. 运行 tests 与示例构建,确认无回归。 + +**Decisions** +- 错误通道:所有运算统一返回 expected。 +- dispatcher 形态:type 协商前移到编译期,运行期固定三层链路 concurrency -> value -> error,不开放顺序重排。 +- 跨底层类型运算:由 type policy 在编译期决定可行性与 common type。 +- value 层职责:判定溢出并决定“本层值修正”或“下放 error 层处理”。 +- 扩展边界:仅开放 policy handler 特化,不开放分发规则。 +- 第一阶段覆盖范围:Addition/Subtraction/Multiplication/Division 全部纳入。 +- In scope:policy 行为协议、operation 分发、primitive 路由、测试与文档。 +- Out of scope:并发策略的运行时同步原语实现细节(atomic/lock-free/锁策略具体执行体)。 + +**Further Considerations** +1. expected 的错误载体类型建议先统一为轻量错误枚举,再逐步演进到可扩展错误域,以减少首版模板复杂度。 +2. type policy 的 common type 规则建议先采用“显式白名单 + static_assert 诊断”,避免首版引入过宽的隐式提升。 From a736c83c337edfc080845228203ca135004c51a0 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 16:11:58 +0800 Subject: [PATCH 08/57] docs: Add standard library dependency usage and update policy behavior protocol --- .agents/docs/architecture.md | 18 +++++++++++++++++- .../plan-policyBehaviorProtocol.prompt.md | 13 +++++++++---- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/.agents/docs/architecture.md b/.agents/docs/architecture.md index 312d6d7..db6cd65 100644 --- a/.agents/docs/architecture.md +++ b/.agents/docs/architecture.md @@ -49,6 +49,22 @@ graph TD ## 命名空间与 API 边界 +### 标准库依赖使用方式 + +由于`import std;`在目前 CMake 的构建系统中属于实验性特性,故使用标准库依赖仍然采用头文件方式。 +使用的预处理指令放于模块源文件的全局模块部分,示例: +```c++ +module; +#include +#include + +export module mcpplibs.primitives.submodule; + +export namespace mcpplibs::primitives::subnamepsace { + // Implements +} +``` + ### 公共 API(导出,稳定承诺) - `mcpplibs::primitives::std_bool` @@ -62,7 +78,7 @@ graph TD ### 内部实现(不导出,不承诺稳定) -- `mcpplibs::primitives::underlying::details::*` +- `mcpplibs::primitives::**::details::*` ### 约定 diff --git a/.github/prompts/plan-policyBehaviorProtocol.prompt.md b/.github/prompts/plan-policyBehaviorProtocol.prompt.md index 3cc2ae8..29dfd28 100644 --- a/.github/prompts/plan-policyBehaviorProtocol.prompt.md +++ b/.github/prompts/plan-policyBehaviorProtocol.prompt.md @@ -104,15 +104,18 @@ 8. 行为一致性验证: 9. 四则运算对默认策略与自定义策略均返回 expected。 10. 失败路径(溢出/除零/不兼容类型)按 error policy 语义编码到 expected 错误值。 -11. 导出稳定性验证: -12. 通过聚合模块导入可直接访问 policy 协议、operation 分发、primitive 运算。 -13. 运行 tests 与示例构建,确认无回归。 +11. unchecked_value 路径验证: +12. 不触发 error policy,保持原生算术语义(含 UB 风险)并通过测试明确边界。 +13. 导出稳定性验证: +14. 通过聚合模块导入可直接访问 policy 协议、operation 分发、primitive 运算。 +15. 运行 tests 与示例构建,确认无回归。 **Decisions** -- 错误通道:所有运算统一返回 expected。 +- 错误通道:默认路径统一返回 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 路由、测试与文档。 @@ -121,3 +124,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 618413a68ae3bcfb162ad994aa04cf6d7973e44e Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 16:12:10 +0800 Subject: [PATCH 09/57] refactor: Add CONFIGURE_DEPENDS to CXX_MODULE_FILES in CMakeLists.txt --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d7ab2b1..17dd694 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,7 +36,7 @@ endif() # Library add_library(mcpplibs-primitives STATIC) -file(GLOB_RECURSE CXX_MODULE_FILES +file(GLOB_RECURSE CXX_MODULE_FILES CONFIGURE_DEPENDS src/*.cppm ) From d40cbf3b94cc48c6c8c104155509e41300bc1f28 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 16:12:54 +0800 Subject: [PATCH 10/57] feat: Add policy handler module with error, concurrency, and value handling protocols --- src/policy/handler.cppm | 132 ++++++++++++++++++++++++++++++++++++++++ src/policy/policy.cppm | 1 + 2 files changed, 133 insertions(+) create mode 100644 src/policy/handler.cppm diff --git a/src/policy/handler.cppm b/src/policy/handler.cppm new file mode 100644 index 0000000..3cc8a65 --- /dev/null +++ b/src/policy/handler.cppm @@ -0,0 +1,132 @@ +module; + +#include +#include +#include + +export module mcpplibs.primitives.policy.handler; + +import mcpplibs.primitives.policy.traits; +import mcpplibs.primitives.operations.traits; + +export namespace mcpplibs::primitives::policy { + +// Unified runtime error kind carried across the policy chain. +enum class runtime_error_kind : unsigned char { + none = 0, + invalid_type_combination, + overflow, + underflow, + divide_by_zero, + domain_error, + unspecified, +}; + +template struct error_request { + runtime_error_kind kind = runtime_error_kind::none; + std::string_view reason{}; + std::optional lhs_value{}; + std::optional rhs_value{}; + std::optional fallback_value{}; +}; + +struct concurrency_injection { + bool fence_before = false; + bool fence_after = false; +}; + +template +struct operation_context { + using op_tag = OpTag; + using lhs_rep = LhsRep; + using rhs_rep = RhsRep; + using common_rep = CommonRep; +}; + +template struct value_decision { + bool has_value = false; + CommonRep value{}; + error_request error{}; +}; + +// Compile-time type negotiation protocol. +template +struct type_handler { + static constexpr bool enabled = false; + static constexpr bool allowed = false; + static constexpr unsigned diagnostic_id = 0; + using common_rep = void; +}; + +template +concept type_handler_available = requires { + requires type_policy; + requires operations::operation; + requires type_handler::enabled; +}; + +// Runtime concurrency wrapper protocol. +template +struct concurrency_handler { + static constexpr bool enabled = false; + using injection_type = concurrency_injection; + using result_type = std::expected; + + static constexpr auto inject() noexcept -> injection_type { return {}; } +}; + +template +concept concurrency_handler_available = requires { + requires concurrency_policy; + requires operations::operation; + requires concurrency_handler::enabled; +}; + +// Runtime value-check protocol. +template +struct value_handler { + static constexpr bool enabled = false; + static constexpr bool may_adjust_value = false; + using decision_type = value_decision; + using result_type = std::expected; + + static constexpr auto finalize(decision_type decision, + concurrency_injection const &) + -> decision_type { + return decision; + } +}; + +template +concept value_handler_available = requires { + requires value_policy; + requires operations::operation; + requires value_handler::enabled; +}; + +// Runtime error-resolution protocol. +template +struct error_handler { + static constexpr bool enabled = false; + using request_type = error_request; + using result_type = std::expected; + + static constexpr auto resolve(request_type const &) -> result_type { + return std::unexpected(ErrorPayload{}); + } +}; + +template +concept error_handler_available = requires { + requires error_policy; + requires operations::operation; + requires error_handler::enabled; +}; + +} // namespace mcpplibs::primitives::policy diff --git a/src/policy/policy.cppm b/src/policy/policy.cppm index adc3f05..744851b 100644 --- a/src/policy/policy.cppm +++ b/src/policy/policy.cppm @@ -4,5 +4,6 @@ export module mcpplibs.primitives.policy; // Entry module for policy: export traits and impl submodules. export import mcpplibs.primitives.policy.traits; +export import mcpplibs.primitives.policy.handler; export import mcpplibs.primitives.policy.impl; export import mcpplibs.primitives.policy.utility; From ea996c0059fed0f6acc131e8a2d1c6da95bde41d Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 16:14:19 +0800 Subject: [PATCH 11/57] feat: Add dispatcher module for operation handling --- src/operations/dispatcher.cppm | 119 +++++++++++++++++++++++++++++++++ src/operations/operations.cppm | 3 +- 2 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 src/operations/dispatcher.cppm diff --git a/src/operations/dispatcher.cppm b/src/operations/dispatcher.cppm new file mode 100644 index 0000000..d5550ee --- /dev/null +++ b/src/operations/dispatcher.cppm @@ -0,0 +1,119 @@ +module; + +#include +#include + +export module mcpplibs.primitives.operations.dispatcher; + +import mcpplibs.primitives.operations.traits; +import mcpplibs.primitives.operations.invoker; +import mcpplibs.primitives.primitive.impl; +import mcpplibs.primitives.primitive.traits; +import mcpplibs.primitives.policy.handler; +import mcpplibs.primitives.policy.impl; + +export namespace mcpplibs::primitives::operations { + +template +concept primitive_instance = requires { + typename mcpplibs::primitives::traits::primitive_traits< + std::remove_cvref_t>::value_type; +}; + +template +struct dispatcher_meta { + using lhs_primitive = std::remove_cvref_t; + using rhs_primitive = std::remove_cvref_t; + + using lhs_traits = + mcpplibs::primitives::traits::primitive_traits; + using rhs_traits = + mcpplibs::primitives::traits::primitive_traits; + + using lhs_rep = typename lhs_traits::value_type; + using rhs_rep = typename rhs_traits::value_type; + + using type_policy = typename lhs_traits::type_policy; + using value_policy = typename lhs_traits::value_policy; + using error_policy = typename lhs_traits::error_policy; + using concurrency_policy = typename lhs_traits::concurrency_policy; + + using common_rep = typename policy::type_handler::common_rep; + + static constexpr bool type_ready = + policy::type_handler_available; + static constexpr bool concurrency_ready = + policy::concurrency_handler_available; + static constexpr bool value_ready = + policy::value_handler_available; + static constexpr bool error_ready = + policy::error_handler_available; +}; + +template +using dispatch_result_t = std::expected< + typename dispatcher_meta::common_rep, + ErrorPayload>; + +// Closed-loop placeholder dispatcher: compile-time type negotiation + runtime +// chain (concurrency -> value -> error). The value/error execution details are +// intentionally hardcoded for now and will migrate to handler-driven calls. +template +constexpr auto dispatch(Lhs const &lhs, Rhs const &rhs) + -> dispatch_result_t { + using meta = dispatcher_meta; + using common_rep = typename meta::common_rep; + using concurrency_handler_t = + policy::concurrency_handler; + using value_handler_t = + policy::value_handler; + + static_assert( + meta::type_ready, + "Missing type_handler specialization for this operation/policy"); + static_assert(policy::type_handler::allowed, + "Type policy rejected this operation for the given operands"); + static_assert( + meta::concurrency_ready, + "Missing concurrency_handler specialization for this operation/policy"); + static_assert( + meta::value_ready, + "Missing value_handler specialization for this operation/policy"); + static_assert( + meta::error_ready, + "Missing error_handler specialization for this operation/policy"); + + // Runtime stage 1: concurrency context injection. + auto const injection = + invoker::inject_concurrency(); + + // Runtime stage 2: value path. + auto const lhs_common = static_cast(lhs.value()); + auto const rhs_common = static_cast(rhs.value()); + + auto const decision = + invoker::run_value(lhs_common, rhs_common, + injection); + + if (decision.has_value) { + return decision.value; + } + + // Runtime stage 3: error policy. + return invoker::resolve_error(decision.error); +} + +} // namespace mcpplibs::primitives::operations diff --git a/src/operations/operations.cppm b/src/operations/operations.cppm index af7236a..0b1d0ff 100644 --- a/src/operations/operations.cppm +++ b/src/operations/operations.cppm @@ -1,3 +1,4 @@ module; -export module mcpplibs.primitives.operations; \ No newline at end of file +export module mcpplibs.primitives.operations; +export import mcpplibs.primitives.operations.dispatcher; \ No newline at end of file From 20845a7e007c45f03f8d730dbc06b84ca993c1fc Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 16:14:42 +0800 Subject: [PATCH 12/57] feat: Add invoker module for operation invocation and error resolution --- src/operations/invoker.cppm | 69 ++++++++++++++++++++++++++++++++++ src/operations/operations.cppm | 1 + 2 files changed, 70 insertions(+) create mode 100644 src/operations/invoker.cppm diff --git a/src/operations/invoker.cppm b/src/operations/invoker.cppm new file mode 100644 index 0000000..a188131 --- /dev/null +++ b/src/operations/invoker.cppm @@ -0,0 +1,69 @@ +module; + +#include +#include +#include +#include +#include + +export module mcpplibs.primitives.operations.invoker; + +import mcpplibs.primitives.operations.traits; +import mcpplibs.primitives.operations.binding; +import mcpplibs.primitives.policy.handler; +import mcpplibs.primitives.policy.impl; + +export namespace mcpplibs::primitives::operations::invoker { + +namespace details { + +constexpr auto apply_runtime_fence(bool enabled) noexcept -> void { + if (!enabled) { + return; + } + + if (!std::is_constant_evaluated()) { + std::atomic_thread_fence(std::memory_order_seq_cst); + } +} + +} // namespace details + +template +constexpr auto inject_concurrency() -> policy::concurrency_injection { + static_cast(sizeof(CommonRep)); + + return ConcurrencyHandler::inject(); +} + +template +constexpr auto run_value(CommonRep lhs, CommonRep rhs, + policy::concurrency_injection const &injection) + -> policy::value_decision { + static_cast(sizeof(ErrorPayload)); + + static_assert( + binding::op_binding_available, + "Missing operation binding specialization for this OpTag/common type"); + + details::apply_runtime_fence(injection.fence_before); + + auto decision = + binding::op_binding::apply(lhs, rhs); + auto finalized = ValueHandler::finalize(std::move(decision), injection); + + details::apply_runtime_fence(injection.fence_after); + return finalized; +} + +template +constexpr auto resolve_error(policy::error_request const &request) + -> std::expected { + using handler_t = + policy::error_handler; + return handler_t::resolve(request); +} + +} // namespace mcpplibs::primitives::operations::invoker diff --git a/src/operations/operations.cppm b/src/operations/operations.cppm index 0b1d0ff..eef0e71 100644 --- a/src/operations/operations.cppm +++ b/src/operations/operations.cppm @@ -1,4 +1,5 @@ module; export module mcpplibs.primitives.operations; +export import mcpplibs.primitives.operations.invoker; export import mcpplibs.primitives.operations.dispatcher; \ No newline at end of file From 1f92d689c0ef9b090cd087d0da15d272f8debb4f Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 16:15:01 +0800 Subject: [PATCH 13/57] feat: Add binding module for operation value policies and arithmetic checks --- src/operations/binding.cppm | 538 +++++++++++++++++++++++++++++++++ src/operations/operations.cppm | 1 + 2 files changed, 539 insertions(+) create mode 100644 src/operations/binding.cppm diff --git a/src/operations/binding.cppm b/src/operations/binding.cppm new file mode 100644 index 0000000..5c1f25c --- /dev/null +++ b/src/operations/binding.cppm @@ -0,0 +1,538 @@ +module; + +#include +#include +#include +#include + +export module mcpplibs.primitives.operations.binding; + +import mcpplibs.primitives.operations.traits; +import mcpplibs.primitives.operations.impl; +import mcpplibs.primitives.policy.handler; +import mcpplibs.primitives.policy.impl; +import mcpplibs.primitives.policy.traits; + +export namespace mcpplibs::primitives::operations::binding { + +namespace details { + +template +constexpr auto make_error(policy::runtime_error_kind kind, char const *reason, + std::optional lhs = std::nullopt, + std::optional rhs = std::nullopt) + -> policy::value_decision { + policy::value_decision out{}; + out.has_value = false; + out.error.kind = kind; + out.error.reason = reason; + out.error.lhs_value = lhs; + out.error.rhs_value = rhs; + return out; +} + +template +constexpr auto checked_add(T lhs, T rhs) -> policy::value_decision { + if constexpr (!std::is_integral_v || std::is_same_v) { + policy::value_decision out{}; + out.has_value = true; + out.value = static_cast(lhs + rhs); + return out; + } else if constexpr (std::is_unsigned_v) { + auto const maxv = std::numeric_limits::max(); + if (lhs > maxv - rhs) { + return make_error(policy::runtime_error_kind::overflow, + "checked addition overflow", lhs, rhs); + } + policy::value_decision out{}; + out.has_value = true; + out.value = static_cast(lhs + rhs); + return out; + } else { + auto const maxv = std::numeric_limits::max(); + auto const minv = std::numeric_limits::min(); + if ((rhs > 0) && (lhs > maxv - rhs)) { + return make_error(policy::runtime_error_kind::overflow, + "checked addition overflow", lhs, rhs); + } + if ((rhs < 0) && (lhs < minv - rhs)) { + return make_error(policy::runtime_error_kind::underflow, + "checked addition underflow", lhs, rhs); + } + policy::value_decision out{}; + out.has_value = true; + out.value = static_cast(lhs + rhs); + return out; + } +} + +template +constexpr auto checked_sub(T lhs, T rhs) -> policy::value_decision { + if constexpr (!std::is_integral_v || std::is_same_v) { + policy::value_decision out{}; + out.has_value = true; + out.value = static_cast(lhs - rhs); + return out; + } else if constexpr (std::is_unsigned_v) { + if (lhs < rhs) { + return make_error(policy::runtime_error_kind::underflow, + "checked subtraction underflow", lhs, rhs); + } + policy::value_decision out{}; + out.has_value = true; + out.value = static_cast(lhs - rhs); + return out; + } else { + auto const maxv = std::numeric_limits::max(); + auto const minv = std::numeric_limits::min(); + if ((rhs < 0) && (lhs > maxv + rhs)) { + return make_error(policy::runtime_error_kind::overflow, + "checked subtraction overflow", lhs, rhs); + } + if ((rhs > 0) && (lhs < minv + rhs)) { + return make_error(policy::runtime_error_kind::underflow, + "checked subtraction underflow", lhs, rhs); + } + policy::value_decision out{}; + out.has_value = true; + out.value = static_cast(lhs - rhs); + return out; + } +} + +template +constexpr auto checked_mul(T lhs, T rhs) -> policy::value_decision { + if constexpr (!std::is_integral_v || std::is_same_v) { + policy::value_decision out{}; + out.has_value = true; + out.value = static_cast(lhs * rhs); + return out; + } else { + if (lhs == T{} || rhs == T{}) { + policy::value_decision out{}; + out.has_value = true; + out.value = T{}; + return out; + } + + if constexpr (std::is_unsigned_v) { + auto const maxv = std::numeric_limits::max(); + if (lhs > maxv / rhs) { + return make_error(policy::runtime_error_kind::overflow, + "checked multiplication overflow", lhs, rhs); + } + policy::value_decision out{}; + out.has_value = true; + out.value = static_cast(lhs * rhs); + return out; + } else { + auto const maxv = std::numeric_limits::max(); + auto const minv = std::numeric_limits::min(); + + if (lhs > 0) { + if (rhs > 0) { + if (lhs > maxv / rhs) { + return make_error(policy::runtime_error_kind::overflow, + "checked multiplication overflow", lhs, rhs); + } + } else { + if (rhs < minv / lhs) { + return make_error(policy::runtime_error_kind::underflow, + "checked multiplication underflow", lhs, rhs); + } + } + } else { + if (rhs > 0) { + if (lhs < minv / rhs) { + return make_error(policy::runtime_error_kind::underflow, + "checked multiplication underflow", lhs, rhs); + } + } else { + if (lhs != 0 && rhs < maxv / lhs) { + return make_error(policy::runtime_error_kind::overflow, + "checked multiplication overflow", lhs, rhs); + } + } + } + + policy::value_decision out{}; + out.has_value = true; + out.value = static_cast(lhs * rhs); + return out; + } + } +} + +template +constexpr auto checked_div(T lhs, T rhs) -> policy::value_decision { + if (rhs == T{}) { + return make_error(policy::runtime_error_kind::divide_by_zero, + "checked division by zero", lhs, rhs); + } + + if constexpr (std::is_integral_v && std::is_signed_v) { + auto const minv = std::numeric_limits::min(); + if (lhs == minv && rhs == static_cast(-1)) { + return make_error(policy::runtime_error_kind::overflow, + "checked division overflow", lhs, rhs); + } + } + + if constexpr (requires { lhs / rhs; }) { + policy::value_decision out{}; + out.has_value = true; + out.value = static_cast(lhs / rhs); + return out; + } + + return make_error( + policy::runtime_error_kind::unspecified, + "checked division not supported for negotiated common type", lhs, rhs); +} + +template +constexpr auto unchecked_add(T lhs, T rhs) -> policy::value_decision { + policy::value_decision out{}; + out.has_value = true; + + if constexpr (requires { lhs + rhs; }) { + out.value = static_cast(lhs + rhs); + return out; + } + + out.value = T{}; + return out; +} + +template +constexpr auto unchecked_sub(T lhs, T rhs) -> policy::value_decision { + policy::value_decision out{}; + out.has_value = true; + + if constexpr (requires { lhs - rhs; }) { + out.value = static_cast(lhs - rhs); + return out; + } + + out.value = T{}; + return out; +} + +template +constexpr auto unchecked_mul(T lhs, T rhs) -> policy::value_decision { + policy::value_decision out{}; + out.has_value = true; + + if constexpr (requires { lhs * rhs; }) { + out.value = static_cast(lhs * rhs); + return out; + } + + out.value = T{}; + return out; +} + +template +constexpr auto unchecked_div(T lhs, T rhs) -> policy::value_decision { + policy::value_decision out{}; + out.has_value = true; + + // Intentionally no guards: unchecked policy delegates error/UB behavior + // to the underlying language/runtime semantics. + out.value = static_cast(lhs / rhs); + return out; +} + +template constexpr auto saturating_add(T lhs, T rhs) -> T { + if constexpr (!std::is_integral_v || std::is_same_v) { + return static_cast(lhs + rhs); + } else if constexpr (std::is_unsigned_v) { + auto const maxv = std::numeric_limits::max(); + return (lhs > maxv - rhs) ? maxv : static_cast(lhs + rhs); + } else { + auto const maxv = std::numeric_limits::max(); + auto const minv = std::numeric_limits::min(); + if ((rhs > 0) && (lhs > maxv - rhs)) { + return maxv; + } + if ((rhs < 0) && (lhs < minv - rhs)) { + return minv; + } + return static_cast(lhs + rhs); + } +} + +template constexpr auto saturating_sub(T lhs, T rhs) -> T { + if constexpr (!std::is_integral_v || std::is_same_v) { + return static_cast(lhs - rhs); + } else if constexpr (std::is_unsigned_v) { + return (lhs < rhs) ? T{} : static_cast(lhs - rhs); + } else { + auto const maxv = std::numeric_limits::max(); + auto const minv = std::numeric_limits::min(); + if ((rhs < 0) && (lhs > maxv + rhs)) { + return maxv; + } + if ((rhs > 0) && (lhs < minv + rhs)) { + return minv; + } + return static_cast(lhs - rhs); + } +} + +template constexpr auto saturating_mul(T lhs, T rhs) -> T { + if constexpr (!std::is_integral_v || std::is_same_v) { + return static_cast(lhs * rhs); + } else { + if (lhs == T{} || rhs == T{}) { + return T{}; + } + + if constexpr (std::is_unsigned_v) { + auto const maxv = std::numeric_limits::max(); + return (lhs > maxv / rhs) ? maxv : static_cast(lhs * rhs); + } else { + auto const maxv = std::numeric_limits::max(); + auto const minv = std::numeric_limits::min(); + + if (lhs > 0) { + if (rhs > 0) { + if (lhs > maxv / rhs) { + return maxv; + } + } else { + if (rhs < minv / lhs) { + return minv; + } + } + } else { + if (rhs > 0) { + if (lhs < minv / rhs) { + return minv; + } + } else { + if (lhs != 0 && rhs < maxv / lhs) { + return maxv; + } + } + } + + return static_cast(lhs * rhs); + } + } +} + +template +constexpr auto make_unsupported(char const *reason) + -> policy::value_decision { + return make_error(policy::runtime_error_kind::unspecified, reason); +} + +template +constexpr auto make_div_zero(char const *reason) + -> policy::value_decision { + return make_error(policy::runtime_error_kind::divide_by_zero, + reason); +} + +} // namespace details + +// Placeholder op-binding protocol for end-to-end bring-up. +// NOTE: OpTag-to-arithmetic mapping is intentionally hardcoded in this module +// for now. It will be replaced by policy-aware operation bindings later. +template +struct op_binding { + static constexpr bool enabled = false; + + static constexpr auto apply(CommonRep, CommonRep) + -> policy::value_decision { + policy::value_decision out{}; + out.has_value = false; + out.error.kind = policy::runtime_error_kind::unspecified; + out.error.reason = "operation binding is not implemented"; + return out; + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value_decision { + if constexpr (requires { details::checked_add(lhs, rhs); }) { + return details::checked_add(lhs, rhs); + } + + return details::make_unsupported( + "checked addition not supported for negotiated common type"); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value_decision { + if constexpr (requires { details::checked_sub(lhs, rhs); }) { + return details::checked_sub(lhs, rhs); + } + + return details::make_unsupported( + "checked subtraction not supported for negotiated common type"); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value_decision { + if constexpr (requires { details::checked_mul(lhs, rhs); }) { + return details::checked_mul(lhs, rhs); + } + + return details::make_unsupported( + "checked multiplication not supported for negotiated common type"); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value_decision { + if constexpr (requires { details::checked_div(lhs, rhs); }) { + return details::checked_div(lhs, rhs); + } + + return details::make_unsupported( + "checked division not supported for negotiated common type"); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value_decision { + return details::unchecked_add(lhs, rhs); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value_decision { + return details::unchecked_sub(lhs, rhs); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value_decision { + return details::unchecked_mul(lhs, rhs); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value_decision { + return details::unchecked_div(lhs, rhs); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value_decision { + policy::value_decision out{}; + if constexpr (requires { details::saturating_add(lhs, rhs); }) { + out.has_value = true; + out.value = details::saturating_add(lhs, rhs); + return out; + } + return details::make_unsupported( + "saturating addition not supported for negotiated common type"); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value_decision { + policy::value_decision out{}; + if constexpr (requires { details::saturating_sub(lhs, rhs); }) { + out.has_value = true; + out.value = details::saturating_sub(lhs, rhs); + return out; + } + return details::make_unsupported( + "saturating subtraction not supported for negotiated common type"); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value_decision { + policy::value_decision out{}; + if constexpr (requires { details::saturating_mul(lhs, rhs); }) { + out.has_value = true; + out.value = details::saturating_mul(lhs, rhs); + return out; + } + return details::make_unsupported( + "saturating multiplication not supported for negotiated common type"); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value_decision { + policy::value_decision out{}; + + if (rhs == CommonRep{}) { + return details::make_div_zero("saturating division by zero"); + } + + if constexpr (requires { lhs / rhs; }) { + out.has_value = true; + out.value = static_cast(lhs / rhs); + return out; + } + + return details::make_unsupported( + "saturating division not supported for negotiated common type"); + } +}; + +template +concept op_binding_available = requires { + requires operation; + requires policy::value_policy; + requires op_binding::enabled; +}; + +} // namespace mcpplibs::primitives::operations::binding diff --git a/src/operations/operations.cppm b/src/operations/operations.cppm index eef0e71..5493db7 100644 --- a/src/operations/operations.cppm +++ b/src/operations/operations.cppm @@ -1,5 +1,6 @@ module; export module mcpplibs.primitives.operations; +export import mcpplibs.primitives.operations.binding; export import mcpplibs.primitives.operations.invoker; export import mcpplibs.primitives.operations.dispatcher; \ No newline at end of file From fc30fdb9cb7ab9a5cde59df5346e4ca64d1aa75e Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 16:15:11 +0800 Subject: [PATCH 14/57] feat: Add binding module for operation value policies and arithmetic checks --- src/operations/operations.cppm | 3 +- src/operations/operators.cppm | 101 ++++++++++++++++++++++++++++++++- 2 files changed, 102 insertions(+), 2 deletions(-) diff --git a/src/operations/operations.cppm b/src/operations/operations.cppm index 5493db7..c2fb3f0 100644 --- a/src/operations/operations.cppm +++ b/src/operations/operations.cppm @@ -3,4 +3,5 @@ module; export module mcpplibs.primitives.operations; export import mcpplibs.primitives.operations.binding; export import mcpplibs.primitives.operations.invoker; -export import mcpplibs.primitives.operations.dispatcher; \ No newline at end of file +export import mcpplibs.primitives.operations.dispatcher; +export import mcpplibs.primitives.operations.operators; \ No newline at end of file diff --git a/src/operations/operators.cppm b/src/operations/operators.cppm index 6454f63..baef17a 100644 --- a/src/operations/operators.cppm +++ b/src/operations/operators.cppm @@ -1,3 +1,102 @@ module; -export module mcpplibs.primitives.operations.operators; \ No newline at end of file +#include + +export module mcpplibs.primitives.operations.operators; + +import mcpplibs.primitives.operations.traits; +import mcpplibs.primitives.operations.dispatcher; +import mcpplibs.primitives.operations.impl; +import mcpplibs.primitives.primitive.impl; +import mcpplibs.primitives.primitive.traits; +import mcpplibs.primitives.policy.handler; + +export namespace mcpplibs::primitives::operations { + +template +using primitive_dispatch_result_t = std::expected< + typename mcpplibs::primitives::traits::make_primitive_t< + typename dispatcher_meta::common_rep, + typename mcpplibs::primitives::traits::primitive_traits::policies>, + ErrorPayload>; + +template +constexpr auto apply(Lhs const &lhs, Rhs const &rhs) + -> primitive_dispatch_result_t { + using result_primitive = + typename primitive_dispatch_result_t::value_type; + + auto const raw = dispatch(lhs, rhs); + if (!raw.has_value()) { + return std::unexpected(raw.error()); + } + + return result_primitive{*raw}; +} + +template +constexpr auto add(Lhs const &lhs, Rhs const &rhs) + -> primitive_dispatch_result_t { + return apply(lhs, rhs); +} + +template +constexpr auto sub(Lhs const &lhs, Rhs const &rhs) + -> primitive_dispatch_result_t { + return apply(lhs, rhs); +} + +template +constexpr auto mul(Lhs const &lhs, Rhs const &rhs) + -> primitive_dispatch_result_t { + return apply(lhs, rhs); +} + +template +constexpr auto div(Lhs const &lhs, Rhs const &rhs) + -> primitive_dispatch_result_t { + return apply(lhs, rhs); +} + +} // namespace mcpplibs::primitives::operations + +export namespace mcpplibs::primitives::operators { + +template +constexpr auto operator+(Lhs const &lhs, Rhs const &rhs) + -> operations::primitive_dispatch_result_t { + return operations::add(lhs, rhs); +} + +template +constexpr auto operator-(Lhs const &lhs, Rhs const &rhs) + -> operations::primitive_dispatch_result_t { + return operations::sub(lhs, rhs); +} + +template +constexpr auto operator*(Lhs const &lhs, Rhs const &rhs) + -> operations::primitive_dispatch_result_t { + return operations::mul(lhs, rhs); +} + +template +constexpr auto operator/(Lhs const &lhs, Rhs const &rhs) + -> operations::primitive_dispatch_result_t { + return operations::div(lhs, rhs); +} + +} // namespace mcpplibs::primitives::operators \ No newline at end of file From bbb7306b944a2ad0235cf8ccf554e9f467b12c81 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 16:15:22 +0800 Subject: [PATCH 15/57] feat: Add traits and impl modules to operations export --- src/operations/operations.cppm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/operations/operations.cppm b/src/operations/operations.cppm index c2fb3f0..fcd5ecf 100644 --- a/src/operations/operations.cppm +++ b/src/operations/operations.cppm @@ -1,6 +1,9 @@ module; export module mcpplibs.primitives.operations; + +export import mcpplibs.primitives.operations.traits; +export import mcpplibs.primitives.operations.impl; export import mcpplibs.primitives.operations.binding; export import mcpplibs.primitives.operations.invoker; export import mcpplibs.primitives.operations.dispatcher; From 6698fa52ea15928e987c6b92439f3ca65e5aacd5 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 16:15:39 +0800 Subject: [PATCH 16/57] feat: Add default protocol specializations for type, concurrency, value, and error handlers --- src/policy/impl.cppm | 161 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) diff --git a/src/policy/impl.cppm b/src/policy/impl.cppm index ed7731b..9e3c338 100644 --- a/src/policy/impl.cppm +++ b/src/policy/impl.cppm @@ -1,8 +1,16 @@ module; +#include +#include +#include +#include +#include + export module mcpplibs.primitives.policy.impl; +import mcpplibs.primitives.operations.traits; import mcpplibs.primitives.policy.traits; +import mcpplibs.primitives.policy.handler; export namespace mcpplibs::primitives::policy { @@ -92,4 +100,157 @@ using default_type = strict_type; using default_error = throw_error; using default_concurrency = single_thread; +// Default protocol specializations. +template +struct type_handler { + static constexpr bool enabled = true; + static constexpr bool allowed = std::same_as; + static constexpr unsigned diagnostic_id = allowed ? 0u : 1u; + using common_rep = std::conditional_t; +}; + +template +struct type_handler { + static constexpr bool enabled = true; + static constexpr bool allowed = + std::is_arithmetic_v && std::is_arithmetic_v; + static constexpr unsigned diagnostic_id = allowed ? 0u : 2u; + using common_rep = + std::conditional_t, void>; +}; + +template +struct type_handler { + static constexpr bool enabled = true; + static constexpr bool allowed = true; + static constexpr unsigned diagnostic_id = 0u; + using common_rep = std::common_type_t; +}; + +template +struct concurrency_handler { + static constexpr bool enabled = true; + static constexpr bool requires_external_sync = false; + using injection_type = concurrency_injection; + using result_type = std::expected; + + static constexpr auto inject() noexcept -> injection_type { + return injection_type{}; + } +}; + +template +struct concurrency_handler { + static constexpr bool enabled = true; + static constexpr bool requires_external_sync = true; + using injection_type = concurrency_injection; + using result_type = std::expected; + + static constexpr auto inject() noexcept -> injection_type { + injection_type out{}; + out.fence_before = true; + out.fence_after = true; + return out; + } +}; + +template +struct value_handler { + static constexpr bool enabled = true; + static constexpr bool may_adjust_value = false; + using decision_type = value_decision; + using result_type = std::expected; + + static constexpr auto finalize(decision_type decision, + concurrency_injection const &) noexcept + -> decision_type { + return decision; + } +}; + +template +struct value_handler { + static constexpr bool enabled = true; + static constexpr bool may_adjust_value = false; + using decision_type = value_decision; + using result_type = std::expected; + + static constexpr auto finalize(decision_type decision, + concurrency_injection const &) noexcept + -> decision_type { + return decision; + } +}; + +template +struct value_handler { + static constexpr bool enabled = true; + static constexpr bool may_adjust_value = true; + using decision_type = value_decision; + using result_type = std::expected; + + static constexpr auto finalize(decision_type decision, + concurrency_injection const &) noexcept + -> decision_type { + return decision; + } +}; + +namespace details { +template +constexpr auto to_error_payload(runtime_error_kind kind) -> ErrorPayload { + if constexpr (std::same_as) { + return kind; + } else { + static_cast(kind); + return ErrorPayload{}; + } +} +} // namespace details + +template +struct error_handler { + static constexpr bool enabled = true; + static constexpr bool converts_to_expected = true; + using request_type = error_request; + using result_type = std::expected; + + static auto resolve(request_type const &request) -> result_type { + throw std::runtime_error(request.reason.data()); + } +}; + +template +struct error_handler { + static constexpr bool enabled = true; + static constexpr bool converts_to_expected = true; + using request_type = error_request; + using result_type = std::expected; + + static constexpr auto resolve(request_type const &request) -> result_type { + return std::unexpected( + details::to_error_payload(request.kind)); + } +}; + +template +struct error_handler { + static constexpr bool enabled = true; + static constexpr bool converts_to_expected = false; + using request_type = error_request; + using result_type = std::expected; + + [[noreturn]] static auto resolve(request_type const &) -> result_type { + std::terminate(); + } +}; + } // namespace mcpplibs::primitives::policy From 231be6db2feeb7520feb21faf0719291776791c1 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 16:15:49 +0800 Subject: [PATCH 17/57] feat: Add tests for atomic policy and concurrency handler fence injection --- tests/basic/test_policies.cpp | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/tests/basic/test_policies.cpp b/tests/basic/test_policies.cpp index 5c0c2d0..7893344 100644 --- a/tests/basic/test_policies.cpp +++ b/tests/basic/test_policies.cpp @@ -2,7 +2,6 @@ import mcpplibs.primitives; - using namespace mcpplibs::primitives; TEST(PolicyTraitsTest, BuiltinPoliciesHaveCategories) { @@ -31,6 +30,10 @@ TEST(PolicyTraitsTest, BuiltinPoliciesHaveCategories) { EXPECT_EQ(policy::traits::kind, policy::category::concurrency); + EXPECT_TRUE((policy::traits::enabled)); + EXPECT_EQ(policy::traits::kind, + policy::category::concurrency); + EXPECT_TRUE((policy::traits::enabled)); EXPECT_EQ(policy::traits::kind, policy::category::value); @@ -40,8 +43,24 @@ TEST(PolicyTraitsTest, BuiltinPoliciesHaveCategories) { EXPECT_FALSE((policy_type)); EXPECT_TRUE((std::is_same_v)); - EXPECT_TRUE( - (std::is_same_v)); + EXPECT_TRUE((std::is_same_v)); +} + +TEST(PolicyConcurrencyTest, AtomicInjectsFences) { + using atomic_handler = + policy::concurrency_handler; + using single_handler = + policy::concurrency_handler; + + auto const atomic_injection = atomic_handler::inject(); + auto const single_injection = single_handler::inject(); + + EXPECT_TRUE(atomic_injection.fence_before); + EXPECT_TRUE(atomic_injection.fence_after); + EXPECT_FALSE(single_injection.fence_before); + EXPECT_FALSE(single_injection.fence_after); } // Use the existing test runner main from other test translation unit. From 003a87110348cbaf437d46b2fec29b83ebc1475e Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 16:16:06 +0800 Subject: [PATCH 18/57] feat: Add comprehensive tests for operations module including arithmetic, error handling, and concurrency policies --- tests/basic/test_operations.cpp | 250 +++++++++++++++++++++++++++++++- 1 file changed, 248 insertions(+), 2 deletions(-) diff --git a/tests/basic/test_operations.cpp b/tests/basic/test_operations.cpp index 44e3fb7..4f3d8b4 100644 --- a/tests/basic/test_operations.cpp +++ b/tests/basic/test_operations.cpp @@ -1,2 +1,248 @@ -// Operations tests are intentionally removed during the operations module -// rewrite. +#include + +#include +#include +#include + +import mcpplibs.primitives; + +using namespace mcpplibs::primitives; + +TEST(OperationsTest, AddReturnsExpectedPrimitive) { + using lhs_t = primitive; + using rhs_t = primitive; + + auto const lhs = lhs_t{10}; + auto const rhs = rhs_t{32}; + + auto const result = operations::add(lhs, rhs); + + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(result->value(), 42); +} + +TEST(OperationsTest, DivisionByZeroReturnsError) { + using value_t = primitive; + + auto const lhs = value_t{100}; + auto const rhs = value_t{0}; + + auto const result = operations::div(lhs, rhs); + + ASSERT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), policy::runtime_error_kind::divide_by_zero); +} + +TEST(OperationsTest, SaturatingAdditionClampsUnsignedOverflow) { + using value_t = primitive; + + auto const lhs = value_t{static_cast(250)}; + auto const rhs = value_t{static_cast(20)}; + + auto const result = operations::add(lhs, rhs); + + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(result->value(), static_cast(255)); +} + +TEST(OperationsTest, CheckedAdditionReportsUnsignedOverflow) { + using value_t = + primitive; + + auto const lhs = value_t{static_cast(250)}; + auto const rhs = value_t{static_cast(20)}; + + auto const result = operations::add(lhs, rhs); + + ASSERT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), policy::runtime_error_kind::overflow); +} + +TEST(OperationsTest, UncheckedAdditionWrapsUnsignedOverflow) { + using value_t = + primitive; + + auto const lhs = value_t{static_cast(250)}; + auto const rhs = value_t{static_cast(20)}; + + auto const result = operations::add(lhs, rhs); + + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(result->value(), static_cast(14)); +} + +TEST(OperationsTest, UncheckedDivisionUsesRawArithmeticWhenValid) { + using value_t = + primitive; + + auto const lhs = value_t{100}; + auto const rhs = value_t{4}; + + auto const result = operations::div(lhs, rhs); + + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(result->value(), 25); +} + +TEST(OperationsTest, AtomicPolicyPathReturnsExpectedValue) { + using value_t = primitive; + + auto const lhs = value_t{12}; + auto const rhs = value_t{30}; + + auto const result = operations::add(lhs, rhs); + + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(result->value(), 42); +} + +TEST(OperationsTest, AtomicPolicyConcurrentInvocationsRemainConsistent) { + using value_t = primitive; + + constexpr int kThreadCount = 8; + constexpr int kIterationsPerThread = 20000; + + auto const lhs = value_t{12}; + auto const rhs = value_t{30}; + + std::atomic mismatch_count{0}; + std::atomic error_count{0}; + std::atomic start{false}; + + std::vector workers; + workers.reserve(kThreadCount); + + for (int i = 0; i < kThreadCount; ++i) { + workers.emplace_back([&]() { + while (!start.load(std::memory_order_acquire)) { + } + + for (int n = 0; n < kIterationsPerThread; ++n) { + auto const result = operations::add(lhs, rhs); + if (!result.has_value()) { + error_count.fetch_add(1, std::memory_order_relaxed); + continue; + } + + if (result->value() != 42) { + mismatch_count.fetch_add(1, std::memory_order_relaxed); + } + } + }); + } + + start.store(true, std::memory_order_release); + + for (auto &worker : workers) { + worker.join(); + } + + EXPECT_EQ(error_count.load(std::memory_order_relaxed), 0); + EXPECT_EQ(mismatch_count.load(std::memory_order_relaxed), 0); +} + +TEST(OperationsTest, StrictTypeRejectsMixedTypesAtCompileTime) { + using lhs_t = primitive; + using rhs_t = primitive; + + using strict_handler = + policy::type_handler; + using strict_meta = + operations::dispatcher_meta; + + static_assert(strict_handler::enabled); + static_assert(!strict_handler::allowed); + static_assert(std::is_same_v); + + EXPECT_EQ(strict_handler::diagnostic_id, 1u); +} + +TEST(OperationsTest, StrictTypeAllowsSameTypeAtRuntime) { + using value_t = primitive; + + auto const lhs = value_t{19}; + auto const rhs = value_t{23}; + + auto const result = operations::add(lhs, rhs); + + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(result->value(), 42); +} + +TEST(OperationsTest, PrimitiveAliasWorksWithFrameworkOperators) { + using namespace mcpplibs::primitives::types; + using namespace mcpplibs::primitives::operators; + using value_t = I32; + + auto const lhs = value_t{20}; + auto const rhs = value_t{22}; + + auto const result = lhs + rhs; + + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(result->value(), 42); +} + +TEST(OperationsTest, PrimitiveAliasMixesWithBuiltinArithmeticExplicitly) { + using namespace mcpplibs::primitives::types; + using namespace mcpplibs::primitives::operators; + using value_t = I32; + + static_assert(!std::is_convertible_v); + + auto const lhs = value_t{40}; + auto const mixed = static_cast(lhs) + 2; + EXPECT_EQ(mixed, 42); + + auto const wrapped = value_t{mixed}; + auto const result = wrapped + value_t{1}; + + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(result->value(), 43); +} + +TEST(OperationsTest, OperatorPlusDelegatesToDispatcher) { + using namespace mcpplibs::primitives::operators; + using value_t = primitive; + + auto const lhs = value_t{7}; + auto const rhs = value_t{8}; + + auto const result = lhs + rhs; + + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(result->value(), 15); +} + +TEST(OperationsTest, ThrowErrorPolicyThrowsException) { + using value_t = primitive; + + auto const lhs = value_t{100}; + auto const rhs = value_t{0}; + + EXPECT_THROW((void)operations::div(lhs, rhs), std::runtime_error); +} + +TEST(OperationsTest, ThrowErrorPolicyExceptionHasReasonMessage) { + using value_t = primitive; + + auto const lhs = value_t{100}; + auto const rhs = value_t{0}; + + try { + (void)operations::div(lhs, rhs); + FAIL() << "Expected std::runtime_error to be thrown"; + } catch (std::runtime_error const &e) { + EXPECT_NE(std::string(e.what()).find("division by zero"), + std::string::npos); + } catch (...) { + FAIL() << "Expected std::runtime_error"; + } +} From a90ec5835171298875250c41c348dfac553a7786 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 20:04:46 +0800 Subject: [PATCH 19/57] docs: Add underlying bridge execution contract details to plan-policyBehaviorProtocol prompt --- .github/prompts/plan-policyBehaviorProtocol.prompt.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/prompts/plan-policyBehaviorProtocol.prompt.md b/.github/prompts/plan-policyBehaviorProtocol.prompt.md index 29dfd28..7bc994b 100644 --- a/.github/prompts/plan-policyBehaviorProtocol.prompt.md +++ b/.github/prompts/plan-policyBehaviorProtocol.prompt.md @@ -121,6 +121,15 @@ - In scope:policy 行为协议、operation 分发、primitive 路由、测试与文档。 - Out of scope:并发策略的运行时同步原语实现细节(atomic/lock-free/锁策略具体执行体)。 +## Underlying Bridge Execution Contract (Runtime) + +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 链路顺序,也不改变错误枚举体系。 + **Further Considerations** 1. expected 的错误载体类型建议先统一为轻量错误枚举,再逐步演进到可扩展错误域,以减少首版模板复杂度。 2. type policy 的 common type 规则建议先采用“显式白名单 + static_assert 诊断”,避免首版引入过宽的隐式提升。 From d8f258e420f46754bc17e0f449e70ea7094a3b30 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 20:05:22 +0800 Subject: [PATCH 20/57] feat: Add capability enum and related traits for operation capabilities --- src/operations/traits.cppm | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/src/operations/traits.cppm b/src/operations/traits.cppm index bfdfe07..82976a7 100644 --- a/src/operations/traits.cppm +++ b/src/operations/traits.cppm @@ -7,12 +7,27 @@ enum class dimension : unsigned char { binary = 2, }; +enum class capability : unsigned char { + none = 0, + arithmetic = 1 << 0, + comparison = 1 << 1, + bitwise = 1 << 2, +}; + +constexpr auto has_capability(capability mask, capability cap) noexcept + -> bool { + return (static_cast(mask) & static_cast(cap)) != + 0; +} + template struct traits { using op_tag = OpTag; - // Operation metadata hooks (default-disabled). + // Operation metadata (default-disabled). + // capability_mask is the single source of truth for capability declaration. static constexpr bool enabled = false; static constexpr auto arity = static_cast(0); + static constexpr auto capability_mask = capability::none; }; template @@ -26,4 +41,24 @@ template concept binary_operation = operation && traits::arity == dimension::binary; +template +inline constexpr bool op_has_capability_v = + operation && has_capability(traits::capability_mask, Cap); + +template +concept arithmetic_operation = + op_has_capability_v; + +template +concept comparison_operation = + op_has_capability_v; + +template +concept bitwise_operation = op_has_capability_v; + +// Validates that an enabled operation has a non-none capability_mask declared. +template +inline constexpr bool op_capability_valid_v = + !operation || traits::capability_mask != capability::none; + } // namespace mcpplibs::primitives::operations \ No newline at end of file From 50a89a23ba0e6e57ca400ed8f81eb16a4d8f8f0d Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 20:05:47 +0800 Subject: [PATCH 21/57] feat: Add capability mask to arithmetic operations traits --- src/operations/impl.cppm | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/operations/impl.cppm b/src/operations/impl.cppm index 9c41236..c910e5f 100644 --- a/src/operations/impl.cppm +++ b/src/operations/impl.cppm @@ -14,6 +14,7 @@ template <> struct traits { static constexpr bool enabled = true; static constexpr auto arity = dimension::binary; + static constexpr auto capability_mask = capability::arithmetic; }; template <> struct traits { @@ -21,6 +22,7 @@ template <> struct traits { static constexpr bool enabled = true; static constexpr auto arity = dimension::binary; + static constexpr auto capability_mask = capability::arithmetic; }; template <> struct traits { @@ -28,6 +30,7 @@ template <> struct traits { static constexpr bool enabled = true; static constexpr auto arity = dimension::binary; + static constexpr auto capability_mask = capability::arithmetic; }; template <> struct traits { @@ -35,6 +38,9 @@ template <> struct traits { static constexpr bool enabled = true; static constexpr auto arity = dimension::binary; + static constexpr auto capability_mask = capability::arithmetic; + static constexpr auto capability_mask = capability::comparison; + static constexpr auto capability_mask = capability::comparison; }; } // namespace mcpplibs::primitives::operations \ No newline at end of file From 3a86cd9c883b6e69bbfde4a11fa33db44b5bc979 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 20:06:04 +0800 Subject: [PATCH 22/57] feat: Add Equal and NotEqual operations and traits --- src/operations/impl.cppm | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/operations/impl.cppm b/src/operations/impl.cppm index c910e5f..deecfc6 100644 --- a/src/operations/impl.cppm +++ b/src/operations/impl.cppm @@ -8,6 +8,8 @@ struct Addition {}; struct Subtraction {}; struct Multiplication {}; struct Division {}; +struct Equal {}; +struct NotEqual {}; template <> struct traits { using op_tag = Addition; @@ -39,7 +41,21 @@ template <> struct traits { static constexpr bool enabled = true; static constexpr auto arity = dimension::binary; static constexpr auto capability_mask = capability::arithmetic; +}; + +template <> struct traits { + using op_tag = Equal; + + static constexpr bool enabled = true; + static constexpr auto arity = dimension::binary; static constexpr auto capability_mask = capability::comparison; +}; + +template <> struct traits { + using op_tag = NotEqual; + + static constexpr bool enabled = true; + static constexpr auto arity = dimension::binary; static constexpr auto capability_mask = capability::comparison; }; From 2d56d449ef51aed12a507245ab5bba42b84d347c Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 20:06:17 +0800 Subject: [PATCH 23/57] feat: Add equality and inequality operations and corresponding operators --- src/operations/operators.cppm | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/operations/operators.cppm b/src/operations/operators.cppm index baef17a..8f90c5a 100644 --- a/src/operations/operators.cppm +++ b/src/operations/operators.cppm @@ -65,6 +65,20 @@ constexpr auto div(Lhs const &lhs, Rhs const &rhs) return apply(lhs, rhs); } +template +constexpr auto equal(Lhs const &lhs, Rhs const &rhs) + -> primitive_dispatch_result_t { + return apply(lhs, rhs); +} + +template +constexpr auto not_equal(Lhs const &lhs, Rhs const &rhs) + -> primitive_dispatch_result_t { + return apply(lhs, rhs); +} + } // namespace mcpplibs::primitives::operations export namespace mcpplibs::primitives::operators { @@ -99,4 +113,18 @@ 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::primitive_dispatch_result_t { + return operations::equal(lhs, rhs); +} + +template +constexpr auto operator!=(Lhs const &lhs, Rhs const &rhs) + -> operations::primitive_dispatch_result_t { + return operations::not_equal(lhs, rhs); +} + } // namespace mcpplibs::primitives::operators \ No newline at end of file From 31f34c587096a04a6a0d4922788c6bb7ef0c5d35 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 20:08:27 +0800 Subject: [PATCH 24/57] refactor: Refactor dispatcher to use underlying traits and add validation for operand representations --- src/operations/dispatcher.cppm | 57 ++++++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/src/operations/dispatcher.cppm b/src/operations/dispatcher.cppm index d5550ee..68186b0 100644 --- a/src/operations/dispatcher.cppm +++ b/src/operations/dispatcher.cppm @@ -11,12 +11,13 @@ import mcpplibs.primitives.primitive.impl; import mcpplibs.primitives.primitive.traits; import mcpplibs.primitives.policy.handler; import mcpplibs.primitives.policy.impl; +import mcpplibs.primitives.underlying; export namespace mcpplibs::primitives::operations { template concept primitive_instance = requires { - typename mcpplibs::primitives::traits::primitive_traits< + typename primitives::traits::primitive_traits< std::remove_cvref_t>::value_type; }; @@ -27,19 +28,22 @@ struct dispatcher_meta { using rhs_primitive = std::remove_cvref_t; using lhs_traits = - mcpplibs::primitives::traits::primitive_traits; + primitives::traits::primitive_traits; using rhs_traits = - mcpplibs::primitives::traits::primitive_traits; + primitives::traits::primitive_traits; - using lhs_rep = typename lhs_traits::value_type; - using rhs_rep = typename rhs_traits::value_type; + using lhs_value_type = lhs_traits::value_type; + using rhs_value_type = rhs_traits::value_type; - using type_policy = typename lhs_traits::type_policy; - using value_policy = typename lhs_traits::value_policy; - using error_policy = typename lhs_traits::error_policy; - using concurrency_policy = typename lhs_traits::concurrency_policy; + using lhs_rep = underlying::traits::rep_type; + using rhs_rep = underlying::traits::rep_type; - using common_rep = typename policy::type_handler::common_rep; static constexpr bool type_ready = @@ -69,7 +73,7 @@ template dispatch_result_t { using meta = dispatcher_meta; - using common_rep = typename meta::common_rep; + using common_rep = meta::common_rep; using concurrency_handler_t = policy::concurrency_handler; @@ -84,6 +88,8 @@ 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(op_capability_valid_v, + "Operation must declare a non-none capability_mask"); static_assert( meta::concurrency_ready, "Missing concurrency_handler specialization for this operation/policy"); @@ -99,8 +105,33 @@ constexpr auto dispatch(Lhs const &lhs, Rhs const &rhs) invoker::inject_concurrency(); // Runtime stage 2: value path. - auto const lhs_common = static_cast(lhs.value()); - auto const rhs_common = static_cast(rhs.value()); + auto const lhs_rep_raw = underlying::traits< + typename meta::lhs_value_type>::to_rep(lhs.value()); + auto const rhs_rep_raw = underlying::traits< + typename meta::rhs_value_type>::to_rep(rhs.value()); + + if (!underlying::traits< + typename meta::lhs_value_type>::is_valid_rep(lhs_rep_raw) || + !underlying::traits< + typename meta::rhs_value_type>::is_valid_rep(rhs_rep_raw)) { + policy::error_request request{}; + request.kind = policy::runtime_error_kind::domain_error; + request.reason = "invalid underlying representation"; + return invoker::resolve_error(request); + } + + auto const lhs_value_normalized = underlying::traits< + typename meta::lhs_value_type>::from_rep(lhs_rep_raw); + auto const rhs_value_normalized = underlying::traits< + typename meta::rhs_value_type>::from_rep(rhs_rep_raw); + + auto const lhs_common = static_cast( + underlying::traits< + typename meta::lhs_value_type>::to_rep(lhs_value_normalized)); + auto const rhs_common = static_cast( + underlying::traits< + typename meta::rhs_value_type>::to_rep(rhs_value_normalized)); auto const decision = invoker::run_value Date: Tue, 17 Mar 2026 20:08:46 +0800 Subject: [PATCH 25/57] feat: Add equality and inequality comparison functions and op_binding specializations --- src/operations/binding.cppm | 89 ++++++++++++++++++++++++++++++++++++- 1 file changed, 88 insertions(+), 1 deletion(-) diff --git a/src/operations/binding.cppm b/src/operations/binding.cppm index 5c1f25c..53a3bcd 100644 --- a/src/operations/binding.cppm +++ b/src/operations/binding.cppm @@ -1,6 +1,5 @@ module; -#include #include #include #include @@ -190,6 +189,34 @@ constexpr auto checked_div(T lhs, T rhs) -> policy::value_decision { "checked division not supported for negotiated common type", lhs, rhs); } +template +constexpr auto compare_equal(T lhs, T rhs) -> policy::value_decision { + policy::value_decision out{}; + if constexpr (requires { lhs == rhs; }) { + out.has_value = true; + out.value = static_cast(lhs == rhs); + return out; + } + + return make_error(policy::runtime_error_kind::unspecified, + "comparison equality not supported for negotiated " + "common type"); +} + +template +constexpr auto compare_not_equal(T lhs, T rhs) -> policy::value_decision { + policy::value_decision out{}; + if constexpr (requires { lhs != rhs; }) { + out.has_value = true; + out.value = static_cast(lhs != rhs); + return out; + } + + return make_error(policy::runtime_error_kind::unspecified, + "comparison inequality not supported for negotiated " + "common type"); +} + template constexpr auto unchecked_add(T lhs, T rhs) -> policy::value_decision { policy::value_decision out{}; @@ -528,6 +555,66 @@ 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_equal(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_equal(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_equal(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_not_equal(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_not_equal(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_not_equal(lhs, rhs); + } +}; + template concept op_binding_available = requires { requires operation; From e4b33be7422b620d71546f2cc018e2f0188b510c Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 20:13:03 +0800 Subject: [PATCH 26/57] refactor: Update and expand underlying traits and concepts --- src/underlying/traits.cppm | 52 ++++++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/src/underlying/traits.cppm b/src/underlying/traits.cppm index f248bd9..c63396a 100644 --- a/src/underlying/traits.cppm +++ b/src/underlying/traits.cppm @@ -26,7 +26,7 @@ concept std_integer = std::integral> && (!std_bool) && (!std_char); template -concept std_numeric = std_integer || std_floating; +concept std_numeric = std_integer || std_floating; template concept std_underlying_type = @@ -59,7 +59,7 @@ template struct traits { }; } // namespace underlying -} +} // namespace mcpplibs::primitives namespace mcpplibs::primitives::underlying::details { @@ -91,7 +91,24 @@ concept has_rep_bridge = template concept has_std_rep_type = - has_rep_type && std_underlying_type>::rep_type>; + has_rep_type && + std_underlying_type>::rep_type>; + +template +concept has_custom_numeric_rep_type = + has_rep_type && + requires(typename traits>::rep_type a, + typename traits>::rep_type b) { + { a + b }; + { a - b }; + { a * b }; + { a / b }; + { a == b } -> std::convertible_to; + }; + +template +concept has_supported_rep_type = + has_std_rep_type || has_custom_numeric_rep_type; template consteval category category_of_std_underlying_type() { @@ -108,10 +125,14 @@ consteval category category_of_std_underlying_type() { template concept has_consistent_category = - has_category && has_std_rep_type && - (traits>::kind == - category_of_std_underlying_type< - typename traits>::rep_type>()); + has_category && has_supported_rep_type && + ((has_std_rep_type && + (traits>::kind == + category_of_std_underlying_type< + typename traits>::rep_type>())) || + (!has_std_rep_type && + (traits>::kind == category::integer || + traits>::kind == category::floating))); } // namespace mcpplibs::primitives::underlying::details @@ -119,33 +140,32 @@ export namespace mcpplibs::primitives { template concept underlying_type = - underlying::details::enabled && - underlying::details::has_category && + underlying::details::enabled && underlying::details::has_category && underlying::details::has_rep_bridge && - underlying::details::has_std_rep_type && + underlying::details::has_supported_rep_type && underlying::details::has_consistent_category; template concept boolean_underlying_type = underlying_type && (underlying::traits>::kind == - underlying::category::boolean); + underlying::category::boolean); template concept character_underlying_type = underlying_type && (underlying::traits>::kind == - underlying::category::character); + underlying::category::character); template concept integer_underlying_type = underlying_type && (underlying::traits>::kind == - underlying::category::integer); + underlying::category::integer); template concept floating_underlying_type = underlying_type && (underlying::traits>::kind == - underlying::category::floating); + underlying::category::floating); template -concept numeric_underlying_type = integer_underlying_type || floating_underlying_type; +concept numeric_underlying_type = + integer_underlying_type || floating_underlying_type; } // namespace mcpplibs::primitives - From 399f01649f8550f8b57d896c1d0ec40950075f9f Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 20:13:17 +0800 Subject: [PATCH 27/57] test: Add tests for boolean and character underlying type operations and comparison delegates --- tests/basic/test_operations.cpp | 75 +++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/tests/basic/test_operations.cpp b/tests/basic/test_operations.cpp index 4f3d8b4..9b280ef 100644 --- a/tests/basic/test_operations.cpp +++ b/tests/basic/test_operations.cpp @@ -176,6 +176,67 @@ TEST(OperationsTest, StrictTypeAllowsSameTypeAtRuntime) { EXPECT_EQ(result->value(), 42); } +TEST(OperationsTest, BoolUnderlyingRejectsArithmeticOperationsAtCompileTime) { + using value_t = primitive; + using bool_handler = policy::type_handler; + using bool_meta = + operations::dispatcher_meta; + + static_assert(bool_handler::enabled); + static_assert(!bool_handler::allowed); + static_assert(std::is_same_v); + + EXPECT_EQ(bool_handler::diagnostic_id, 3u); +} + +TEST(OperationsTest, CharUnderlyingRejectsArithmeticEvenWithTransparentType) { + using value_t = primitive; + using char_handler = policy::type_handler; + using char_meta = + operations::dispatcher_meta; + + static_assert(char_handler::enabled); + static_assert(!char_handler::allowed); + static_assert(std::is_same_v); + + EXPECT_EQ(char_handler::diagnostic_id, 3u); +} + +TEST(OperationsTest, BoolUnderlyingAllowsComparisonOperations) { + using value_t = primitive; + + auto const lhs = value_t{true}; + auto const rhs = value_t{false}; + + auto const eq_result = operations::equal(lhs, rhs); + auto const ne_result = operations::not_equal(lhs, rhs); + + ASSERT_TRUE(eq_result.has_value()); + ASSERT_TRUE(ne_result.has_value()); + EXPECT_FALSE(eq_result->value()); + EXPECT_TRUE(ne_result->value()); +} + +TEST(OperationsTest, CharUnderlyingAllowsComparisonWithTransparentType) { + using value_t = primitive; + + auto const lhs = value_t{'a'}; + auto const rhs = value_t{'a'}; + + auto const eq_result = operations::equal(lhs, rhs); + + ASSERT_TRUE(eq_result.has_value()); + EXPECT_EQ(eq_result->value(), static_cast(1)); +} + TEST(OperationsTest, PrimitiveAliasWorksWithFrameworkOperators) { using namespace mcpplibs::primitives::types; using namespace mcpplibs::primitives::operators; @@ -208,6 +269,20 @@ TEST(OperationsTest, PrimitiveAliasMixesWithBuiltinArithmeticExplicitly) { EXPECT_EQ(result->value(), 43); } +TEST(OperationsTest, OperatorEqualDelegatesToDispatcher) { + using namespace mcpplibs::primitives::operators; + using value_t = primitive; + + auto const lhs = value_t{7}; + auto const rhs = value_t{7}; + + auto const result = (lhs == rhs); + + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(result->value(), 1); +} + TEST(OperationsTest, OperatorPlusDelegatesToDispatcher) { using namespace mcpplibs::primitives::operators; using value_t = primitive; From 1eb53394c5a19a31d8bb16b015e7947e2fe10179 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 20:13:56 +0800 Subject: [PATCH 28/57] test: Add tests for operation traits and capability masks --- tests/basic/test_policies.cpp | 51 +++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/tests/basic/test_policies.cpp b/tests/basic/test_policies.cpp index 7893344..6862da6 100644 --- a/tests/basic/test_policies.cpp +++ b/tests/basic/test_policies.cpp @@ -4,6 +4,19 @@ import mcpplibs.primitives; using namespace mcpplibs::primitives; +namespace { +struct NullCapabilityProbe {}; +} // namespace + +template <> +struct operations::traits { + using op_tag = NullCapabilityProbe; + + static constexpr bool enabled = true; + static constexpr auto arity = dimension::binary; + static constexpr auto capability_mask = capability::none; +}; + TEST(PolicyTraitsTest, BuiltinPoliciesHaveCategories) { using namespace policy; @@ -63,4 +76,42 @@ TEST(PolicyConcurrencyTest, AtomicInjectsFences) { EXPECT_FALSE(single_injection.fence_after); } +TEST(OperationTraitsTest, BuiltinArithmeticOperationsExposeCapability) { + EXPECT_TRUE( + (operations::op_has_capability_v)); + EXPECT_TRUE( + (operations::op_has_capability_v)); + EXPECT_TRUE( + (operations::op_has_capability_v)); + EXPECT_TRUE( + (operations::op_has_capability_v)); + EXPECT_FALSE( + (operations::op_has_capability_v)); + EXPECT_TRUE( + (operations::op_has_capability_v)); + EXPECT_TRUE( + (operations::op_has_capability_v)); +} + +TEST(OperationTraitsTest, BuiltinOperationsHaveNonNullCapabilityMask) { + EXPECT_TRUE((operations::op_capability_valid_v)); + EXPECT_TRUE((operations::op_capability_valid_v)); + EXPECT_TRUE((operations::op_capability_valid_v)); + EXPECT_TRUE((operations::op_capability_valid_v)); + EXPECT_TRUE((operations::op_capability_valid_v)); + EXPECT_TRUE((operations::op_capability_valid_v)); +} + +TEST(OperationTraitsTest, NullCapabilityMaskIsDetectedAtCompileTime) { + static_assert(!operations::op_capability_valid_v); + EXPECT_FALSE((operations::op_capability_valid_v)); +} + // Use the existing test runner main from other test translation unit. From 35cca990356196fc40def82ce2e55b577bafbf33 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 20:14:27 +0800 Subject: [PATCH 29/57] test: Add and update tests for custom numeric-like types and underlying traits --- tests/basic/test_templates.cpp | 260 +++++++++++++++++++++++++++++++-- 1 file changed, 245 insertions(+), 15 deletions(-) diff --git a/tests/basic/test_templates.cpp b/tests/basic/test_templates.cpp index 2c8ca8e..a4f164e 100644 --- a/tests/basic/test_templates.cpp +++ b/tests/basic/test_templates.cpp @@ -20,25 +20,116 @@ struct BadKind { int value; }; +struct BigIntLike { + long long value; + + friend constexpr auto operator+(BigIntLike lhs, BigIntLike rhs) noexcept + -> BigIntLike { + return BigIntLike{lhs.value + rhs.value}; + } + + friend constexpr auto operator-(BigIntLike lhs, BigIntLike rhs) noexcept + -> BigIntLike { + return BigIntLike{lhs.value - rhs.value}; + } + + friend constexpr auto operator*(BigIntLike lhs, BigIntLike rhs) noexcept + -> BigIntLike { + return BigIntLike{lhs.value * rhs.value}; + } + + friend constexpr auto operator/(BigIntLike lhs, BigIntLike rhs) noexcept + -> BigIntLike { + return BigIntLike{lhs.value / rhs.value}; + } + + friend constexpr auto operator==(BigIntLike lhs, BigIntLike rhs) noexcept + -> bool { + return lhs.value == rhs.value; + } +}; + +struct BadCustomKind { + long long value; + + friend constexpr auto operator+(BadCustomKind lhs, BadCustomKind rhs) noexcept + -> BadCustomKind { + return BadCustomKind{lhs.value + rhs.value}; + } + + friend constexpr auto operator-(BadCustomKind lhs, BadCustomKind rhs) noexcept + -> BadCustomKind { + return BadCustomKind{lhs.value - rhs.value}; + } + + friend constexpr auto operator*(BadCustomKind lhs, BadCustomKind rhs) noexcept + -> BadCustomKind { + return BadCustomKind{lhs.value * rhs.value}; + } + + friend constexpr auto operator/(BadCustomKind lhs, BadCustomKind rhs) noexcept + -> BadCustomKind { + return BadCustomKind{lhs.value / rhs.value}; + } + + friend constexpr auto operator==(BadCustomKind lhs, + BadCustomKind rhs) noexcept -> bool { + return lhs.value == rhs.value; + } +}; + +struct MissingDivisionLike { + long long value; + + friend constexpr auto operator+(MissingDivisionLike lhs, + MissingDivisionLike rhs) noexcept + -> MissingDivisionLike { + return MissingDivisionLike{lhs.value + rhs.value}; + } + + friend constexpr auto operator-(MissingDivisionLike lhs, + MissingDivisionLike rhs) noexcept + -> MissingDivisionLike { + return MissingDivisionLike{lhs.value - rhs.value}; + } + + friend constexpr auto operator*(MissingDivisionLike lhs, + MissingDivisionLike rhs) noexcept + -> MissingDivisionLike { + return MissingDivisionLike{lhs.value * rhs.value}; + } + + friend constexpr auto operator==(MissingDivisionLike lhs, + MissingDivisionLike rhs) noexcept -> bool { + return lhs.value == rhs.value; + } +}; + +struct NonNegativeInt { + int value; +}; + } // namespace -template <> -struct mcpplibs::primitives::underlying::traits { +template <> struct mcpplibs::primitives::underlying::traits { using value_type = UserInteger; 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.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 UserInteger{value}; } + static constexpr value_type from_rep(rep_type value) noexcept { + return UserInteger{value}; + } static constexpr bool is_valid_rep(rep_type) noexcept { return true; } }; -template <> -struct mcpplibs::primitives::underlying::traits { +template <> struct mcpplibs::primitives::underlying::traits { using value_type = BadRep; using rep_type = BadRep; @@ -47,26 +138,100 @@ struct mcpplibs::primitives::underlying::traits { 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 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 { +template <> struct mcpplibs::primitives::underlying::traits { using value_type = BadKind; using rep_type = int; static constexpr bool enabled = true; static constexpr auto kind = category::floating; - static constexpr rep_type to_rep(value_type value) noexcept { return value.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 BadKind{value}; + } + + static constexpr bool is_valid_rep(rep_type) noexcept { return true; } +}; + +template <> struct mcpplibs::primitives::underlying::traits { + using value_type = BigIntLike; + using rep_type = BigIntLike; + + 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 = BadCustomKind; + using rep_type = BadCustomKind; + + static constexpr bool enabled = true; + static constexpr auto kind = category::boolean; + + 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 = MissingDivisionLike; + using rep_type = MissingDivisionLike; - static constexpr value_type from_rep(rep_type value) noexcept { return BadKind{value}; } + 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 = NonNegativeInt; + 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.value; + } + + static constexpr value_type from_rep(rep_type value) noexcept { + return NonNegativeInt{value}; + } + + static constexpr bool is_valid_rep(rep_type value) noexcept { + return value >= 0; + } +}; + TEST(PrimitiveTraitsTest, StandardTypeConcepts) { EXPECT_TRUE((mcpplibs::primitives::std_integer)); EXPECT_TRUE((mcpplibs::primitives::std_integer)); @@ -86,7 +251,7 @@ TEST(PrimitiveTraitsTest, StandardTypeConcepts) { EXPECT_TRUE((mcpplibs::primitives::std_underlying_type)); EXPECT_TRUE((mcpplibs::primitives::std_underlying_type)); - EXPECT_FALSE((mcpplibs::primitives::std_underlying_type)); + EXPECT_FALSE((mcpplibs::primitives::std_underlying_type)); } TEST(PrimitiveTraitsTest, UnderlyingTraitsDefaultsAndCustomRegistration) { @@ -95,13 +260,17 @@ TEST(PrimitiveTraitsTest, UnderlyingTraitsDefaultsAndCustomRegistration) { mcpplibs::primitives::underlying::category::integer); EXPECT_TRUE((mcpplibs::primitives::underlying_type)); - EXPECT_EQ(mcpplibs::primitives::underlying::traits::to_rep(UserInteger{7}), 7); + EXPECT_EQ(mcpplibs::primitives::underlying::traits::to_rep( + UserInteger{7}), + 7); EXPECT_FALSE((mcpplibs::primitives::underlying_type)); - EXPECT_FALSE((mcpplibs::primitives::underlying::traits::enabled)); + EXPECT_FALSE( + (mcpplibs::primitives::underlying::traits::enabled)); } -TEST(PrimitiveTraitsTest, UnderlyingTypeRequiresValidRepTypeAndCategoryConsistency) { +TEST(PrimitiveTraitsTest, + UnderlyingTypeRequiresValidRepTypeAndCategoryConsistency) { EXPECT_TRUE((mcpplibs::primitives::underlying::traits::enabled)); EXPECT_FALSE((mcpplibs::primitives::underlying_type)); @@ -109,6 +278,67 @@ TEST(PrimitiveTraitsTest, UnderlyingTypeRequiresValidRepTypeAndCategoryConsisten EXPECT_FALSE((mcpplibs::primitives::underlying_type)); } +TEST(PrimitiveTraitsTest, UnderlyingTypeAllowsCustomNumericLikeRepType) { + EXPECT_TRUE((mcpplibs::primitives::underlying::traits::enabled)); + EXPECT_TRUE((mcpplibs::primitives::underlying_type)); +} + +TEST(PrimitiveTraitsTest, CustomNumericLikeRepTypeRejectsInvalidCategory) { + EXPECT_TRUE( + (mcpplibs::primitives::underlying::traits::enabled)); + EXPECT_FALSE((mcpplibs::primitives::underlying_type)); +} + +TEST(PrimitiveTraitsTest, CustomUnderlyingParticipatesInPrimitiveOperations) { + using value_t = mcpplibs::primitives::primitive< + BigIntLike, mcpplibs::primitives::policy::checked_value, + mcpplibs::primitives::policy::expected_error>; + + auto const lhs = value_t{BigIntLike{40}}; + auto const rhs = value_t{BigIntLike{2}}; + + auto const result = mcpplibs::primitives::operations::add(lhs, rhs); + + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(result->value().value, 42); +} + +TEST(PrimitiveTraitsTest, CustomWrappedUnderlyingUsesRepBridgeForArithmetic) { + using value_t = mcpplibs::primitives::primitive< + UserInteger, mcpplibs::primitives::policy::checked_value, + mcpplibs::primitives::policy::expected_error>; + + auto const lhs = value_t{UserInteger{40}}; + auto const rhs = value_t{UserInteger{2}}; + + auto const result = mcpplibs::primitives::operations::add(lhs, rhs); + + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(result->value(), 42); +} + +TEST(PrimitiveTraitsTest, InvalidUnderlyingRepIsRejectedByDispatcher) { + using value_t = mcpplibs::primitives::primitive< + NonNegativeInt, mcpplibs::primitives::policy::checked_value, + mcpplibs::primitives::policy::expected_error>; + + auto const lhs = value_t{NonNegativeInt{-1}}; + auto const rhs = value_t{NonNegativeInt{2}}; + + auto const result = mcpplibs::primitives::operations::add(lhs, rhs); + + ASSERT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), + mcpplibs::primitives::policy::runtime_error_kind::domain_error); +} + +TEST(PrimitiveTraitsTest, + CustomNumericLikeRepTypeRequiresDivisionOperatorForEligibility) { + EXPECT_TRUE( + (mcpplibs::primitives::underlying::traits::enabled)); + EXPECT_FALSE((mcpplibs::primitives::underlying_type)); +} + int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); From 1d53d380550ecd8fcc4c6b9c3d006fb2c7c3be4a Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 20:14:40 +0800 Subject: [PATCH 30/57] refactor: Add and update type handlers to reject arithmetic for boolean and character types --- src/policy/impl.cppm | 45 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/src/policy/impl.cppm b/src/policy/impl.cppm index 9e3c338..f07d0d5 100644 --- a/src/policy/impl.cppm +++ b/src/policy/impl.cppm @@ -9,6 +9,7 @@ module; export module mcpplibs.primitives.policy.impl; import mcpplibs.primitives.operations.traits; +import mcpplibs.primitives.operations.impl; import mcpplibs.primitives.policy.traits; import mcpplibs.primitives.policy.handler; @@ -100,12 +101,39 @@ using default_type = strict_type; using default_error = throw_error; using default_concurrency = single_thread; +namespace details { + +template +inline constexpr bool is_arithmetic_operation_v = + operations::op_has_capability_v; + +template +inline constexpr bool is_bool_or_text_char_v = + std::same_as, bool> || + std::same_as, char> || + std::same_as, wchar_t> || + std::same_as, char8_t> || + std::same_as, char16_t> || + std::same_as, char32_t>; + +template +inline constexpr bool rejects_arithmetic_for_bool_or_char_v = + is_arithmetic_operation_v && + (is_bool_or_text_char_v || is_bool_or_text_char_v); + +} // namespace details + // Default protocol specializations. template struct type_handler { static constexpr bool enabled = true; - static constexpr bool allowed = std::same_as; - static constexpr unsigned diagnostic_id = allowed ? 0u : 1u; + static constexpr bool allowed = + std::same_as && + !details::rejects_arithmetic_for_bool_or_char_v; + static constexpr unsigned diagnostic_id = + details::rejects_arithmetic_for_bool_or_char_v + ? 3u + : (allowed ? 0u : 1u); using common_rep = std::conditional_t; }; @@ -113,8 +141,12 @@ template struct type_handler { static constexpr bool enabled = true; static constexpr bool allowed = - std::is_arithmetic_v && std::is_arithmetic_v; - static constexpr unsigned diagnostic_id = allowed ? 0u : 2u; + std::is_arithmetic_v && std::is_arithmetic_v && + !details::rejects_arithmetic_for_bool_or_char_v; + static constexpr unsigned diagnostic_id = + details::rejects_arithmetic_for_bool_or_char_v + ? 3u + : (allowed ? 0u : 2u); using common_rep = std::conditional_t, void>; }; @@ -122,8 +154,9 @@ struct type_handler { template struct type_handler { static constexpr bool enabled = true; - static constexpr bool allowed = true; - static constexpr unsigned diagnostic_id = 0u; + static constexpr bool allowed = + !details::rejects_arithmetic_for_bool_or_char_v; + static constexpr unsigned diagnostic_id = allowed ? 0u : 3u; using common_rep = std::common_type_t; }; From 35f401fafa884329b461af8f2c8f05c0ac518043 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 20:23:28 +0800 Subject: [PATCH 31/57] refactor: Update and simplify boolean and character type checks --- src/policy/impl.cppm | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/policy/impl.cppm b/src/policy/impl.cppm index f07d0d5..fc636e2 100644 --- a/src/policy/impl.cppm +++ b/src/policy/impl.cppm @@ -12,6 +12,7 @@ import mcpplibs.primitives.operations.traits; import mcpplibs.primitives.operations.impl; import mcpplibs.primitives.policy.traits; import mcpplibs.primitives.policy.handler; +import mcpplibs.primitives.underlying.traits; export namespace mcpplibs::primitives::policy { @@ -108,18 +109,12 @@ inline constexpr bool is_arithmetic_operation_v = operations::op_has_capability_v; template -inline constexpr bool is_bool_or_text_char_v = - std::same_as, bool> || - std::same_as, char> || - std::same_as, wchar_t> || - std::same_as, char8_t> || - std::same_as, char16_t> || - std::same_as, char32_t>; +inline constexpr bool is_boolean_or_character_v = std_bool || std_char; template -inline constexpr bool rejects_arithmetic_for_bool_or_char_v = +inline constexpr bool rejects_arithmetic_for_boolean_or_character_v = is_arithmetic_operation_v && - (is_bool_or_text_char_v || is_bool_or_text_char_v); + (is_boolean_or_character_v || is_boolean_or_character_v); } // namespace details @@ -129,9 +124,11 @@ struct type_handler { static constexpr bool enabled = true; static constexpr bool allowed = std::same_as && - !details::rejects_arithmetic_for_bool_or_char_v; + !details::rejects_arithmetic_for_boolean_or_character_v; static constexpr unsigned diagnostic_id = - details::rejects_arithmetic_for_bool_or_char_v + details::rejects_arithmetic_for_boolean_or_character_v ? 3u : (allowed ? 0u : 1u); using common_rep = std::conditional_t; @@ -142,9 +139,11 @@ struct type_handler { static constexpr bool enabled = true; static constexpr bool allowed = std::is_arithmetic_v && std::is_arithmetic_v && - !details::rejects_arithmetic_for_bool_or_char_v; + !details::rejects_arithmetic_for_boolean_or_character_v; static constexpr unsigned diagnostic_id = - details::rejects_arithmetic_for_bool_or_char_v + details::rejects_arithmetic_for_boolean_or_character_v ? 3u : (allowed ? 0u : 2u); using common_rep = @@ -155,7 +154,8 @@ template struct type_handler { static constexpr bool enabled = true; static constexpr bool allowed = - !details::rejects_arithmetic_for_bool_or_char_v; + !details::rejects_arithmetic_for_boolean_or_character_v; static constexpr unsigned diagnostic_id = allowed ? 0u : 3u; using common_rep = std::common_type_t; }; From 851c4b2ed9ebf0e0fa654ea7fc09f7507a2f201d Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 20:23:39 +0800 Subject: [PATCH 32/57] test: Update tests to use `std::uint16_t` for arithmetic operations --- tests/basic/test_operations.cpp | 40 ++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/tests/basic/test_operations.cpp b/tests/basic/test_operations.cpp index 9b280ef..035690b 100644 --- a/tests/basic/test_operations.cpp +++ b/tests/basic/test_operations.cpp @@ -1,6 +1,7 @@ #include #include +#include #include #include @@ -34,23 +35,23 @@ TEST(OperationsTest, DivisionByZeroReturnsError) { } TEST(OperationsTest, SaturatingAdditionClampsUnsignedOverflow) { - using value_t = primitive; + using value_t = primitive; - auto const lhs = value_t{static_cast(250)}; - auto const rhs = value_t{static_cast(20)}; + auto const lhs = value_t{static_cast(65530)}; + auto const rhs = value_t{static_cast(20)}; auto const result = operations::add(lhs, rhs); ASSERT_TRUE(result.has_value()); - EXPECT_EQ(result->value(), static_cast(255)); + EXPECT_EQ(result->value(), static_cast(65535)); } TEST(OperationsTest, CheckedAdditionReportsUnsignedOverflow) { using value_t = - primitive; + primitive; - auto const lhs = value_t{static_cast(250)}; - auto const rhs = value_t{static_cast(20)}; + auto const lhs = value_t{static_cast(65530)}; + auto const rhs = value_t{static_cast(20)}; auto const result = operations::add(lhs, rhs); @@ -60,15 +61,15 @@ TEST(OperationsTest, CheckedAdditionReportsUnsignedOverflow) { TEST(OperationsTest, UncheckedAdditionWrapsUnsignedOverflow) { using value_t = - primitive; + primitive; - auto const lhs = value_t{static_cast(250)}; - auto const rhs = value_t{static_cast(20)}; + auto const lhs = value_t{static_cast(65530)}; + auto const rhs = value_t{static_cast(20)}; auto const result = operations::add(lhs, rhs); ASSERT_TRUE(result.has_value()); - EXPECT_EQ(result->value(), static_cast(14)); + EXPECT_EQ(result->value(), static_cast(14)); } TEST(OperationsTest, UncheckedDivisionUsesRawArithmeticWhenValid) { @@ -208,6 +209,23 @@ TEST(OperationsTest, CharUnderlyingRejectsArithmeticEvenWithTransparentType) { EXPECT_EQ(char_handler::diagnostic_id, 3u); } +TEST(OperationsTest, SignedAndUnsignedCharRejectArithmeticAtCompileTime) { + using signed_handler = + policy::type_handler; + using unsigned_handler = + policy::type_handler; + + static_assert(signed_handler::enabled); + static_assert(!signed_handler::allowed); + static_assert(unsigned_handler::enabled); + static_assert(!unsigned_handler::allowed); + + EXPECT_EQ(signed_handler::diagnostic_id, 3u); + EXPECT_EQ(unsigned_handler::diagnostic_id, 3u); +} + TEST(OperationsTest, BoolUnderlyingAllowsComparisonOperations) { using value_t = primitive; From 817d3bb3e3bacde529404335b595313afd7b1d5e Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 21:40:15 +0800 Subject: [PATCH 33/57] refactor: Rename and reorganize error, concurrency, type, and value handlers --- src/operations/binding.cppm | 151 ++++++++++++++++----------------- src/operations/dispatcher.cppm | 82 +++++++++--------- src/operations/invoker.cppm | 10 +-- src/operations/operators.cppm | 16 ++-- src/policy/handler.cppm | 120 ++++++++++++++------------ src/policy/impl.cppm | 48 +++++------ 6 files changed, 219 insertions(+), 208 deletions(-) diff --git a/src/operations/binding.cppm b/src/operations/binding.cppm index 53a3bcd..97c6e0d 100644 --- a/src/operations/binding.cppm +++ b/src/operations/binding.cppm @@ -17,11 +17,11 @@ export namespace mcpplibs::primitives::operations::binding { namespace details { template -constexpr auto make_error(policy::runtime_error_kind kind, char const *reason, +constexpr auto make_error(policy::error::kind kind, char const *reason, std::optional lhs = std::nullopt, std::optional rhs = std::nullopt) - -> policy::value_decision { - policy::value_decision out{}; + -> policy::value::decision { + policy::value::decision out{}; out.has_value = false; out.error.kind = kind; out.error.reason = reason; @@ -31,19 +31,19 @@ constexpr auto make_error(policy::runtime_error_kind kind, char const *reason, } template -constexpr auto checked_add(T lhs, T rhs) -> policy::value_decision { +constexpr auto checked_add(T lhs, T rhs) -> policy::value::decision { if constexpr (!std::is_integral_v || std::is_same_v) { - policy::value_decision out{}; + policy::value::decision out{}; out.has_value = true; out.value = static_cast(lhs + rhs); return out; } else if constexpr (std::is_unsigned_v) { auto const maxv = std::numeric_limits::max(); if (lhs > maxv - rhs) { - return make_error(policy::runtime_error_kind::overflow, + return make_error(policy::error::kind::overflow, "checked addition overflow", lhs, rhs); } - policy::value_decision out{}; + policy::value::decision out{}; out.has_value = true; out.value = static_cast(lhs + rhs); return out; @@ -51,14 +51,14 @@ constexpr auto checked_add(T lhs, T rhs) -> policy::value_decision { auto const maxv = std::numeric_limits::max(); auto const minv = std::numeric_limits::min(); if ((rhs > 0) && (lhs > maxv - rhs)) { - return make_error(policy::runtime_error_kind::overflow, + return make_error(policy::error::kind::overflow, "checked addition overflow", lhs, rhs); } if ((rhs < 0) && (lhs < minv - rhs)) { - return make_error(policy::runtime_error_kind::underflow, + return make_error(policy::error::kind::underflow, "checked addition underflow", lhs, rhs); } - policy::value_decision out{}; + policy::value::decision out{}; out.has_value = true; out.value = static_cast(lhs + rhs); return out; @@ -66,18 +66,18 @@ constexpr auto checked_add(T lhs, T rhs) -> policy::value_decision { } template -constexpr auto checked_sub(T lhs, T rhs) -> policy::value_decision { +constexpr auto checked_sub(T lhs, T rhs) -> policy::value::decision { if constexpr (!std::is_integral_v || std::is_same_v) { - policy::value_decision out{}; + policy::value::decision out{}; out.has_value = true; out.value = static_cast(lhs - rhs); return out; } else if constexpr (std::is_unsigned_v) { if (lhs < rhs) { - return make_error(policy::runtime_error_kind::underflow, + return make_error(policy::error::kind::underflow, "checked subtraction underflow", lhs, rhs); } - policy::value_decision out{}; + policy::value::decision out{}; out.has_value = true; out.value = static_cast(lhs - rhs); return out; @@ -85,14 +85,14 @@ constexpr auto checked_sub(T lhs, T rhs) -> policy::value_decision { auto const maxv = std::numeric_limits::max(); auto const minv = std::numeric_limits::min(); if ((rhs < 0) && (lhs > maxv + rhs)) { - return make_error(policy::runtime_error_kind::overflow, + return make_error(policy::error::kind::overflow, "checked subtraction overflow", lhs, rhs); } if ((rhs > 0) && (lhs < minv + rhs)) { - return make_error(policy::runtime_error_kind::underflow, + return make_error(policy::error::kind::underflow, "checked subtraction underflow", lhs, rhs); } - policy::value_decision out{}; + policy::value::decision out{}; out.has_value = true; out.value = static_cast(lhs - rhs); return out; @@ -100,15 +100,15 @@ constexpr auto checked_sub(T lhs, T rhs) -> policy::value_decision { } template -constexpr auto checked_mul(T lhs, T rhs) -> policy::value_decision { +constexpr auto checked_mul(T lhs, T rhs) -> policy::value::decision { if constexpr (!std::is_integral_v || std::is_same_v) { - policy::value_decision out{}; + policy::value::decision out{}; out.has_value = true; out.value = static_cast(lhs * rhs); return out; } else { if (lhs == T{} || rhs == T{}) { - policy::value_decision out{}; + policy::value::decision out{}; out.has_value = true; out.value = T{}; return out; @@ -117,10 +117,10 @@ constexpr auto checked_mul(T lhs, T rhs) -> policy::value_decision { if constexpr (std::is_unsigned_v) { auto const maxv = std::numeric_limits::max(); if (lhs > maxv / rhs) { - return make_error(policy::runtime_error_kind::overflow, + return make_error(policy::error::kind::overflow, "checked multiplication overflow", lhs, rhs); } - policy::value_decision out{}; + policy::value::decision out{}; out.has_value = true; out.value = static_cast(lhs * rhs); return out; @@ -131,30 +131,30 @@ constexpr auto checked_mul(T lhs, T rhs) -> policy::value_decision { if (lhs > 0) { if (rhs > 0) { if (lhs > maxv / rhs) { - return make_error(policy::runtime_error_kind::overflow, + return make_error(policy::error::kind::overflow, "checked multiplication overflow", lhs, rhs); } } else { if (rhs < minv / lhs) { - return make_error(policy::runtime_error_kind::underflow, + return make_error(policy::error::kind::underflow, "checked multiplication underflow", lhs, rhs); } } } else { if (rhs > 0) { if (lhs < minv / rhs) { - return make_error(policy::runtime_error_kind::underflow, + return make_error(policy::error::kind::underflow, "checked multiplication underflow", lhs, rhs); } } else { if (lhs != 0 && rhs < maxv / lhs) { - return make_error(policy::runtime_error_kind::overflow, + return make_error(policy::error::kind::overflow, "checked multiplication overflow", lhs, rhs); } } } - policy::value_decision out{}; + policy::value::decision out{}; out.has_value = true; out.value = static_cast(lhs * rhs); return out; @@ -163,63 +163,63 @@ constexpr auto checked_mul(T lhs, T rhs) -> policy::value_decision { } template -constexpr auto checked_div(T lhs, T rhs) -> policy::value_decision { +constexpr auto checked_div(T lhs, T rhs) -> policy::value::decision { if (rhs == T{}) { - return make_error(policy::runtime_error_kind::divide_by_zero, + return make_error(policy::error::kind::divide_by_zero, "checked division by zero", lhs, rhs); } if constexpr (std::is_integral_v && std::is_signed_v) { auto const minv = std::numeric_limits::min(); if (lhs == minv && rhs == static_cast(-1)) { - return make_error(policy::runtime_error_kind::overflow, + return make_error(policy::error::kind::overflow, "checked division overflow", lhs, rhs); } } if constexpr (requires { lhs / rhs; }) { - policy::value_decision out{}; + policy::value::decision out{}; out.has_value = true; out.value = static_cast(lhs / rhs); return out; } return make_error( - policy::runtime_error_kind::unspecified, + policy::error::kind::unspecified, "checked division not supported for negotiated common type", lhs, rhs); } template -constexpr auto compare_equal(T lhs, T rhs) -> policy::value_decision { - policy::value_decision out{}; +constexpr auto compare_equal(T lhs, T rhs) -> policy::value::decision { + policy::value::decision out{}; if constexpr (requires { lhs == rhs; }) { out.has_value = true; out.value = static_cast(lhs == rhs); return out; } - return make_error(policy::runtime_error_kind::unspecified, + return make_error(policy::error::kind::unspecified, "comparison equality not supported for negotiated " "common type"); } template -constexpr auto compare_not_equal(T lhs, T rhs) -> policy::value_decision { - policy::value_decision out{}; +constexpr auto compare_not_equal(T lhs, T rhs) -> policy::value::decision { + policy::value::decision out{}; if constexpr (requires { lhs != rhs; }) { out.has_value = true; out.value = static_cast(lhs != rhs); return out; } - return make_error(policy::runtime_error_kind::unspecified, + return make_error(policy::error::kind::unspecified, "comparison inequality not supported for negotiated " "common type"); } template -constexpr auto unchecked_add(T lhs, T rhs) -> policy::value_decision { - policy::value_decision out{}; +constexpr auto unchecked_add(T lhs, T rhs) -> policy::value::decision { + policy::value::decision out{}; out.has_value = true; if constexpr (requires { lhs + rhs; }) { @@ -232,8 +232,8 @@ constexpr auto unchecked_add(T lhs, T rhs) -> policy::value_decision { } template -constexpr auto unchecked_sub(T lhs, T rhs) -> policy::value_decision { - policy::value_decision out{}; +constexpr auto unchecked_sub(T lhs, T rhs) -> policy::value::decision { + policy::value::decision out{}; out.has_value = true; if constexpr (requires { lhs - rhs; }) { @@ -246,8 +246,8 @@ constexpr auto unchecked_sub(T lhs, T rhs) -> policy::value_decision { } template -constexpr auto unchecked_mul(T lhs, T rhs) -> policy::value_decision { - policy::value_decision out{}; +constexpr auto unchecked_mul(T lhs, T rhs) -> policy::value::decision { + policy::value::decision out{}; out.has_value = true; if constexpr (requires { lhs * rhs; }) { @@ -260,8 +260,8 @@ constexpr auto unchecked_mul(T lhs, T rhs) -> policy::value_decision { } template -constexpr auto unchecked_div(T lhs, T rhs) -> policy::value_decision { - policy::value_decision out{}; +constexpr auto unchecked_div(T lhs, T rhs) -> policy::value::decision { + policy::value::decision out{}; out.has_value = true; // Intentionally no guards: unchecked policy delegates error/UB behavior @@ -351,15 +351,14 @@ template constexpr auto saturating_mul(T lhs, T rhs) -> T { template constexpr auto make_unsupported(char const *reason) - -> policy::value_decision { - return make_error(policy::runtime_error_kind::unspecified, reason); + -> policy::value::decision { + return make_error(policy::error::kind::unspecified, reason); } template constexpr auto make_div_zero(char const *reason) - -> policy::value_decision { - return make_error(policy::runtime_error_kind::divide_by_zero, - reason); + -> policy::value::decision { + return make_error(policy::error::kind::divide_by_zero, reason); } } // namespace details @@ -372,10 +371,10 @@ struct op_binding { static constexpr bool enabled = false; static constexpr auto apply(CommonRep, CommonRep) - -> policy::value_decision { - policy::value_decision out{}; + -> policy::value::decision { + policy::value::decision out{}; out.has_value = false; - out.error.kind = policy::runtime_error_kind::unspecified; + out.error.kind = policy::error::kind::unspecified; out.error.reason = "operation binding is not implemented"; return out; } @@ -386,7 +385,7 @@ struct op_binding { static constexpr bool enabled = true; static constexpr auto apply(CommonRep lhs, CommonRep rhs) - -> policy::value_decision { + -> policy::value::decision { if constexpr (requires { details::checked_add(lhs, rhs); }) { return details::checked_add(lhs, rhs); } @@ -401,7 +400,7 @@ struct op_binding { static constexpr bool enabled = true; static constexpr auto apply(CommonRep lhs, CommonRep rhs) - -> policy::value_decision { + -> policy::value::decision { if constexpr (requires { details::checked_sub(lhs, rhs); }) { return details::checked_sub(lhs, rhs); } @@ -416,7 +415,7 @@ struct op_binding { static constexpr bool enabled = true; static constexpr auto apply(CommonRep lhs, CommonRep rhs) - -> policy::value_decision { + -> policy::value::decision { if constexpr (requires { details::checked_mul(lhs, rhs); }) { return details::checked_mul(lhs, rhs); } @@ -431,7 +430,7 @@ struct op_binding { static constexpr bool enabled = true; static constexpr auto apply(CommonRep lhs, CommonRep rhs) - -> policy::value_decision { + -> policy::value::decision { if constexpr (requires { details::checked_div(lhs, rhs); }) { return details::checked_div(lhs, rhs); } @@ -446,7 +445,7 @@ struct op_binding { static constexpr bool enabled = true; static constexpr auto apply(CommonRep lhs, CommonRep rhs) - -> policy::value_decision { + -> policy::value::decision { return details::unchecked_add(lhs, rhs); } }; @@ -456,7 +455,7 @@ struct op_binding { static constexpr bool enabled = true; static constexpr auto apply(CommonRep lhs, CommonRep rhs) - -> policy::value_decision { + -> policy::value::decision { return details::unchecked_sub(lhs, rhs); } }; @@ -466,7 +465,7 @@ struct op_binding { static constexpr bool enabled = true; static constexpr auto apply(CommonRep lhs, CommonRep rhs) - -> policy::value_decision { + -> policy::value::decision { return details::unchecked_mul(lhs, rhs); } }; @@ -476,7 +475,7 @@ struct op_binding { static constexpr bool enabled = true; static constexpr auto apply(CommonRep lhs, CommonRep rhs) - -> policy::value_decision { + -> policy::value::decision { return details::unchecked_div(lhs, rhs); } }; @@ -486,8 +485,8 @@ struct op_binding { static constexpr bool enabled = true; static constexpr auto apply(CommonRep lhs, CommonRep rhs) - -> policy::value_decision { - policy::value_decision out{}; + -> policy::value::decision { + policy::value::decision out{}; if constexpr (requires { details::saturating_add(lhs, rhs); }) { out.has_value = true; out.value = details::saturating_add(lhs, rhs); @@ -503,8 +502,8 @@ struct op_binding { static constexpr bool enabled = true; static constexpr auto apply(CommonRep lhs, CommonRep rhs) - -> policy::value_decision { - policy::value_decision out{}; + -> policy::value::decision { + policy::value::decision out{}; if constexpr (requires { details::saturating_sub(lhs, rhs); }) { out.has_value = true; out.value = details::saturating_sub(lhs, rhs); @@ -520,8 +519,8 @@ struct op_binding { static constexpr bool enabled = true; static constexpr auto apply(CommonRep lhs, CommonRep rhs) - -> policy::value_decision { - policy::value_decision out{}; + -> policy::value::decision { + policy::value::decision out{}; if constexpr (requires { details::saturating_mul(lhs, rhs); }) { out.has_value = true; out.value = details::saturating_mul(lhs, rhs); @@ -537,8 +536,8 @@ struct op_binding { static constexpr bool enabled = true; static constexpr auto apply(CommonRep lhs, CommonRep rhs) - -> policy::value_decision { - policy::value_decision out{}; + -> policy::value::decision { + policy::value::decision out{}; if (rhs == CommonRep{}) { return details::make_div_zero("saturating division by zero"); @@ -560,7 +559,7 @@ struct op_binding { static constexpr bool enabled = true; static constexpr auto apply(CommonRep lhs, CommonRep rhs) - -> policy::value_decision { + -> policy::value::decision { return details::compare_equal(lhs, rhs); } }; @@ -570,7 +569,7 @@ struct op_binding { static constexpr bool enabled = true; static constexpr auto apply(CommonRep lhs, CommonRep rhs) - -> policy::value_decision { + -> policy::value::decision { return details::compare_equal(lhs, rhs); } }; @@ -580,7 +579,7 @@ struct op_binding { static constexpr bool enabled = true; static constexpr auto apply(CommonRep lhs, CommonRep rhs) - -> policy::value_decision { + -> policy::value::decision { return details::compare_equal(lhs, rhs); } }; @@ -590,7 +589,7 @@ struct op_binding { static constexpr bool enabled = true; static constexpr auto apply(CommonRep lhs, CommonRep rhs) - -> policy::value_decision { + -> policy::value::decision { return details::compare_not_equal(lhs, rhs); } }; @@ -600,7 +599,7 @@ struct op_binding { static constexpr bool enabled = true; static constexpr auto apply(CommonRep lhs, CommonRep rhs) - -> policy::value_decision { + -> policy::value::decision { return details::compare_not_equal(lhs, rhs); } }; @@ -610,7 +609,7 @@ struct op_binding { static constexpr bool enabled = true; static constexpr auto apply(CommonRep lhs, CommonRep rhs) - -> policy::value_decision { + -> policy::value::decision { return details::compare_not_equal(lhs, rhs); } }; diff --git a/src/operations/dispatcher.cppm b/src/operations/dispatcher.cppm index 68186b0..1cc5b15 100644 --- a/src/operations/dispatcher.cppm +++ b/src/operations/dispatcher.cppm @@ -22,15 +22,13 @@ concept primitive_instance = requires { }; template + typename ErrorPayload = policy::error::kind> 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::traits::primitive_traits; + using rhs_traits = primitives::traits::primitive_traits; using lhs_value_type = lhs_traits::value_type; using rhs_value_type = rhs_traits::value_type; @@ -43,24 +41,24 @@ struct dispatcher_meta { using error_policy = lhs_traits::error_policy; using concurrency_policy = lhs_traits::concurrency_policy; - using common_rep = policy::type_handler::common_rep; + using common_rep = + policy::type::handler::common_rep; static constexpr bool type_ready = - policy::type_handler_available; + policy::type::handler_available; static constexpr bool concurrency_ready = - policy::concurrency_handler_available; + policy::concurrency::handler_available; static constexpr bool value_ready = - policy::value_handler_available; + policy::value::handler_available; static constexpr bool error_ready = - policy::error_handler_available; + policy::error::handler_available; }; template + typename ErrorPayload = policy::error::kind> using dispatch_result_t = std::expected< typename dispatcher_meta::common_rep, ErrorPayload>; @@ -69,24 +67,24 @@ using dispatch_result_t = std::expected< // chain (concurrency -> value -> error). The value/error execution details are // intentionally hardcoded for now and will migrate to handler-driven calls. template + typename ErrorPayload = policy::error::kind> constexpr auto dispatch(Lhs const &lhs, Rhs const &rhs) -> dispatch_result_t { using meta = dispatcher_meta; using common_rep = meta::common_rep; using concurrency_handler_t = - policy::concurrency_handler; + policy::concurrency::handler; using value_handler_t = - policy::value_handler; + policy::value::handler; static_assert( meta::type_ready, "Missing type_handler specialization for this operation/policy"); - static_assert(policy::type_handler::allowed, + static_assert(policy::type::handler::allowed, "Type policy rejected this operation for the given operands"); static_assert(op_capability_valid_v, "Operation must declare a non-none capability_mask"); @@ -105,33 +103,33 @@ constexpr auto dispatch(Lhs const &lhs, Rhs const &rhs) invoker::inject_concurrency(); // Runtime stage 2: value path. - auto const lhs_rep_raw = underlying::traits< - typename meta::lhs_value_type>::to_rep(lhs.value()); - auto const rhs_rep_raw = underlying::traits< - typename meta::rhs_value_type>::to_rep(rhs.value()); - - if (!underlying::traits< - typename meta::lhs_value_type>::is_valid_rep(lhs_rep_raw) || - !underlying::traits< - typename meta::rhs_value_type>::is_valid_rep(rhs_rep_raw)) { - policy::error_request request{}; - request.kind = policy::runtime_error_kind::domain_error; + auto const lhs_rep_raw = + underlying::traits::to_rep(lhs.value()); + auto const rhs_rep_raw = + underlying::traits::to_rep(rhs.value()); + + if (!underlying::traits::is_valid_rep( + lhs_rep_raw) || + !underlying::traits::is_valid_rep( + rhs_rep_raw)) { + policy::error::request request{}; + request.kind = policy::error::kind::domain_error; request.reason = "invalid underlying representation"; return invoker::resolve_error(request); } - auto const lhs_value_normalized = underlying::traits< - typename meta::lhs_value_type>::from_rep(lhs_rep_raw); - auto const rhs_value_normalized = underlying::traits< - typename meta::rhs_value_type>::from_rep(rhs_rep_raw); + auto const lhs_value_normalized = + underlying::traits::from_rep(lhs_rep_raw); + auto const rhs_value_normalized = + underlying::traits::from_rep(rhs_rep_raw); auto const lhs_common = static_cast( - underlying::traits< - typename meta::lhs_value_type>::to_rep(lhs_value_normalized)); + underlying::traits::to_rep( + lhs_value_normalized)); auto const rhs_common = static_cast( - underlying::traits< - typename meta::rhs_value_type>::to_rep(rhs_value_normalized)); + underlying::traits::to_rep( + rhs_value_normalized)); auto const decision = invoker::run_value void { } // namespace details template -constexpr auto inject_concurrency() -> policy::concurrency_injection { +constexpr auto inject_concurrency() -> policy::concurrency::injection { static_cast(sizeof(CommonRep)); return ConcurrencyHandler::inject(); @@ -39,8 +39,8 @@ constexpr auto inject_concurrency() -> policy::concurrency_injection { template constexpr auto run_value(CommonRep lhs, CommonRep rhs, - policy::concurrency_injection const &injection) - -> policy::value_decision { + policy::concurrency::injection const &injection) + -> policy::value::decision { static_cast(sizeof(ErrorPayload)); static_assert( @@ -59,10 +59,10 @@ constexpr auto run_value(CommonRep lhs, CommonRep rhs, template -constexpr auto resolve_error(policy::error_request const &request) +constexpr auto resolve_error(policy::error::request const &request) -> std::expected { using handler_t = - policy::error_handler; + policy::error::handler; return handler_t::resolve(request); } diff --git a/src/operations/operators.cppm b/src/operations/operators.cppm index 8f90c5a..9ecb6e0 100644 --- a/src/operations/operators.cppm +++ b/src/operations/operators.cppm @@ -14,7 +14,7 @@ import mcpplibs.primitives.policy.handler; export namespace mcpplibs::primitives::operations { template + typename ErrorPayload = policy::error::kind> using primitive_dispatch_result_t = std::expected< typename mcpplibs::primitives::traits::make_primitive_t< typename dispatcher_meta::common_rep, @@ -22,7 +22,7 @@ using primitive_dispatch_result_t = std::expected< ErrorPayload>; template + typename ErrorPayload = policy::error::kind> constexpr auto apply(Lhs const &lhs, Rhs const &rhs) -> primitive_dispatch_result_t { using result_primitive = @@ -38,42 +38,42 @@ constexpr auto apply(Lhs const &lhs, Rhs const &rhs) } template + typename ErrorPayload = policy::error::kind> constexpr auto add(Lhs const &lhs, Rhs const &rhs) -> primitive_dispatch_result_t { return apply(lhs, rhs); } template + typename ErrorPayload = policy::error::kind> constexpr auto sub(Lhs const &lhs, Rhs const &rhs) -> primitive_dispatch_result_t { return apply(lhs, rhs); } template + typename ErrorPayload = policy::error::kind> constexpr auto mul(Lhs const &lhs, Rhs const &rhs) -> primitive_dispatch_result_t { return apply(lhs, rhs); } template + typename ErrorPayload = policy::error::kind> constexpr auto div(Lhs const &lhs, Rhs const &rhs) -> primitive_dispatch_result_t { return apply(lhs, rhs); } template + typename ErrorPayload = policy::error::kind> constexpr auto equal(Lhs const &lhs, Rhs const &rhs) -> primitive_dispatch_result_t { return apply(lhs, rhs); } template + typename ErrorPayload = policy::error::kind> constexpr auto not_equal(Lhs const &lhs, Rhs const &rhs) -> primitive_dispatch_result_t { return apply(lhs, rhs); diff --git a/src/policy/handler.cppm b/src/policy/handler.cppm index 3cc8a65..5520224 100644 --- a/src/policy/handler.cppm +++ b/src/policy/handler.cppm @@ -11,8 +11,10 @@ import mcpplibs.primitives.operations.traits; export namespace mcpplibs::primitives::policy { +namespace error { + // Unified runtime error kind carried across the policy chain. -enum class runtime_error_kind : unsigned char { +enum class kind : unsigned char { none = 0, invalid_type_combination, overflow, @@ -22,19 +24,65 @@ enum class runtime_error_kind : unsigned char { unspecified, }; -template struct error_request { - runtime_error_kind kind = runtime_error_kind::none; +template struct request { + kind kind = kind::none; std::string_view reason{}; std::optional lhs_value{}; std::optional rhs_value{}; std::optional fallback_value{}; }; -struct concurrency_injection { +template +struct handler { + static constexpr bool enabled = false; + using request_type = request; + using result_type = std::expected; + + static constexpr auto resolve(request_type const &) -> result_type { + return std::unexpected(ErrorPayload{}); + } +}; + +template +concept handler_available = requires { + requires error_policy; + requires operations::operation; + requires handler::enabled; +}; + +} // namespace error + +namespace concurrency { + +struct injection { bool fence_before = false; bool fence_after = false; }; +template +struct handler { + static constexpr bool enabled = false; + using injection_type = injection; + using result_type = std::expected; + + static constexpr auto inject() noexcept -> injection_type { return {}; } +}; + +template +concept handler_available = requires { + requires concurrency_policy; + requires operations::operation; + requires handler::enabled; +}; + +} // namespace concurrency + +namespace type { + template struct operation_context { using op_tag = OpTag; @@ -43,15 +91,9 @@ struct operation_context { using common_rep = CommonRep; }; -template struct value_decision { - bool has_value = false; - CommonRep value{}; - error_request error{}; -}; - // Compile-time type negotiation protocol. template -struct type_handler { +struct handler { static constexpr bool enabled = false; static constexpr bool allowed = false; static constexpr unsigned diagnostic_id = 0; @@ -59,42 +101,33 @@ struct type_handler { }; template -concept type_handler_available = requires { +concept handler_available = requires { requires type_policy; requires operations::operation; - requires type_handler::enabled; + requires handler::enabled; }; -// Runtime concurrency wrapper protocol. -template -struct concurrency_handler { - static constexpr bool enabled = false; - using injection_type = concurrency_injection; - using result_type = std::expected; +} // namespace type - static constexpr auto inject() noexcept -> injection_type { return {}; } -}; +namespace value { -template -concept concurrency_handler_available = requires { - requires concurrency_policy; - requires operations::operation; - requires concurrency_handler::enabled; +template struct decision { + bool has_value = false; + CommonRep value{}; + error::request error{}; }; // Runtime value-check protocol. template -struct value_handler { +struct handler { static constexpr bool enabled = false; static constexpr bool may_adjust_value = false; - using decision_type = value_decision; + using decision_type = decision; using result_type = std::expected; static constexpr auto finalize(decision_type decision, - concurrency_injection const &) + concurrency::injection const &) -> decision_type { return decision; } @@ -102,31 +135,12 @@ struct value_handler { template -concept value_handler_available = requires { +concept handler_available = requires { requires value_policy; requires operations::operation; - requires value_handler::enabled; -}; - -// Runtime error-resolution protocol. -template -struct error_handler { - static constexpr bool enabled = false; - using request_type = error_request; - using result_type = std::expected; - - static constexpr auto resolve(request_type const &) -> result_type { - return std::unexpected(ErrorPayload{}); - } + requires handler::enabled; }; -template -concept error_handler_available = requires { - requires error_policy; - requires operations::operation; - requires error_handler::enabled; -}; +} // namespace value } // namespace mcpplibs::primitives::policy diff --git a/src/policy/impl.cppm b/src/policy/impl.cppm index fc636e2..7c63b19 100644 --- a/src/policy/impl.cppm +++ b/src/policy/impl.cppm @@ -120,7 +120,7 @@ inline constexpr bool rejects_arithmetic_for_boolean_or_character_v = // Default protocol specializations. template -struct type_handler { +struct type::handler { static constexpr bool enabled = true; static constexpr bool allowed = std::same_as && @@ -135,7 +135,7 @@ struct type_handler { }; template -struct type_handler { +struct type::handler { static constexpr bool enabled = true; static constexpr bool allowed = std::is_arithmetic_v && std::is_arithmetic_v && @@ -151,7 +151,7 @@ struct type_handler { }; template -struct type_handler { +struct type::handler { static constexpr bool enabled = true; static constexpr bool allowed = !details::rejects_arithmetic_for_boolean_or_character_v { template -struct concurrency_handler { +struct concurrency::handler { static constexpr bool enabled = true; static constexpr bool requires_external_sync = false; - using injection_type = concurrency_injection; + using injection_type = concurrency::injection; using result_type = std::expected; static constexpr auto inject() noexcept -> injection_type { @@ -175,10 +175,10 @@ struct concurrency_handler { template -struct concurrency_handler { +struct concurrency::handler { static constexpr bool enabled = true; static constexpr bool requires_external_sync = true; - using injection_type = concurrency_injection; + using injection_type = concurrency::injection; using result_type = std::expected; static constexpr auto inject() noexcept -> injection_type { @@ -191,14 +191,14 @@ struct concurrency_handler { template -struct value_handler { +struct value::handler { static constexpr bool enabled = true; static constexpr bool may_adjust_value = false; - using decision_type = value_decision; + using decision_type = value::decision; using result_type = std::expected; static constexpr auto finalize(decision_type decision, - concurrency_injection const &) noexcept + concurrency::injection const &) noexcept -> decision_type { return decision; } @@ -206,14 +206,14 @@ struct value_handler { template -struct value_handler { +struct value::handler { static constexpr bool enabled = true; static constexpr bool may_adjust_value = false; - using decision_type = value_decision; + using decision_type = value::decision; using result_type = std::expected; static constexpr auto finalize(decision_type decision, - concurrency_injection const &) noexcept + concurrency::injection const &) noexcept -> decision_type { return decision; } @@ -221,14 +221,14 @@ struct value_handler { template -struct value_handler { +struct value::handler { static constexpr bool enabled = true; static constexpr bool may_adjust_value = true; - using decision_type = value_decision; + using decision_type = value::decision; using result_type = std::expected; static constexpr auto finalize(decision_type decision, - concurrency_injection const &) noexcept + concurrency::injection const &) noexcept -> decision_type { return decision; } @@ -236,8 +236,8 @@ struct value_handler { namespace details { template -constexpr auto to_error_payload(runtime_error_kind kind) -> ErrorPayload { - if constexpr (std::same_as) { +constexpr auto to_error_payload(error::kind kind) -> ErrorPayload { + if constexpr (std::same_as) { return kind; } else { static_cast(kind); @@ -248,10 +248,10 @@ constexpr auto to_error_payload(runtime_error_kind kind) -> ErrorPayload { template -struct error_handler { +struct error::handler { static constexpr bool enabled = true; static constexpr bool converts_to_expected = true; - using request_type = error_request; + using request_type = error::request; using result_type = std::expected; static auto resolve(request_type const &request) -> result_type { @@ -261,10 +261,10 @@ struct error_handler { template -struct error_handler { +struct error::handler { static constexpr bool enabled = true; static constexpr bool converts_to_expected = true; - using request_type = error_request; + using request_type = error::request; using result_type = std::expected; static constexpr auto resolve(request_type const &request) -> result_type { @@ -275,10 +275,10 @@ struct error_handler { template -struct error_handler { +struct error::handler { static constexpr bool enabled = true; static constexpr bool converts_to_expected = false; - using request_type = error_request; + using request_type = error::request; using result_type = std::expected; [[noreturn]] static auto resolve(request_type const &) -> result_type { From 5d15a1a1d78dd336a79cfa8ab8907177f7c4294b Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 21:40:25 +0800 Subject: [PATCH 34/57] refactor: Update error kind references and type handler namespaces --- tests/basic/test_operations.cpp | 39 +++++++++++++++------------------ tests/basic/test_policies.cpp | 11 +++++----- tests/basic/test_templates.cpp | 2 +- 3 files changed, 24 insertions(+), 28 deletions(-) diff --git a/tests/basic/test_operations.cpp b/tests/basic/test_operations.cpp index 035690b..c3be456 100644 --- a/tests/basic/test_operations.cpp +++ b/tests/basic/test_operations.cpp @@ -31,7 +31,7 @@ TEST(OperationsTest, DivisionByZeroReturnsError) { auto const result = operations::div(lhs, rhs); ASSERT_FALSE(result.has_value()); - EXPECT_EQ(result.error(), policy::runtime_error_kind::divide_by_zero); + EXPECT_EQ(result.error(), policy::error::kind::divide_by_zero); } TEST(OperationsTest, SaturatingAdditionClampsUnsignedOverflow) { @@ -56,7 +56,7 @@ TEST(OperationsTest, CheckedAdditionReportsUnsignedOverflow) { auto const result = operations::add(lhs, rhs); ASSERT_FALSE(result.has_value()); - EXPECT_EQ(result.error(), policy::runtime_error_kind::overflow); + EXPECT_EQ(result.error(), policy::error::kind::overflow); } TEST(OperationsTest, UncheckedAdditionWrapsUnsignedOverflow) { @@ -151,11 +151,10 @@ TEST(OperationsTest, StrictTypeRejectsMixedTypesAtCompileTime) { policy::expected_error>; using strict_handler = - policy::type_handler; - using strict_meta = - operations::dispatcher_meta; + policy::type::handler; + using strict_meta = operations::dispatcher_meta; static_assert(strict_handler::enabled); static_assert(!strict_handler::allowed); @@ -180,11 +179,10 @@ TEST(OperationsTest, StrictTypeAllowsSameTypeAtRuntime) { TEST(OperationsTest, BoolUnderlyingRejectsArithmeticOperationsAtCompileTime) { using value_t = primitive; - using bool_handler = policy::type_handler; - using bool_meta = - operations::dispatcher_meta; + using bool_handler = policy::type::handler; + using bool_meta = operations::dispatcher_meta; static_assert(bool_handler::enabled); static_assert(!bool_handler::allowed); @@ -196,11 +194,10 @@ TEST(OperationsTest, BoolUnderlyingRejectsArithmeticOperationsAtCompileTime) { TEST(OperationsTest, CharUnderlyingRejectsArithmeticEvenWithTransparentType) { using value_t = primitive; - using char_handler = policy::type_handler; - using char_meta = - operations::dispatcher_meta; + using char_handler = policy::type::handler; + using char_meta = operations::dispatcher_meta; static_assert(char_handler::enabled); static_assert(!char_handler::allowed); @@ -211,11 +208,11 @@ TEST(OperationsTest, CharUnderlyingRejectsArithmeticEvenWithTransparentType) { TEST(OperationsTest, SignedAndUnsignedCharRejectArithmeticAtCompileTime) { using signed_handler = - policy::type_handler; + policy::type::handler; using unsigned_handler = - policy::type_handler; + policy::type::handler; static_assert(signed_handler::enabled); static_assert(!signed_handler::allowed); diff --git a/tests/basic/test_policies.cpp b/tests/basic/test_policies.cpp index 6862da6..6793fac 100644 --- a/tests/basic/test_policies.cpp +++ b/tests/basic/test_policies.cpp @@ -8,8 +8,7 @@ namespace { struct NullCapabilityProbe {}; } // namespace -template <> -struct operations::traits { +template <> struct operations::traits { using op_tag = NullCapabilityProbe; static constexpr bool enabled = true; @@ -61,11 +60,11 @@ TEST(PolicyTraitsTest, BuiltinPoliciesHaveCategories) { TEST(PolicyConcurrencyTest, AtomicInjectsFences) { using atomic_handler = - policy::concurrency_handler; + policy::concurrency::handler; using single_handler = - policy::concurrency_handler; + policy::concurrency::handler; auto const atomic_injection = atomic_handler::inject(); auto const single_injection = single_handler::inject(); diff --git a/tests/basic/test_templates.cpp b/tests/basic/test_templates.cpp index a4f164e..37240ff 100644 --- a/tests/basic/test_templates.cpp +++ b/tests/basic/test_templates.cpp @@ -329,7 +329,7 @@ TEST(PrimitiveTraitsTest, InvalidUnderlyingRepIsRejectedByDispatcher) { ASSERT_FALSE(result.has_value()); EXPECT_EQ(result.error(), - mcpplibs::primitives::policy::runtime_error_kind::domain_error); + mcpplibs::primitives::policy::error::kind::domain_error); } TEST(PrimitiveTraitsTest, From a36b970ef9543762342579b964476730c0d48b6c Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 21:40:34 +0800 Subject: [PATCH 35/57] docs: Update README with new policy namespace structure and error kind references --- README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3cb412f..80d1ea2 100644 --- a/README.md +++ b/README.md @@ -34,9 +34,19 @@ auto c = a + b; // primitive primitive x{std::numeric_limits::max()}; primitive y{1}; -auto maybe = x + y; // std::expected, std::overflow_error> +auto maybe = x + y; // std::expected, policy::error::kind> ``` +## Policy 协议命名空间 + +自定义 policy 时,协议入口已按职责拆分到子命名空间: + +- `policy::type::handler` / `policy::type::handler_available` +- `policy::concurrency::handler` / `policy::concurrency::injection` +- `policy::value::handler` / `policy::value::decision` +- `policy::error::handler` / `policy::error::request` / `policy::error::kind` + + ## 项目结构 ``` From 38650cba8ced9e2301fb65c98e56be01597542d2 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 22:00:37 +0800 Subject: [PATCH 36/57] refactor: Update policy namespaces and default values --- src/operations/binding.cppm | 36 ++++++------ src/policy/impl.cppm | 111 ++++++++++++++++++++---------------- src/policy/utility.cppm | 17 +++--- src/primitive/traits.cppm | 23 +++----- 4 files changed, 95 insertions(+), 92 deletions(-) diff --git a/src/operations/binding.cppm b/src/operations/binding.cppm index 97c6e0d..ac01b8b 100644 --- a/src/operations/binding.cppm +++ b/src/operations/binding.cppm @@ -381,7 +381,7 @@ struct op_binding { }; template -struct op_binding { +struct op_binding { static constexpr bool enabled = true; static constexpr auto apply(CommonRep lhs, CommonRep rhs) @@ -396,7 +396,7 @@ struct op_binding { }; template -struct op_binding { +struct op_binding { static constexpr bool enabled = true; static constexpr auto apply(CommonRep lhs, CommonRep rhs) @@ -411,7 +411,7 @@ struct op_binding { }; template -struct op_binding { +struct op_binding { static constexpr bool enabled = true; static constexpr auto apply(CommonRep lhs, CommonRep rhs) @@ -426,7 +426,7 @@ struct op_binding { }; template -struct op_binding { +struct op_binding { static constexpr bool enabled = true; static constexpr auto apply(CommonRep lhs, CommonRep rhs) @@ -441,7 +441,7 @@ struct op_binding { }; template -struct op_binding { +struct op_binding { static constexpr bool enabled = true; static constexpr auto apply(CommonRep lhs, CommonRep rhs) @@ -451,7 +451,7 @@ struct op_binding { }; template -struct op_binding { +struct op_binding { static constexpr bool enabled = true; static constexpr auto apply(CommonRep lhs, CommonRep rhs) @@ -461,7 +461,7 @@ struct op_binding { }; template -struct op_binding { +struct op_binding { static constexpr bool enabled = true; static constexpr auto apply(CommonRep lhs, CommonRep rhs) @@ -471,7 +471,7 @@ struct op_binding { }; template -struct op_binding { +struct op_binding { static constexpr bool enabled = true; static constexpr auto apply(CommonRep lhs, CommonRep rhs) @@ -481,7 +481,7 @@ struct op_binding { }; template -struct op_binding { +struct op_binding { static constexpr bool enabled = true; static constexpr auto apply(CommonRep lhs, CommonRep rhs) @@ -498,7 +498,7 @@ struct op_binding { }; template -struct op_binding { +struct op_binding { static constexpr bool enabled = true; static constexpr auto apply(CommonRep lhs, CommonRep rhs) @@ -515,7 +515,7 @@ struct op_binding { }; template -struct op_binding { +struct op_binding { static constexpr bool enabled = true; static constexpr auto apply(CommonRep lhs, CommonRep rhs) @@ -532,7 +532,7 @@ struct op_binding { }; template -struct op_binding { +struct op_binding { static constexpr bool enabled = true; static constexpr auto apply(CommonRep lhs, CommonRep rhs) @@ -555,7 +555,7 @@ struct op_binding { }; template -struct op_binding { +struct op_binding { static constexpr bool enabled = true; static constexpr auto apply(CommonRep lhs, CommonRep rhs) @@ -565,7 +565,7 @@ struct op_binding { }; template -struct op_binding { +struct op_binding { static constexpr bool enabled = true; static constexpr auto apply(CommonRep lhs, CommonRep rhs) @@ -575,7 +575,7 @@ struct op_binding { }; template -struct op_binding { +struct op_binding { static constexpr bool enabled = true; static constexpr auto apply(CommonRep lhs, CommonRep rhs) @@ -585,7 +585,7 @@ struct op_binding { }; template -struct op_binding { +struct op_binding { static constexpr bool enabled = true; static constexpr auto apply(CommonRep lhs, CommonRep rhs) @@ -595,7 +595,7 @@ struct op_binding { }; template -struct op_binding { +struct op_binding { static constexpr bool enabled = true; static constexpr auto apply(CommonRep lhs, CommonRep rhs) @@ -605,7 +605,7 @@ struct op_binding { }; template -struct op_binding { +struct op_binding { static constexpr bool enabled = true; static constexpr auto apply(CommonRep lhs, CommonRep rhs) diff --git a/src/policy/impl.cppm b/src/policy/impl.cppm index 7c63b19..ab3f9e2 100644 --- a/src/policy/impl.cppm +++ b/src/policy/impl.cppm @@ -16,91 +16,101 @@ import mcpplibs.primitives.underlying.traits; export namespace mcpplibs::primitives::policy { -struct checked_value {}; -struct unchecked_value {}; -struct saturating_value {}; - -struct strict_type {}; -struct category_compatible_type {}; -struct transparent_type {}; - -struct throw_error {}; -struct expected_error {}; -struct terminate_error {}; - -struct single_thread {}; +namespace value { +struct checked {}; +struct unchecked {}; +struct saturating {}; +} // namespace value + +namespace type { +struct strict {}; +struct compatible {}; +struct transparent {}; +} // namespace type + +namespace error { +struct throwing {}; +struct expected {}; +struct terminate {}; +} // namespace error + +namespace concurrency { +struct none {}; struct atomic {}; +} // namespace concurrency -template <> struct traits { - using policy_type = checked_value; +template <> struct traits { + using policy_type = value::checked; static constexpr bool enabled = true; static constexpr auto kind = category::value; }; -template <> struct traits { - using policy_type = unchecked_value; +template <> struct traits { + using policy_type = value::unchecked; static constexpr bool enabled = true; static constexpr auto kind = category::value; }; -template <> struct traits { - using policy_type = saturating_value; +template <> struct traits { + using policy_type = value::saturating; static constexpr bool enabled = true; static constexpr auto kind = category::value; }; -template <> struct traits { - using policy_type = strict_type; +template <> struct traits { + using policy_type = type::strict; static constexpr bool enabled = true; static constexpr auto kind = category::type; }; -template <> struct traits { - using policy_type = category_compatible_type; +template <> struct traits { + using policy_type = type::compatible; static constexpr bool enabled = true; static constexpr auto kind = category::type; }; -template <> struct traits { - using policy_type = transparent_type; +template <> struct traits { + using policy_type = type::transparent; static constexpr bool enabled = true; static constexpr auto kind = category::type; }; -template <> struct traits { - using policy_type = throw_error; +template <> struct traits { + using policy_type = error::throwing; static constexpr bool enabled = true; static constexpr auto kind = category::error; }; -template <> struct traits { - using policy_type = expected_error; +template <> struct traits { + using policy_type = error::expected; static constexpr bool enabled = true; static constexpr auto kind = category::error; }; -template <> struct traits { - using policy_type = terminate_error; +template <> struct traits { + using policy_type = error::terminate; static constexpr bool enabled = true; static constexpr auto kind = category::error; }; -template <> struct traits { - using policy_type = single_thread; +template <> struct traits { + using policy_type = concurrency::none; static constexpr bool enabled = true; static constexpr auto kind = category::concurrency; }; -template <> struct traits { - using policy_type = atomic; +template <> struct traits { + using policy_type = concurrency::atomic; static constexpr bool enabled = true; static constexpr auto kind = category::concurrency; }; -using default_value = checked_value; -using default_type = strict_type; -using default_error = throw_error; -using default_concurrency = single_thread; +namespace defaults { +using value = policy::value::checked; +using type = policy::type::strict; +using error = policy::error::throwing; +using concurrency = policy::concurrency::none; +} // namespace defaults namespace details { @@ -120,7 +130,7 @@ inline constexpr bool rejects_arithmetic_for_boolean_or_character_v = // Default protocol specializations. template -struct type::handler { +struct type::handler { static constexpr bool enabled = true; static constexpr bool allowed = std::same_as && @@ -135,7 +145,7 @@ struct type::handler { }; template -struct type::handler { +struct type::handler { static constexpr bool enabled = true; static constexpr bool allowed = std::is_arithmetic_v && std::is_arithmetic_v && @@ -151,7 +161,7 @@ struct type::handler { }; template -struct type::handler { +struct type::handler { static constexpr bool enabled = true; static constexpr bool allowed = !details::rejects_arithmetic_for_boolean_or_character_v { template -struct concurrency::handler { +struct concurrency::handler { static constexpr bool enabled = true; static constexpr bool requires_external_sync = false; using injection_type = concurrency::injection; @@ -175,7 +185,8 @@ struct concurrency::handler { template -struct concurrency::handler { +struct concurrency::handler { static constexpr bool enabled = true; static constexpr bool requires_external_sync = true; using injection_type = concurrency::injection; @@ -191,7 +202,7 @@ struct concurrency::handler { template -struct value::handler { +struct value::handler { static constexpr bool enabled = true; static constexpr bool may_adjust_value = false; using decision_type = value::decision; @@ -206,7 +217,7 @@ struct value::handler { template -struct value::handler { +struct value::handler { static constexpr bool enabled = true; static constexpr bool may_adjust_value = false; using decision_type = value::decision; @@ -221,7 +232,7 @@ struct value::handler { template -struct value::handler { +struct value::handler { static constexpr bool enabled = true; static constexpr bool may_adjust_value = true; using decision_type = value::decision; @@ -248,7 +259,7 @@ constexpr auto to_error_payload(error::kind kind) -> ErrorPayload { template -struct error::handler { +struct error::handler { static constexpr bool enabled = true; static constexpr bool converts_to_expected = true; using request_type = error::request; @@ -261,7 +272,7 @@ struct error::handler { template -struct error::handler { +struct error::handler { static constexpr bool enabled = true; static constexpr bool converts_to_expected = true; using request_type = error::request; @@ -275,7 +286,7 @@ struct error::handler { template -struct error::handler { +struct error::handler { static constexpr bool enabled = true; static constexpr bool converts_to_expected = false; using request_type = error::request; diff --git a/src/policy/utility.cppm b/src/policy/utility.cppm index 448fa23..51de8c7 100644 --- a/src/policy/utility.cppm +++ b/src/policy/utility.cppm @@ -52,34 +52,33 @@ template struct priority_concurrency { }; template <> struct priority_value { - using type = std::tuple; + using type = std::tuple; }; template <> struct priority_type { - using type = - std::tuple; + using type = std::tuple; }; template <> struct priority_error { - using type = std::tuple; + using type = std::tuple; }; template <> struct priority_concurrency { - using type = std::tuple; + using type = std::tuple; }; template struct common_policies { using value_policy = details::pick_first_from_priority_impl< - default_value, priority_value<>::type, Ps...>::type; + defaults::value, priority_value<>::type, Ps...>::type; using type_policy = details::pick_first_from_priority_impl< - default_type, priority_type<>::type, Ps...>::type; + defaults::type, priority_type<>::type, Ps...>::type; using error_policy = details::pick_first_from_priority_impl< - default_error, priority_error<>::type, Ps...>::type; + defaults::error, priority_error<>::type, Ps...>::type; using concurrency_policy = details::pick_first_from_priority_impl< - default_concurrency, priority_concurrency<>::type, Ps...>::type; + defaults::concurrency, priority_concurrency<>::type, Ps...>::type; using type = std::tuple; diff --git a/src/primitive/traits.cppm b/src/primitive/traits.cppm index fb93e8d..29bd12c 100644 --- a/src/primitive/traits.cppm +++ b/src/primitive/traits.cppm @@ -60,20 +60,13 @@ struct resolve_policy_impl { using type = std::conditional_t< std::is_same_v, std::conditional_t< - C == policy_category::value, - policy::default_value, + C == policy_category::value, policy::defaults::value, std::conditional_t< - C == policy_category::type, - policy::default_type, - std::conditional_t< - C == policy_category::error, - policy::default_error, - policy::default_concurrency - > - > - >, - found - >; + C == policy_category::type, policy::defaults::type, + std::conditional_t>>, + found>; }; template @@ -99,8 +92,8 @@ template using make_primitive_t = make_primitive::type; using default_policies = - std::tuple; + std::tuple; template struct primitive_traits; From 40daf6d22dde0a995a1f698e9577f676efaf9a34 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 22:00:48 +0800 Subject: [PATCH 37/57] refactor: Update policy namespaces and references in tests --- tests/basic/test_operations.cpp | 74 ++++++++++++++++----------------- tests/basic/test_policies.cpp | 51 ++++++++++++----------- tests/basic/test_templates.cpp | 12 +++--- 3 files changed, 70 insertions(+), 67 deletions(-) diff --git a/tests/basic/test_operations.cpp b/tests/basic/test_operations.cpp index c3be456..6deaec9 100644 --- a/tests/basic/test_operations.cpp +++ b/tests/basic/test_operations.cpp @@ -10,8 +10,8 @@ import mcpplibs.primitives; using namespace mcpplibs::primitives; TEST(OperationsTest, AddReturnsExpectedPrimitive) { - using lhs_t = primitive; - using rhs_t = primitive; + using lhs_t = primitive; + using rhs_t = primitive; auto const lhs = lhs_t{10}; auto const rhs = rhs_t{32}; @@ -23,7 +23,7 @@ TEST(OperationsTest, AddReturnsExpectedPrimitive) { } TEST(OperationsTest, DivisionByZeroReturnsError) { - using value_t = primitive; + using value_t = primitive; auto const lhs = value_t{100}; auto const rhs = value_t{0}; @@ -35,7 +35,7 @@ TEST(OperationsTest, DivisionByZeroReturnsError) { } TEST(OperationsTest, SaturatingAdditionClampsUnsignedOverflow) { - using value_t = primitive; + using value_t = primitive; auto const lhs = value_t{static_cast(65530)}; auto const rhs = value_t{static_cast(20)}; @@ -48,7 +48,7 @@ TEST(OperationsTest, SaturatingAdditionClampsUnsignedOverflow) { TEST(OperationsTest, CheckedAdditionReportsUnsignedOverflow) { using value_t = - primitive; + primitive; auto const lhs = value_t{static_cast(65530)}; auto const rhs = value_t{static_cast(20)}; @@ -61,7 +61,7 @@ TEST(OperationsTest, CheckedAdditionReportsUnsignedOverflow) { TEST(OperationsTest, UncheckedAdditionWrapsUnsignedOverflow) { using value_t = - primitive; + primitive; auto const lhs = value_t{static_cast(65530)}; auto const rhs = value_t{static_cast(20)}; @@ -74,7 +74,7 @@ TEST(OperationsTest, UncheckedAdditionWrapsUnsignedOverflow) { TEST(OperationsTest, UncheckedDivisionUsesRawArithmeticWhenValid) { using value_t = - primitive; + primitive; auto const lhs = value_t{100}; auto const rhs = value_t{4}; @@ -86,8 +86,8 @@ TEST(OperationsTest, UncheckedDivisionUsesRawArithmeticWhenValid) { } TEST(OperationsTest, AtomicPolicyPathReturnsExpectedValue) { - using value_t = primitive; + using value_t = primitive; auto const lhs = value_t{12}; auto const rhs = value_t{30}; @@ -99,8 +99,8 @@ TEST(OperationsTest, AtomicPolicyPathReturnsExpectedValue) { } TEST(OperationsTest, AtomicPolicyConcurrentInvocationsRemainConsistent) { - using value_t = primitive; + using value_t = primitive; constexpr int kThreadCount = 8; constexpr int kIterationsPerThread = 20000; @@ -145,13 +145,13 @@ TEST(OperationsTest, AtomicPolicyConcurrentInvocationsRemainConsistent) { } TEST(OperationsTest, StrictTypeRejectsMixedTypesAtCompileTime) { - using lhs_t = primitive; - using rhs_t = primitive; + using lhs_t = primitive; + using rhs_t = primitive; using strict_handler = - policy::type::handler; using strict_meta = operations::dispatcher_meta; @@ -164,8 +164,8 @@ TEST(OperationsTest, StrictTypeRejectsMixedTypesAtCompileTime) { } TEST(OperationsTest, StrictTypeAllowsSameTypeAtRuntime) { - using value_t = primitive; + using value_t = primitive; auto const lhs = value_t{19}; auto const rhs = value_t{23}; @@ -177,9 +177,9 @@ TEST(OperationsTest, StrictTypeAllowsSameTypeAtRuntime) { } TEST(OperationsTest, BoolUnderlyingRejectsArithmeticOperationsAtCompileTime) { - using value_t = primitive; - using bool_handler = policy::type::handler; + using bool_handler = policy::type::handler; using bool_meta = operations::dispatcher_meta; @@ -192,9 +192,9 @@ TEST(OperationsTest, BoolUnderlyingRejectsArithmeticOperationsAtCompileTime) { } TEST(OperationsTest, CharUnderlyingRejectsArithmeticEvenWithTransparentType) { - using value_t = primitive; - using char_handler = policy::type::handler; + using char_handler = policy::type::handler; using char_meta = operations::dispatcher_meta; @@ -208,10 +208,10 @@ TEST(OperationsTest, CharUnderlyingRejectsArithmeticEvenWithTransparentType) { TEST(OperationsTest, SignedAndUnsignedCharRejectArithmeticAtCompileTime) { using signed_handler = - policy::type::handler; using unsigned_handler = - policy::type::handler; static_assert(signed_handler::enabled); @@ -224,8 +224,8 @@ TEST(OperationsTest, SignedAndUnsignedCharRejectArithmeticAtCompileTime) { } TEST(OperationsTest, BoolUnderlyingAllowsComparisonOperations) { - using value_t = primitive; + using value_t = primitive; auto const lhs = value_t{true}; auto const rhs = value_t{false}; @@ -240,8 +240,8 @@ TEST(OperationsTest, BoolUnderlyingAllowsComparisonOperations) { } TEST(OperationsTest, CharUnderlyingAllowsComparisonWithTransparentType) { - using value_t = primitive; + using value_t = primitive; auto const lhs = value_t{'a'}; auto const rhs = value_t{'a'}; @@ -255,7 +255,7 @@ TEST(OperationsTest, CharUnderlyingAllowsComparisonWithTransparentType) { TEST(OperationsTest, PrimitiveAliasWorksWithFrameworkOperators) { using namespace mcpplibs::primitives::types; using namespace mcpplibs::primitives::operators; - using value_t = I32; + using value_t = I32; auto const lhs = value_t{20}; auto const rhs = value_t{22}; @@ -269,7 +269,7 @@ TEST(OperationsTest, PrimitiveAliasWorksWithFrameworkOperators) { TEST(OperationsTest, PrimitiveAliasMixesWithBuiltinArithmeticExplicitly) { using namespace mcpplibs::primitives::types; using namespace mcpplibs::primitives::operators; - using value_t = I32; + using value_t = I32; static_assert(!std::is_convertible_v); @@ -286,8 +286,8 @@ TEST(OperationsTest, PrimitiveAliasMixesWithBuiltinArithmeticExplicitly) { TEST(OperationsTest, OperatorEqualDelegatesToDispatcher) { using namespace mcpplibs::primitives::operators; - using value_t = primitive; + using value_t = primitive; auto const lhs = value_t{7}; auto const rhs = value_t{7}; @@ -300,7 +300,7 @@ TEST(OperationsTest, OperatorEqualDelegatesToDispatcher) { TEST(OperationsTest, OperatorPlusDelegatesToDispatcher) { using namespace mcpplibs::primitives::operators; - using value_t = primitive; + using value_t = primitive; auto const lhs = value_t{7}; auto const rhs = value_t{8}; @@ -312,7 +312,7 @@ TEST(OperationsTest, OperatorPlusDelegatesToDispatcher) { } TEST(OperationsTest, ThrowErrorPolicyThrowsException) { - using value_t = primitive; + using value_t = primitive; auto const lhs = value_t{100}; auto const rhs = value_t{0}; @@ -321,7 +321,7 @@ TEST(OperationsTest, ThrowErrorPolicyThrowsException) { } TEST(OperationsTest, ThrowErrorPolicyExceptionHasReasonMessage) { - using value_t = primitive; + using value_t = primitive; auto const lhs = value_t{100}; auto const rhs = value_t{0}; diff --git a/tests/basic/test_policies.cpp b/tests/basic/test_policies.cpp index 6793fac..a29e614 100644 --- a/tests/basic/test_policies.cpp +++ b/tests/basic/test_policies.cpp @@ -19,52 +19,55 @@ template <> struct operations::traits { TEST(PolicyTraitsTest, BuiltinPoliciesHaveCategories) { using namespace policy; - EXPECT_TRUE((policy::traits::enabled)); - EXPECT_EQ(policy::traits::kind, + EXPECT_TRUE((policy::traits::enabled)); + EXPECT_EQ(policy::traits::kind, policy::category::value); - EXPECT_TRUE((policy::traits::enabled)); - EXPECT_EQ(policy::traits::kind, + EXPECT_TRUE((policy::traits::enabled)); + EXPECT_EQ(policy::traits::kind, policy::category::type); - EXPECT_TRUE((policy::traits::enabled)); - EXPECT_EQ(policy::traits::kind, + EXPECT_TRUE((policy::traits::enabled)); + EXPECT_EQ(policy::traits::kind, policy::category::type); - EXPECT_TRUE((policy::traits::enabled)); - EXPECT_EQ(policy::traits::kind, policy::category::error); + EXPECT_TRUE((policy::traits::enabled)); + EXPECT_EQ(policy::traits::kind, + policy::category::error); - EXPECT_TRUE((policy::traits::enabled)); - EXPECT_EQ(policy::traits::kind, + EXPECT_TRUE((policy::traits::enabled)); + EXPECT_EQ(policy::traits::kind, policy::category::error); - EXPECT_TRUE((policy::traits::enabled)); - EXPECT_EQ(policy::traits::kind, + EXPECT_TRUE((policy::traits::enabled)); + EXPECT_EQ(policy::traits::kind, policy::category::concurrency); - EXPECT_TRUE((policy::traits::enabled)); - EXPECT_EQ(policy::traits::kind, + EXPECT_TRUE((policy::traits::enabled)); + EXPECT_EQ(policy::traits::kind, policy::category::concurrency); - EXPECT_TRUE((policy::traits::enabled)); - EXPECT_EQ(policy::traits::kind, + EXPECT_TRUE((policy::traits::enabled)); + EXPECT_EQ(policy::traits::kind, policy::category::value); - EXPECT_TRUE((policy_type)); - EXPECT_TRUE((policy_type)); + EXPECT_TRUE((policy_type)); + EXPECT_TRUE((policy_type)); EXPECT_FALSE((policy_type)); - EXPECT_TRUE((std::is_same_v)); - EXPECT_TRUE((std::is_same_v)); + EXPECT_TRUE( + (std::is_same_v)); + EXPECT_TRUE((std::is_same_v)); } TEST(PolicyConcurrencyTest, AtomicInjectsFences) { using atomic_handler = - policy::concurrency::handler; - using single_handler = - policy::concurrency::handler; + using single_handler = policy::concurrency::handler; auto const atomic_injection = atomic_handler::inject(); auto const single_injection = single_handler::inject(); diff --git a/tests/basic/test_templates.cpp b/tests/basic/test_templates.cpp index 37240ff..e68c560 100644 --- a/tests/basic/test_templates.cpp +++ b/tests/basic/test_templates.cpp @@ -291,8 +291,8 @@ TEST(PrimitiveTraitsTest, CustomNumericLikeRepTypeRejectsInvalidCategory) { TEST(PrimitiveTraitsTest, CustomUnderlyingParticipatesInPrimitiveOperations) { using value_t = mcpplibs::primitives::primitive< - BigIntLike, mcpplibs::primitives::policy::checked_value, - mcpplibs::primitives::policy::expected_error>; + BigIntLike, mcpplibs::primitives::policy::value::checked, + mcpplibs::primitives::policy::error::expected>; auto const lhs = value_t{BigIntLike{40}}; auto const rhs = value_t{BigIntLike{2}}; @@ -305,8 +305,8 @@ TEST(PrimitiveTraitsTest, CustomUnderlyingParticipatesInPrimitiveOperations) { TEST(PrimitiveTraitsTest, CustomWrappedUnderlyingUsesRepBridgeForArithmetic) { using value_t = mcpplibs::primitives::primitive< - UserInteger, mcpplibs::primitives::policy::checked_value, - mcpplibs::primitives::policy::expected_error>; + UserInteger, mcpplibs::primitives::policy::value::checked, + mcpplibs::primitives::policy::error::expected>; auto const lhs = value_t{UserInteger{40}}; auto const rhs = value_t{UserInteger{2}}; @@ -319,8 +319,8 @@ TEST(PrimitiveTraitsTest, CustomWrappedUnderlyingUsesRepBridgeForArithmetic) { TEST(PrimitiveTraitsTest, InvalidUnderlyingRepIsRejectedByDispatcher) { using value_t = mcpplibs::primitives::primitive< - NonNegativeInt, mcpplibs::primitives::policy::checked_value, - mcpplibs::primitives::policy::expected_error>; + NonNegativeInt, mcpplibs::primitives::policy::value::checked, + mcpplibs::primitives::policy::error::expected>; auto const lhs = value_t{NonNegativeInt{-1}}; auto const rhs = value_t{NonNegativeInt{2}}; From e97ebb69f13fd3e074f8de50b9e7b3f7d29d547a Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 22:01:00 +0800 Subject: [PATCH 38/57] docs: Update README with new policy namespaces and default values --- README.md | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 80d1ea2..119a66d 100644 --- a/README.md +++ b/README.md @@ -19,8 +19,8 @@ 该库在 `primitive` 类型上重载了常见的 C++ 算术、位运算和一元运算符。算术行为受策略(policy)控制: -- 值策略(`checked_value` / `saturating_value` / `unchecked_value`)决定溢出行为; -- 错误策略(`throw_error` / `expected_error` / `terminate_error`)决定在 `checked_value` 且发生错误时的处理方式。 +- 值策略(`policy::value::checked` / `policy::value::saturating` / `policy::value::unchecked`)决定溢出行为; +- 错误策略(`policy::error::throwing` / `policy::error::expected` / `policy::error::terminate`)决定在 `policy::value::checked` 且发生错误时的处理方式。 示例: @@ -32,9 +32,9 @@ using namespace mcpplibs::primitives::policy; primitive a{1}, b{2}; auto c = a + b; // primitive -primitive x{std::numeric_limits::max()}; -primitive y{1}; -auto maybe = x + y; // std::expected, policy::error::kind> +primitive x{std::numeric_limits::max()}; +primitive y{1}; +auto maybe = x + y; // std::expected, policy::error::kind> ``` ## Policy 协议命名空间 @@ -46,6 +46,20 @@ auto maybe = x + y; // std::expected, policy::err - `policy::value::handler` / `policy::value::decision` - `policy::error::handler` / `policy::error::request` / `policy::error::kind` +预设 policy 标签也按类别归档: + +- `policy::value::{checked, unchecked, saturating}` +- `policy::type::{strict, compatible, transparent}` +- `policy::error::{throwing, expected, terminate}` +- `policy::concurrency::{none, atomic}` + +默认策略位于 `policy::defaults`: + +- `policy::defaults::value` +- `policy::defaults::type` +- `policy::defaults::error` +- `policy::defaults::concurrency` + ## 项目结构 From c0c7f4256779de99495872740bd7bc86a74d9adf Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 22:03:26 +0800 Subject: [PATCH 39/57] refactor: Simplify default policy namespace references --- src/policy/impl.cppm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/policy/impl.cppm b/src/policy/impl.cppm index ab3f9e2..dc6a360 100644 --- a/src/policy/impl.cppm +++ b/src/policy/impl.cppm @@ -106,10 +106,10 @@ template <> struct traits { }; namespace defaults { -using value = policy::value::checked; -using type = policy::type::strict; -using error = policy::error::throwing; -using concurrency = policy::concurrency::none; +using value = value::checked; +using type = type::strict; +using error = error::throwing; +using concurrency = concurrency::none; } // namespace defaults namespace details { From d6347de7ed90f2cbbd2b1b8ee67ecc4e4f89d7f9 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 22:28:10 +0800 Subject: [PATCH 40/57] refactor: Enhance and separate handler protocol and availability checks --- src/operations/dispatcher.cppm | 20 ++++++ src/operations/invoker.cppm | 10 +++ src/policy/handler.cppm | 113 +++++++++++++++++++++++++++++---- 3 files changed, 131 insertions(+), 12 deletions(-) diff --git a/src/operations/dispatcher.cppm b/src/operations/dispatcher.cppm index 1cc5b15..2a76559 100644 --- a/src/operations/dispatcher.cppm +++ b/src/operations/dispatcher.cppm @@ -46,15 +46,26 @@ struct dispatcher_meta { static constexpr bool type_ready = policy::type::handler_available; + static constexpr bool type_protocol_ready = + policy::type::handler_protocol; static constexpr bool concurrency_ready = policy::concurrency::handler_available; + static constexpr bool concurrency_protocol_ready = + policy::concurrency::handler_protocol; static constexpr bool value_ready = policy::value::handler_available; + static constexpr bool value_protocol_ready = + policy::value::handler_protocol; static constexpr bool error_ready = policy::error::handler_available; + static constexpr bool error_protocol_ready = + policy::error::handler_protocol; }; template ::allowed, @@ -91,12 +104,19 @@ constexpr auto dispatch(Lhs const &lhs, Rhs const &rhs) static_assert( meta::concurrency_ready, "Missing concurrency_handler specialization for this operation/policy"); + static_assert( + meta::concurrency_protocol_ready, + "concurrency::handler does not satisfy the policy protocol contract"); static_assert( meta::value_ready, "Missing value_handler specialization for this operation/policy"); + static_assert(meta::value_protocol_ready, + "value::handler does not satisfy the policy protocol contract"); static_assert( meta::error_ready, "Missing error_handler specialization for this operation/policy"); + static_assert(meta::error_protocol_ready, + "error::handler does not satisfy the policy protocol contract"); // Runtime stage 1: concurrency context injection. auto const injection = diff --git a/src/operations/invoker.cppm b/src/operations/invoker.cppm index 022754f..923dc1e 100644 --- a/src/operations/invoker.cppm +++ b/src/operations/invoker.cppm @@ -32,6 +32,11 @@ constexpr auto apply_runtime_fence(bool enabled) noexcept -> void { template constexpr auto inject_concurrency() -> policy::concurrency::injection { static_cast(sizeof(CommonRep)); + static_assert(ConcurrencyHandler::enabled, + "Selected concurrency handler is not enabled"); + static_assert(std::same_as, + "concurrency handler must use policy::concurrency::injection"); return ConcurrencyHandler::inject(); } @@ -42,6 +47,11 @@ constexpr auto run_value(CommonRep lhs, CommonRep rhs, policy::concurrency::injection const &injection) -> policy::value::decision { static_cast(sizeof(ErrorPayload)); + static_assert(ValueHandler::enabled, "Selected value handler is not enabled"); + static_assert( + std::same_as>, + "value handler decision_type must match policy::value::decision"); static_assert( binding::op_binding_available, diff --git a/src/policy/handler.cppm b/src/policy/handler.cppm index 5520224..93fd197 100644 --- a/src/policy/handler.cppm +++ b/src/policy/handler.cppm @@ -1,8 +1,11 @@ module; +#include #include #include #include +#include +#include export module mcpplibs.primitives.policy.handler; @@ -46,11 +49,33 @@ struct handler { template -concept handler_available = requires { - requires error_policy; - requires operations::operation; - requires handler::enabled; -}; +concept handler_protocol = + error_policy && operations::operation && + (!std::same_as) && requires { + typename handler::request_type; + typename handler::result_type; + { + handler::enabled + } -> std::convertible_to; + requires handler::enabled; + requires std::same_as::request_type, + request>; + requires std::same_as< + typename handler::result_type, + std::expected>; + { + handler::resolve( + std::declval const &>()) + } -> std::same_as::result_type>; + }; + +template +concept handler_available = + handler_protocol && + handler::enabled; } // namespace error @@ -73,10 +98,32 @@ struct handler { template -concept handler_available = requires { +concept handler_protocol = requires { requires concurrency_policy; requires operations::operation; + typename handler::injection_type; + typename handler::result_type; + { + handler::enabled + } -> std::convertible_to; + requires handler::enabled; + requires std::same_as< + typename handler::injection_type, + injection>; + requires std::same_as< + typename handler::result_type, + std::expected>; + { + handler::inject() + } noexcept -> std::same_as< + typename handler::injection_type>; +}; + +template +concept handler_available = requires { requires handler::enabled; + requires handler_protocol; }; } // namespace concurrency @@ -101,12 +148,28 @@ struct handler { }; template -concept handler_available = requires { +concept handler_protocol = requires { requires type_policy; requires operations::operation; + typename handler::common_rep; + { + handler::enabled + } -> std::convertible_to; + { + handler::allowed + } -> std::convertible_to; + { + handler::diagnostic_id + } -> std::convertible_to; requires handler::enabled; }; +template +concept handler_available = requires { + requires handler::enabled; + requires handler_protocol; +}; + } // namespace type namespace value { @@ -135,11 +198,37 @@ struct handler { template -concept handler_available = requires { - requires value_policy; - requires operations::operation; - requires handler::enabled; -}; +concept handler_protocol = + value_policy && operations::operation && + (!std::same_as) && requires { + typename handler::decision_type; + typename handler::result_type; + { + handler::enabled + } -> std::convertible_to; + { + handler::may_adjust_value + } -> std::convertible_to; + requires handler::enabled; + requires std::same_as::decision_type, + decision>; + requires std::same_as< + typename handler::result_type, + std::expected>; + { + handler::finalize( + std::declval>(), + std::declval()) + } -> std::same_as::decision_type>; + }; + +template +concept handler_available = + handler_protocol && + handler::enabled; } // namespace value From 03c5bcccb7bbc1a41f17e9977a05fd8076597270 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 22:28:18 +0800 Subject: [PATCH 41/57] test: Add tests for builtin handlers satisfying protocol concepts --- tests/basic/test_policies.cpp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/basic/test_policies.cpp b/tests/basic/test_policies.cpp index a29e614..06bed67 100644 --- a/tests/basic/test_policies.cpp +++ b/tests/basic/test_policies.cpp @@ -78,6 +78,32 @@ TEST(PolicyConcurrencyTest, AtomicInjectsFences) { EXPECT_FALSE(single_injection.fence_after); } +TEST(PolicyProtocolTest, BuiltinHandlersSatisfyProtocolConcepts) { + static_assert(policy::type::handler_protocol); + static_assert(policy::concurrency::handler_protocol); + static_assert(policy::value::handler_protocol); + static_assert(policy::error::handler_protocol); + + EXPECT_TRUE((policy::type::handler_protocol)); + EXPECT_TRUE((policy::concurrency::handler_protocol)); + EXPECT_TRUE((policy::value::handler_protocol)); + EXPECT_TRUE((policy::error::handler_protocol)); +} + TEST(OperationTraitsTest, BuiltinArithmeticOperationsExposeCapability) { EXPECT_TRUE( (operations::op_has_capability_v Date: Tue, 17 Mar 2026 22:30:05 +0800 Subject: [PATCH 42/57] refactor: Replace non UTF-8 characters --- src/primitive/traits.cppm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/primitive/traits.cppm b/src/primitive/traits.cppm index 29bd12c..4328827 100644 --- a/src/primitive/traits.cppm +++ b/src/primitive/traits.cppm @@ -8,7 +8,7 @@ import mcpplibs.primitives.primitive.impl; import mcpplibs.primitives.policy; import mcpplibs.primitives.underlying; -// Internal implementation details — not exported. +// Internal implementation details - not exported. namespace mcpplibs::primitives::traits::details { using policy_category = policy::category; From 158defd7ca2d5600ebabdf26d62b8fe610a7375c Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 22:43:08 +0800 Subject: [PATCH 43/57] docs: Update op_binding to use operation-to-value-policy binding table --- src/operations/binding.cppm | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/operations/binding.cppm b/src/operations/binding.cppm index ac01b8b..5d7f6a4 100644 --- a/src/operations/binding.cppm +++ b/src/operations/binding.cppm @@ -363,9 +363,8 @@ constexpr auto make_div_zero(char const *reason) } // namespace details -// Placeholder op-binding protocol for end-to-end bring-up. -// NOTE: OpTag-to-arithmetic mapping is intentionally hardcoded in this module -// for now. It will be replaced by policy-aware operation bindings later. +// Operation-to-value-policy binding table used by invoker runtime dispatch. +// This module centralizes OpTag mapping and fallback diagnostics. template struct op_binding { static constexpr bool enabled = false; From 8f6a07244eae5acbea0f6d8e330b3c4cdca13783 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 22:43:24 +0800 Subject: [PATCH 44/57] docs: Simplify and clarify dispatcher pipeline description --- src/operations/dispatcher.cppm | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/operations/dispatcher.cppm b/src/operations/dispatcher.cppm index 2a76559..603f070 100644 --- a/src/operations/dispatcher.cppm +++ b/src/operations/dispatcher.cppm @@ -74,9 +74,8 @@ using dispatch_result_t = std::expected< typename dispatcher_meta::common_rep, ErrorPayload>; -// Closed-loop placeholder dispatcher: compile-time type negotiation + runtime -// chain (concurrency -> value -> error). The value/error execution details are -// intentionally hardcoded for now and will migrate to handler-driven calls. +// Dispatcher pipeline: compile-time negotiation plus runtime chain +// (concurrency -> value -> error) through selected policy handlers. template constexpr auto dispatch(Lhs const &lhs, Rhs const &rhs) From 3c175db64d34c8263722bb5e09b3767440184ac1 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 22:43:35 +0800 Subject: [PATCH 45/57] refactor: Remove unused imports in operations.cppm --- src/operations/operations.cppm | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/operations/operations.cppm b/src/operations/operations.cppm index fcd5ecf..cabb288 100644 --- a/src/operations/operations.cppm +++ b/src/operations/operations.cppm @@ -4,7 +4,5 @@ export module mcpplibs.primitives.operations; export import mcpplibs.primitives.operations.traits; export import mcpplibs.primitives.operations.impl; -export import mcpplibs.primitives.operations.binding; -export import mcpplibs.primitives.operations.invoker; export import mcpplibs.primitives.operations.dispatcher; export import mcpplibs.primitives.operations.operators; \ No newline at end of file From 07cdd1c3592863fe4c4a18ced0104c38306aae36 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 23:02:14 +0800 Subject: [PATCH 46/57] refactor: Move operation bindings to invoker and update dispatcher references --- src/operations/binding.cppm | 623 --------------------------------- src/operations/dispatcher.cppm | 8 +- src/operations/invoker.cppm | 614 +++++++++++++++++++++++++++++++- 3 files changed, 612 insertions(+), 633 deletions(-) delete mode 100644 src/operations/binding.cppm diff --git a/src/operations/binding.cppm b/src/operations/binding.cppm deleted file mode 100644 index 5d7f6a4..0000000 --- a/src/operations/binding.cppm +++ /dev/null @@ -1,623 +0,0 @@ -module; - -#include -#include -#include - -export module mcpplibs.primitives.operations.binding; - -import mcpplibs.primitives.operations.traits; -import mcpplibs.primitives.operations.impl; -import mcpplibs.primitives.policy.handler; -import mcpplibs.primitives.policy.impl; -import mcpplibs.primitives.policy.traits; - -export namespace mcpplibs::primitives::operations::binding { - -namespace details { - -template -constexpr auto make_error(policy::error::kind kind, char const *reason, - std::optional lhs = std::nullopt, - std::optional rhs = std::nullopt) - -> policy::value::decision { - policy::value::decision out{}; - out.has_value = false; - out.error.kind = kind; - out.error.reason = reason; - out.error.lhs_value = lhs; - out.error.rhs_value = rhs; - return out; -} - -template -constexpr auto checked_add(T lhs, T rhs) -> policy::value::decision { - if constexpr (!std::is_integral_v || std::is_same_v) { - policy::value::decision out{}; - out.has_value = true; - out.value = static_cast(lhs + rhs); - return out; - } else if constexpr (std::is_unsigned_v) { - auto const maxv = std::numeric_limits::max(); - if (lhs > maxv - rhs) { - return make_error(policy::error::kind::overflow, - "checked addition overflow", lhs, rhs); - } - policy::value::decision out{}; - out.has_value = true; - out.value = static_cast(lhs + rhs); - return out; - } else { - auto const maxv = std::numeric_limits::max(); - auto const minv = std::numeric_limits::min(); - if ((rhs > 0) && (lhs > maxv - rhs)) { - return make_error(policy::error::kind::overflow, - "checked addition overflow", lhs, rhs); - } - if ((rhs < 0) && (lhs < minv - rhs)) { - return make_error(policy::error::kind::underflow, - "checked addition underflow", lhs, rhs); - } - policy::value::decision out{}; - out.has_value = true; - out.value = static_cast(lhs + rhs); - return out; - } -} - -template -constexpr auto checked_sub(T lhs, T rhs) -> policy::value::decision { - if constexpr (!std::is_integral_v || std::is_same_v) { - policy::value::decision out{}; - out.has_value = true; - out.value = static_cast(lhs - rhs); - return out; - } else if constexpr (std::is_unsigned_v) { - if (lhs < rhs) { - return make_error(policy::error::kind::underflow, - "checked subtraction underflow", lhs, rhs); - } - policy::value::decision out{}; - out.has_value = true; - out.value = static_cast(lhs - rhs); - return out; - } else { - auto const maxv = std::numeric_limits::max(); - auto const minv = std::numeric_limits::min(); - if ((rhs < 0) && (lhs > maxv + rhs)) { - return make_error(policy::error::kind::overflow, - "checked subtraction overflow", lhs, rhs); - } - if ((rhs > 0) && (lhs < minv + rhs)) { - return make_error(policy::error::kind::underflow, - "checked subtraction underflow", lhs, rhs); - } - policy::value::decision out{}; - out.has_value = true; - out.value = static_cast(lhs - rhs); - return out; - } -} - -template -constexpr auto checked_mul(T lhs, T rhs) -> policy::value::decision { - if constexpr (!std::is_integral_v || std::is_same_v) { - policy::value::decision out{}; - out.has_value = true; - out.value = static_cast(lhs * rhs); - return out; - } else { - if (lhs == T{} || rhs == T{}) { - policy::value::decision out{}; - out.has_value = true; - out.value = T{}; - return out; - } - - if constexpr (std::is_unsigned_v) { - auto const maxv = std::numeric_limits::max(); - if (lhs > maxv / rhs) { - return make_error(policy::error::kind::overflow, - "checked multiplication overflow", lhs, rhs); - } - policy::value::decision out{}; - out.has_value = true; - out.value = static_cast(lhs * rhs); - return out; - } else { - auto const maxv = std::numeric_limits::max(); - auto const minv = std::numeric_limits::min(); - - if (lhs > 0) { - if (rhs > 0) { - if (lhs > maxv / rhs) { - return make_error(policy::error::kind::overflow, - "checked multiplication overflow", lhs, rhs); - } - } else { - if (rhs < minv / lhs) { - return make_error(policy::error::kind::underflow, - "checked multiplication underflow", lhs, rhs); - } - } - } else { - if (rhs > 0) { - if (lhs < minv / rhs) { - return make_error(policy::error::kind::underflow, - "checked multiplication underflow", lhs, rhs); - } - } else { - if (lhs != 0 && rhs < maxv / lhs) { - return make_error(policy::error::kind::overflow, - "checked multiplication overflow", lhs, rhs); - } - } - } - - policy::value::decision out{}; - out.has_value = true; - out.value = static_cast(lhs * rhs); - return out; - } - } -} - -template -constexpr auto checked_div(T lhs, T rhs) -> policy::value::decision { - if (rhs == T{}) { - return make_error(policy::error::kind::divide_by_zero, - "checked division by zero", lhs, rhs); - } - - if constexpr (std::is_integral_v && std::is_signed_v) { - auto const minv = std::numeric_limits::min(); - if (lhs == minv && rhs == static_cast(-1)) { - return make_error(policy::error::kind::overflow, - "checked division overflow", lhs, rhs); - } - } - - if constexpr (requires { lhs / rhs; }) { - policy::value::decision out{}; - out.has_value = true; - out.value = static_cast(lhs / rhs); - return out; - } - - return make_error( - policy::error::kind::unspecified, - "checked division not supported for negotiated common type", lhs, rhs); -} - -template -constexpr auto compare_equal(T lhs, T rhs) -> policy::value::decision { - policy::value::decision out{}; - if constexpr (requires { lhs == rhs; }) { - out.has_value = true; - out.value = static_cast(lhs == rhs); - return out; - } - - return make_error(policy::error::kind::unspecified, - "comparison equality not supported for negotiated " - "common type"); -} - -template -constexpr auto compare_not_equal(T lhs, T rhs) -> policy::value::decision { - policy::value::decision out{}; - if constexpr (requires { lhs != rhs; }) { - out.has_value = true; - out.value = static_cast(lhs != rhs); - return out; - } - - return make_error(policy::error::kind::unspecified, - "comparison inequality not supported for negotiated " - "common type"); -} - -template -constexpr auto unchecked_add(T lhs, T rhs) -> policy::value::decision { - policy::value::decision out{}; - out.has_value = true; - - if constexpr (requires { lhs + rhs; }) { - out.value = static_cast(lhs + rhs); - return out; - } - - out.value = T{}; - return out; -} - -template -constexpr auto unchecked_sub(T lhs, T rhs) -> policy::value::decision { - policy::value::decision out{}; - out.has_value = true; - - if constexpr (requires { lhs - rhs; }) { - out.value = static_cast(lhs - rhs); - return out; - } - - out.value = T{}; - return out; -} - -template -constexpr auto unchecked_mul(T lhs, T rhs) -> policy::value::decision { - policy::value::decision out{}; - out.has_value = true; - - if constexpr (requires { lhs * rhs; }) { - out.value = static_cast(lhs * rhs); - return out; - } - - out.value = T{}; - return out; -} - -template -constexpr auto unchecked_div(T lhs, T rhs) -> policy::value::decision { - policy::value::decision out{}; - out.has_value = true; - - // Intentionally no guards: unchecked policy delegates error/UB behavior - // to the underlying language/runtime semantics. - out.value = static_cast(lhs / rhs); - return out; -} - -template constexpr auto saturating_add(T lhs, T rhs) -> T { - if constexpr (!std::is_integral_v || std::is_same_v) { - return static_cast(lhs + rhs); - } else if constexpr (std::is_unsigned_v) { - auto const maxv = std::numeric_limits::max(); - return (lhs > maxv - rhs) ? maxv : static_cast(lhs + rhs); - } else { - auto const maxv = std::numeric_limits::max(); - auto const minv = std::numeric_limits::min(); - if ((rhs > 0) && (lhs > maxv - rhs)) { - return maxv; - } - if ((rhs < 0) && (lhs < minv - rhs)) { - return minv; - } - return static_cast(lhs + rhs); - } -} - -template constexpr auto saturating_sub(T lhs, T rhs) -> T { - if constexpr (!std::is_integral_v || std::is_same_v) { - return static_cast(lhs - rhs); - } else if constexpr (std::is_unsigned_v) { - return (lhs < rhs) ? T{} : static_cast(lhs - rhs); - } else { - auto const maxv = std::numeric_limits::max(); - auto const minv = std::numeric_limits::min(); - if ((rhs < 0) && (lhs > maxv + rhs)) { - return maxv; - } - if ((rhs > 0) && (lhs < minv + rhs)) { - return minv; - } - return static_cast(lhs - rhs); - } -} - -template constexpr auto saturating_mul(T lhs, T rhs) -> T { - if constexpr (!std::is_integral_v || std::is_same_v) { - return static_cast(lhs * rhs); - } else { - if (lhs == T{} || rhs == T{}) { - return T{}; - } - - if constexpr (std::is_unsigned_v) { - auto const maxv = std::numeric_limits::max(); - return (lhs > maxv / rhs) ? maxv : static_cast(lhs * rhs); - } else { - auto const maxv = std::numeric_limits::max(); - auto const minv = std::numeric_limits::min(); - - if (lhs > 0) { - if (rhs > 0) { - if (lhs > maxv / rhs) { - return maxv; - } - } else { - if (rhs < minv / lhs) { - return minv; - } - } - } else { - if (rhs > 0) { - if (lhs < minv / rhs) { - return minv; - } - } else { - if (lhs != 0 && rhs < maxv / lhs) { - return maxv; - } - } - } - - return static_cast(lhs * rhs); - } - } -} - -template -constexpr auto make_unsupported(char const *reason) - -> policy::value::decision { - return make_error(policy::error::kind::unspecified, reason); -} - -template -constexpr auto make_div_zero(char const *reason) - -> policy::value::decision { - return make_error(policy::error::kind::divide_by_zero, reason); -} - -} // namespace details - -// Operation-to-value-policy binding table used by invoker runtime dispatch. -// This module centralizes OpTag mapping and fallback diagnostics. -template -struct op_binding { - static constexpr bool enabled = false; - - static constexpr auto apply(CommonRep, CommonRep) - -> policy::value::decision { - policy::value::decision out{}; - out.has_value = false; - out.error.kind = policy::error::kind::unspecified; - out.error.reason = "operation binding is not implemented"; - return out; - } -}; - -template -struct op_binding { - static constexpr bool enabled = true; - - static constexpr auto apply(CommonRep lhs, CommonRep rhs) - -> policy::value::decision { - if constexpr (requires { details::checked_add(lhs, rhs); }) { - return details::checked_add(lhs, rhs); - } - - return details::make_unsupported( - "checked addition not supported for negotiated common type"); - } -}; - -template -struct op_binding { - static constexpr bool enabled = true; - - static constexpr auto apply(CommonRep lhs, CommonRep rhs) - -> policy::value::decision { - if constexpr (requires { details::checked_sub(lhs, rhs); }) { - return details::checked_sub(lhs, rhs); - } - - return details::make_unsupported( - "checked subtraction not supported for negotiated common type"); - } -}; - -template -struct op_binding { - static constexpr bool enabled = true; - - static constexpr auto apply(CommonRep lhs, CommonRep rhs) - -> policy::value::decision { - if constexpr (requires { details::checked_mul(lhs, rhs); }) { - return details::checked_mul(lhs, rhs); - } - - return details::make_unsupported( - "checked multiplication not supported for negotiated common type"); - } -}; - -template -struct op_binding { - static constexpr bool enabled = true; - - static constexpr auto apply(CommonRep lhs, CommonRep rhs) - -> policy::value::decision { - if constexpr (requires { details::checked_div(lhs, rhs); }) { - return details::checked_div(lhs, rhs); - } - - return details::make_unsupported( - "checked division not supported for negotiated common type"); - } -}; - -template -struct op_binding { - static constexpr bool enabled = true; - - static constexpr auto apply(CommonRep lhs, CommonRep rhs) - -> policy::value::decision { - return details::unchecked_add(lhs, rhs); - } -}; - -template -struct op_binding { - static constexpr bool enabled = true; - - static constexpr auto apply(CommonRep lhs, CommonRep rhs) - -> policy::value::decision { - return details::unchecked_sub(lhs, rhs); - } -}; - -template -struct op_binding { - static constexpr bool enabled = true; - - static constexpr auto apply(CommonRep lhs, CommonRep rhs) - -> policy::value::decision { - return details::unchecked_mul(lhs, rhs); - } -}; - -template -struct op_binding { - static constexpr bool enabled = true; - - static constexpr auto apply(CommonRep lhs, CommonRep rhs) - -> policy::value::decision { - return details::unchecked_div(lhs, rhs); - } -}; - -template -struct op_binding { - static constexpr bool enabled = true; - - static constexpr auto apply(CommonRep lhs, CommonRep rhs) - -> policy::value::decision { - policy::value::decision out{}; - if constexpr (requires { details::saturating_add(lhs, rhs); }) { - out.has_value = true; - out.value = details::saturating_add(lhs, rhs); - return out; - } - return details::make_unsupported( - "saturating addition not supported for negotiated common type"); - } -}; - -template -struct op_binding { - static constexpr bool enabled = true; - - static constexpr auto apply(CommonRep lhs, CommonRep rhs) - -> policy::value::decision { - policy::value::decision out{}; - if constexpr (requires { details::saturating_sub(lhs, rhs); }) { - out.has_value = true; - out.value = details::saturating_sub(lhs, rhs); - return out; - } - return details::make_unsupported( - "saturating subtraction not supported for negotiated common type"); - } -}; - -template -struct op_binding { - static constexpr bool enabled = true; - - static constexpr auto apply(CommonRep lhs, CommonRep rhs) - -> policy::value::decision { - policy::value::decision out{}; - if constexpr (requires { details::saturating_mul(lhs, rhs); }) { - out.has_value = true; - out.value = details::saturating_mul(lhs, rhs); - return out; - } - return details::make_unsupported( - "saturating multiplication not supported for negotiated common type"); - } -}; - -template -struct op_binding { - static constexpr bool enabled = true; - - static constexpr auto apply(CommonRep lhs, CommonRep rhs) - -> policy::value::decision { - policy::value::decision out{}; - - if (rhs == CommonRep{}) { - return details::make_div_zero("saturating division by zero"); - } - - if constexpr (requires { lhs / rhs; }) { - out.has_value = true; - out.value = static_cast(lhs / rhs); - return out; - } - - return details::make_unsupported( - "saturating division not supported for negotiated common type"); - } -}; - -template -struct op_binding { - static constexpr bool enabled = true; - - static constexpr auto apply(CommonRep lhs, CommonRep rhs) - -> policy::value::decision { - return details::compare_equal(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_equal(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_equal(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_not_equal(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_not_equal(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_not_equal(lhs, rhs); - } -}; - -template -concept op_binding_available = requires { - requires operation; - requires policy::value_policy; - requires op_binding::enabled; -}; - -} // namespace mcpplibs::primitives::operations::binding diff --git a/src/operations/dispatcher.cppm b/src/operations/dispatcher.cppm index 603f070..d8ff8e9 100644 --- a/src/operations/dispatcher.cppm +++ b/src/operations/dispatcher.cppm @@ -119,7 +119,7 @@ constexpr auto dispatch(Lhs const &lhs, Rhs const &rhs) // Runtime stage 1: concurrency context injection. auto const injection = - invoker::inject_concurrency(); + runtime::inject_concurrency(); // Runtime stage 2: value path. auto const lhs_rep_raw = @@ -134,7 +134,7 @@ constexpr auto dispatch(Lhs const &lhs, Rhs const &rhs) policy::error::request request{}; request.kind = policy::error::kind::domain_error; request.reason = "invalid underlying representation"; - return invoker::resolve_error(request); } @@ -151,7 +151,7 @@ constexpr auto dispatch(Lhs const &lhs, Rhs const &rhs) rhs_value_normalized)); auto const decision = - invoker::run_value(lhs_common, rhs_common, injection); @@ -160,7 +160,7 @@ constexpr auto dispatch(Lhs const &lhs, Rhs const &rhs) } // Runtime stage 3: error policy. - return invoker::resolve_error(decision.error); } diff --git a/src/operations/invoker.cppm b/src/operations/invoker.cppm index 923dc1e..c496d5a 100644 --- a/src/operations/invoker.cppm +++ b/src/operations/invoker.cppm @@ -3,20 +3,368 @@ module; #include #include #include +#include +#include #include #include export module mcpplibs.primitives.operations.invoker; import mcpplibs.primitives.operations.traits; -import mcpplibs.primitives.operations.binding; +import mcpplibs.primitives.operations.impl; import mcpplibs.primitives.policy.handler; import mcpplibs.primitives.policy.impl; +import mcpplibs.primitives.policy.traits; -export namespace mcpplibs::primitives::operations::invoker { +export namespace mcpplibs::primitives::operations::runtime { namespace details { +template +constexpr auto make_error(policy::error::kind kind, char const *reason, + std::optional lhs = std::nullopt, + std::optional rhs = std::nullopt) + -> policy::value::decision { + policy::value::decision out{}; + out.has_value = false; + out.error.kind = kind; + out.error.reason = reason; + out.error.lhs_value = lhs; + out.error.rhs_value = rhs; + return out; +} + +template +constexpr auto checked_add(T lhs, T rhs) -> policy::value::decision { + if constexpr (!std::is_integral_v || std::is_same_v) { + policy::value::decision out{}; + out.has_value = true; + out.value = static_cast(lhs + rhs); + return out; + } else if constexpr (std::is_unsigned_v) { + auto const maxv = std::numeric_limits::max(); + if (lhs > maxv - rhs) { + return make_error(policy::error::kind::overflow, + "checked addition overflow", lhs, rhs); + } + policy::value::decision out{}; + out.has_value = true; + out.value = static_cast(lhs + rhs); + return out; + } else { + auto const maxv = std::numeric_limits::max(); + auto const minv = std::numeric_limits::min(); + if ((rhs > 0) && (lhs > maxv - rhs)) { + return make_error(policy::error::kind::overflow, + "checked addition overflow", lhs, rhs); + } + if ((rhs < 0) && (lhs < minv - rhs)) { + return make_error(policy::error::kind::underflow, + "checked addition underflow", lhs, rhs); + } + policy::value::decision out{}; + out.has_value = true; + out.value = static_cast(lhs + rhs); + return out; + } +} + +template +constexpr auto checked_sub(T lhs, T rhs) -> policy::value::decision { + if constexpr (!std::is_integral_v || std::is_same_v) { + policy::value::decision out{}; + out.has_value = true; + out.value = static_cast(lhs - rhs); + return out; + } else if constexpr (std::is_unsigned_v) { + if (lhs < rhs) { + return make_error(policy::error::kind::underflow, + "checked subtraction underflow", lhs, rhs); + } + policy::value::decision out{}; + out.has_value = true; + out.value = static_cast(lhs - rhs); + return out; + } else { + auto const maxv = std::numeric_limits::max(); + auto const minv = std::numeric_limits::min(); + if ((rhs < 0) && (lhs > maxv + rhs)) { + return make_error(policy::error::kind::overflow, + "checked subtraction overflow", lhs, rhs); + } + if ((rhs > 0) && (lhs < minv + rhs)) { + return make_error(policy::error::kind::underflow, + "checked subtraction underflow", lhs, rhs); + } + policy::value::decision out{}; + out.has_value = true; + out.value = static_cast(lhs - rhs); + return out; + } +} + +template +constexpr auto checked_mul(T lhs, T rhs) -> policy::value::decision { + if constexpr (!std::is_integral_v || std::is_same_v) { + policy::value::decision out{}; + out.has_value = true; + out.value = static_cast(lhs * rhs); + return out; + } else { + if (lhs == T{} || rhs == T{}) { + policy::value::decision out{}; + out.has_value = true; + out.value = T{}; + return out; + } + + if constexpr (std::is_unsigned_v) { + auto const maxv = std::numeric_limits::max(); + if (lhs > maxv / rhs) { + return make_error(policy::error::kind::overflow, + "checked multiplication overflow", lhs, rhs); + } + policy::value::decision out{}; + out.has_value = true; + out.value = static_cast(lhs * rhs); + return out; + } else { + auto const maxv = std::numeric_limits::max(); + auto const minv = std::numeric_limits::min(); + + if (lhs > 0) { + if (rhs > 0) { + if (lhs > maxv / rhs) { + return make_error(policy::error::kind::overflow, + "checked multiplication overflow", lhs, rhs); + } + } else { + if (rhs < minv / lhs) { + return make_error(policy::error::kind::underflow, + "checked multiplication underflow", lhs, rhs); + } + } + } else { + if (rhs > 0) { + if (lhs < minv / rhs) { + return make_error(policy::error::kind::underflow, + "checked multiplication underflow", lhs, rhs); + } + } else { + if (lhs != 0 && rhs < maxv / lhs) { + return make_error(policy::error::kind::overflow, + "checked multiplication overflow", lhs, rhs); + } + } + } + + policy::value::decision out{}; + out.has_value = true; + out.value = static_cast(lhs * rhs); + return out; + } + } +} + +template +constexpr auto checked_div(T lhs, T rhs) -> policy::value::decision { + if (rhs == T{}) { + return make_error(policy::error::kind::divide_by_zero, + "checked division by zero", lhs, rhs); + } + + if constexpr (std::is_integral_v && std::is_signed_v) { + auto const minv = std::numeric_limits::min(); + if (lhs == minv && rhs == static_cast(-1)) { + return make_error(policy::error::kind::overflow, + "checked division overflow", lhs, rhs); + } + } + + if constexpr (requires { lhs / rhs; }) { + policy::value::decision out{}; + out.has_value = true; + out.value = static_cast(lhs / rhs); + return out; + } + + return make_error( + policy::error::kind::unspecified, + "checked division not supported for negotiated common type", lhs, rhs); +} + +template +constexpr auto compare_equal(T lhs, T rhs) -> policy::value::decision { + policy::value::decision out{}; + if constexpr (requires { lhs == rhs; }) { + out.has_value = true; + out.value = static_cast(lhs == rhs); + return out; + } + + return make_error(policy::error::kind::unspecified, + "comparison equality not supported for negotiated " + "common type"); +} + +template +constexpr auto compare_not_equal(T lhs, T rhs) -> policy::value::decision { + policy::value::decision out{}; + if constexpr (requires { lhs != rhs; }) { + out.has_value = true; + out.value = static_cast(lhs != rhs); + return out; + } + + return make_error(policy::error::kind::unspecified, + "comparison inequality not supported for negotiated " + "common type"); +} + +template +constexpr auto unchecked_add(T lhs, T rhs) -> policy::value::decision { + policy::value::decision out{}; + out.has_value = true; + + if constexpr (requires { lhs + rhs; }) { + out.value = static_cast(lhs + rhs); + return out; + } + + out.value = T{}; + return out; +} + +template +constexpr auto unchecked_sub(T lhs, T rhs) -> policy::value::decision { + policy::value::decision out{}; + out.has_value = true; + + if constexpr (requires { lhs - rhs; }) { + out.value = static_cast(lhs - rhs); + return out; + } + + out.value = T{}; + return out; +} + +template +constexpr auto unchecked_mul(T lhs, T rhs) -> policy::value::decision { + policy::value::decision out{}; + out.has_value = true; + + if constexpr (requires { lhs * rhs; }) { + out.value = static_cast(lhs * rhs); + return out; + } + + out.value = T{}; + return out; +} + +template +constexpr auto unchecked_div(T lhs, T rhs) -> policy::value::decision { + policy::value::decision out{}; + out.has_value = true; + + // Intentionally no guards: unchecked policy delegates error/UB behavior + // to the underlying language/runtime semantics. + out.value = static_cast(lhs / rhs); + return out; +} + +template constexpr auto saturating_add(T lhs, T rhs) -> T { + if constexpr (!std::is_integral_v || std::is_same_v) { + return static_cast(lhs + rhs); + } else if constexpr (std::is_unsigned_v) { + auto const maxv = std::numeric_limits::max(); + return (lhs > maxv - rhs) ? maxv : static_cast(lhs + rhs); + } else { + auto const maxv = std::numeric_limits::max(); + auto const minv = std::numeric_limits::min(); + if ((rhs > 0) && (lhs > maxv - rhs)) { + return maxv; + } + if ((rhs < 0) && (lhs < minv - rhs)) { + return minv; + } + return static_cast(lhs + rhs); + } +} + +template constexpr auto saturating_sub(T lhs, T rhs) -> T { + if constexpr (!std::is_integral_v || std::is_same_v) { + return static_cast(lhs - rhs); + } else if constexpr (std::is_unsigned_v) { + return (lhs < rhs) ? T{} : static_cast(lhs - rhs); + } else { + auto const maxv = std::numeric_limits::max(); + auto const minv = std::numeric_limits::min(); + if ((rhs < 0) && (lhs > maxv + rhs)) { + return maxv; + } + if ((rhs > 0) && (lhs < minv + rhs)) { + return minv; + } + return static_cast(lhs - rhs); + } +} + +template constexpr auto saturating_mul(T lhs, T rhs) -> T { + if constexpr (!std::is_integral_v || std::is_same_v) { + return static_cast(lhs * rhs); + } else { + if (lhs == T{} || rhs == T{}) { + return T{}; + } + + if constexpr (std::is_unsigned_v) { + auto const maxv = std::numeric_limits::max(); + return (lhs > maxv / rhs) ? maxv : static_cast(lhs * rhs); + } else { + auto const maxv = std::numeric_limits::max(); + auto const minv = std::numeric_limits::min(); + + if (lhs > 0) { + if (rhs > 0) { + if (lhs > maxv / rhs) { + return maxv; + } + } else { + if (rhs < minv / lhs) { + return minv; + } + } + } else { + if (rhs > 0) { + if (lhs < minv / rhs) { + return minv; + } + } else { + if (lhs != 0 && rhs < maxv / lhs) { + return maxv; + } + } + } + + return static_cast(lhs * rhs); + } + } +} + +template +constexpr auto make_unsupported(char const *reason) + -> policy::value::decision { + return make_error(policy::error::kind::unspecified, reason); +} + +template +constexpr auto make_div_zero(char const *reason) + -> policy::value::decision { + return make_error(policy::error::kind::divide_by_zero, reason); +} + constexpr auto apply_runtime_fence(bool enabled) noexcept -> void { if (!enabled) { return; @@ -29,6 +377,261 @@ constexpr auto apply_runtime_fence(bool enabled) noexcept -> void { } // namespace details +template +struct op_binding { + static constexpr bool enabled = false; + + static constexpr auto apply(CommonRep, CommonRep) + -> policy::value::decision { + policy::value::decision out{}; + out.has_value = false; + out.error.kind = policy::error::kind::unspecified; + out.error.reason = "operation binding is not implemented"; + return out; + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value::decision { + if constexpr (requires { details::checked_add(lhs, rhs); }) { + return details::checked_add(lhs, rhs); + } + + return details::make_unsupported( + "checked addition not supported for negotiated common type"); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value::decision { + if constexpr (requires { details::checked_sub(lhs, rhs); }) { + return details::checked_sub(lhs, rhs); + } + + return details::make_unsupported( + "checked subtraction not supported for negotiated common type"); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value::decision { + if constexpr (requires { details::checked_mul(lhs, rhs); }) { + return details::checked_mul(lhs, rhs); + } + + return details::make_unsupported( + "checked multiplication not supported for negotiated common type"); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value::decision { + if constexpr (requires { details::checked_div(lhs, rhs); }) { + return details::checked_div(lhs, rhs); + } + + return details::make_unsupported( + "checked division not supported for negotiated common type"); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value::decision { + return details::unchecked_add(lhs, rhs); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value::decision { + return details::unchecked_sub(lhs, rhs); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value::decision { + return details::unchecked_mul(lhs, rhs); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value::decision { + return details::unchecked_div(lhs, rhs); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value::decision { + policy::value::decision out{}; + if constexpr (requires { details::saturating_add(lhs, rhs); }) { + out.has_value = true; + out.value = details::saturating_add(lhs, rhs); + return out; + } + return details::make_unsupported( + "saturating addition not supported for negotiated common type"); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value::decision { + policy::value::decision out{}; + if constexpr (requires { details::saturating_sub(lhs, rhs); }) { + out.has_value = true; + out.value = details::saturating_sub(lhs, rhs); + return out; + } + return details::make_unsupported( + "saturating subtraction not supported for negotiated common type"); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value::decision { + policy::value::decision out{}; + if constexpr (requires { details::saturating_mul(lhs, rhs); }) { + out.has_value = true; + out.value = details::saturating_mul(lhs, rhs); + return out; + } + return details::make_unsupported( + "saturating multiplication not supported for negotiated common type"); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value::decision { + policy::value::decision out{}; + + if (rhs == CommonRep{}) { + return details::make_div_zero("saturating division by zero"); + } + + if constexpr (requires { lhs / rhs; }) { + out.has_value = true; + out.value = static_cast(lhs / rhs); + return out; + } + + return details::make_unsupported( + "saturating division not supported for negotiated common type"); + } +}; + +template +struct op_binding { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value::decision { + return details::compare_equal(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_equal(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_equal(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_not_equal(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_not_equal(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_not_equal(lhs, rhs); + } +}; + +template +concept op_binding_available = requires { + requires operation; + requires policy::value_policy; + requires op_binding::enabled; +}; + template constexpr auto inject_concurrency() -> policy::concurrency::injection { static_cast(sizeof(CommonRep)); @@ -54,13 +657,12 @@ constexpr auto run_value(CommonRep lhs, CommonRep rhs, "value handler decision_type must match policy::value::decision"); static_assert( - binding::op_binding_available, + op_binding_available, "Missing operation binding specialization for this OpTag/common type"); details::apply_runtime_fence(injection.fence_before); - auto decision = - binding::op_binding::apply(lhs, rhs); + auto decision = op_binding::apply(lhs, rhs); auto finalized = ValueHandler::finalize(std::move(decision), injection); details::apply_runtime_fence(injection.fence_after); @@ -76,4 +678,4 @@ constexpr auto resolve_error(policy::error::request const &request) return handler_t::resolve(request); } -} // namespace mcpplibs::primitives::operations::invoker +} // namespace mcpplibs::primitives::operations::runtime From 51fbf82fe11f0d2da4ab31eea3498fa0057fce17 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 23:49:02 +0800 Subject: [PATCH 47/57] Add examples for default arithmetic, type policy, value policy, error policy, concurrency policy, custom underlying, custom policy, and custom operations --- examples/ex01_default_arithmetic.cpp | 35 +++++++ examples/ex02_type_policy.cpp | 40 ++++++++ examples/ex03_value_policy.cpp | 49 ++++++++++ examples/ex04_error_policy.cpp | 43 ++++++++ examples/ex05_concurrency_policy.cpp | 47 +++++++++ examples/ex06_custom_underlying.cpp | 86 ++++++++++++++++ examples/ex07_custom_policy.cpp | 141 +++++++++++++++++++++++++++ examples/ex08_custom_operation.cpp | 103 +++++++++++++++++++ 8 files changed, 544 insertions(+) create mode 100644 examples/ex01_default_arithmetic.cpp create mode 100644 examples/ex02_type_policy.cpp create mode 100644 examples/ex03_value_policy.cpp create mode 100644 examples/ex04_error_policy.cpp create mode 100644 examples/ex05_concurrency_policy.cpp create mode 100644 examples/ex06_custom_underlying.cpp create mode 100644 examples/ex07_custom_policy.cpp create mode 100644 examples/ex08_custom_operation.cpp diff --git a/examples/ex01_default_arithmetic.cpp b/examples/ex01_default_arithmetic.cpp new file mode 100644 index 0000000..7d8285d --- /dev/null +++ b/examples/ex01_default_arithmetic.cpp @@ -0,0 +1,35 @@ +#include +#include + +import mcpplibs.primitives; + +using namespace mcpplibs::primitives; + +int main() { + // Point 1: Use built-in primitive type aliases and framework operators. + using namespace mcpplibs::primitives::types; + using namespace mcpplibs::primitives::operators; + + using value_t = I32<>; + + // Prepare operands. + auto const a = value_t{40}; + auto const b = value_t{2}; + + // Run the four arithmetic operators routed through dispatcher. + auto const sum = a + b; + auto const diff = a - b; + auto const prod = a * b; + auto const quot = a / b; + + // Validate all dispatches succeeded. + if (!sum.has_value() || !diff.has_value() || !prod.has_value() || + !quot.has_value()) { + std::cerr << "default arithmetic failed\n"; + return 1; + } + + std::cout << "sum=" << sum->value() << ", diff=" << diff->value() + << ", prod=" << prod->value() << ", quot=" << quot->value() << '\n'; + return 0; +} diff --git a/examples/ex02_type_policy.cpp b/examples/ex02_type_policy.cpp new file mode 100644 index 0000000..0d1b531 --- /dev/null +++ b/examples/ex02_type_policy.cpp @@ -0,0 +1,40 @@ +#include +#include + +import mcpplibs.primitives; + +using namespace mcpplibs::primitives; + +int main() { + // Point 2: Compare different type_policy behaviors. + // Case A: strict policy rejects mixed underlying reps at compile time. + using strict_i32 = primitive; + using strict_i64 = primitive; + + using strict_meta = + operations::dispatcher_meta; + + static_assert(std::is_same_v); + static_assert( + !policy::type::handler::allowed); + + // Case B: compatible policy allows mixed arithmetic reps and negotiates a + // common type. + using compatible_meta = operations::dispatcher_meta< + operations::Addition, + primitive, + primitive, + policy::error::kind>; + + static_assert( + std::is_same_v); + + std::cout << "type_policy demo passed\n"; + return 0; +} diff --git a/examples/ex03_value_policy.cpp b/examples/ex03_value_policy.cpp new file mode 100644 index 0000000..5054d01 --- /dev/null +++ b/examples/ex03_value_policy.cpp @@ -0,0 +1,49 @@ +#include +#include + +import mcpplibs.primitives; + +using namespace mcpplibs::primitives; + +int main() { + // Point 3: Compare checked / unchecked / saturating value policies. + using checked_t = + primitive; + using unchecked_t = primitive; + using saturating_t = primitive; + + // Same overflow input for all three policies. + auto const lhs_checked = checked_t{65530}; + auto const rhs_checked = checked_t{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 unchecked_result = operations::add(lhs_unchecked, rhs_unchecked); + + auto const lhs_saturating = saturating_t{65530}; + auto const rhs_saturating = saturating_t{20}; + auto const saturating_result = + operations::add(lhs_saturating, rhs_saturating); + + // Expected outcomes: + // checked -> error, unchecked -> wrap, saturating -> clamp. + if (checked_result.has_value()) { + std::cerr << "checked policy should report overflow\n"; + return 1; + } + if (!unchecked_result.has_value() || unchecked_result->value() != 14) { + std::cerr << "unchecked policy should wrap\n"; + return 1; + } + if (!saturating_result.has_value() || saturating_result->value() != 65535) { + std::cerr << "saturating policy should clamp\n"; + return 1; + } + + std::cout << "checked=overflow, unchecked=" << unchecked_result->value() + << ", saturating=" << saturating_result->value() << '\n'; + return 0; +} diff --git a/examples/ex04_error_policy.cpp b/examples/ex04_error_policy.cpp new file mode 100644 index 0000000..60baa95 --- /dev/null +++ b/examples/ex04_error_policy.cpp @@ -0,0 +1,43 @@ +#include +#include + +import mcpplibs.primitives; + +using namespace mcpplibs::primitives; + +int main() { + // Point 4: Compare error_policy behavior under divide-by-zero. + using expected_t = + primitive; + using throwing_t = + primitive; + + // Case A: expected policy returns error payload. + auto const e_lhs = expected_t{42}; + auto const e_rhs = expected_t{0}; + auto const expected_result = operations::div(e_lhs, e_rhs); + if (expected_result.has_value() || + expected_result.error() != policy::error::kind::divide_by_zero) { + std::cerr << "expected policy did not return divide_by_zero\n"; + return 1; + } + + // Case B: throwing policy raises exception. + auto const t_lhs = throwing_t{42}; + auto const t_rhs = throwing_t{0}; + + bool caught = false; + try { + (void)operations::div(t_lhs, t_rhs); + } catch (std::runtime_error const &) { + caught = true; + } + + if (!caught) { + std::cerr << "throwing policy did not throw\n"; + return 1; + } + + std::cout << "error_policy demo passed\n"; + return 0; +} diff --git a/examples/ex05_concurrency_policy.cpp b/examples/ex05_concurrency_policy.cpp new file mode 100644 index 0000000..ac9e382 --- /dev/null +++ b/examples/ex05_concurrency_policy.cpp @@ -0,0 +1,47 @@ +#include +#include +#include +#include + +import mcpplibs.primitives; + +using namespace mcpplibs::primitives; + +int main() { + // Point 5: Use atomic concurrency policy and verify concurrent consistency. + using atomic_t = + primitive; + + auto const lhs = atomic_t{12}; + auto const rhs = atomic_t{30}; + + std::atomic mismatch_count{0}; + std::vector workers; + workers.reserve(4); + + // Run many concurrent dispatches. Any error or wrong value is counted. + for (int i = 0; i < 4; ++i) { + workers.emplace_back([&]() { + for (int n = 0; n < 10000; ++n) { + auto const out = operations::add(lhs, rhs); + if (!out.has_value() || out->value() != 42) { + mismatch_count.fetch_add(1, std::memory_order_relaxed); + } + } + }); + } + + for (auto &worker : workers) { + worker.join(); + } + + // A non-zero mismatch count indicates unexpected behavior under concurrency. + if (mismatch_count.load(std::memory_order_relaxed) != 0) { + std::cerr << "atomic policy path mismatch\n"; + return 1; + } + + std::cout << "concurrency_policy demo passed\n"; + return 0; +} diff --git a/examples/ex06_custom_underlying.cpp b/examples/ex06_custom_underlying.cpp new file mode 100644 index 0000000..f521627 --- /dev/null +++ b/examples/ex06_custom_underlying.cpp @@ -0,0 +1,86 @@ +#include + +import mcpplibs.primitives; + +using namespace mcpplibs::primitives; + +// Point 6 / Step 1: Define custom domain value types. +struct UserInteger { + int value; +}; + +struct NonNegativeInt { + int value; +}; + +// Point 6 / Step 2: Register underlying::traits for UserInteger. +// This type has a full int bridge and accepts all reps. +template <> struct mcpplibs::primitives::underlying::traits { + using value_type = UserInteger; + using rep_type = int; + + static constexpr bool enabled = true; + static constexpr auto kind = category::integer; + + static constexpr auto to_rep(value_type value) noexcept -> rep_type { + return value.value; + } + + static constexpr auto from_rep(rep_type value) noexcept -> value_type { + return UserInteger{value}; + } + + static constexpr auto is_valid_rep(rep_type) noexcept -> bool { return true; } +}; + +// Point 6 / Step 3: Register underlying::traits for NonNegativeInt. +// This one demonstrates custom rep validation (rep must be non-negative). +template <> struct mcpplibs::primitives::underlying::traits { + using value_type = NonNegativeInt; + using rep_type = int; + + static constexpr bool enabled = true; + static constexpr auto kind = category::integer; + + static constexpr auto to_rep(value_type value) noexcept -> rep_type { + return value.value; + } + + static constexpr auto from_rep(rep_type value) noexcept -> value_type { + return NonNegativeInt{value}; + } + + static constexpr auto is_valid_rep(rep_type value) noexcept -> bool { + return value >= 0; + } +}; + +int main() { + // Point 6 / Step 4A: Call operations on UserInteger custom underlying. + using user_t = + primitive; + + auto const ua = user_t{UserInteger{40}}; + auto const ub = user_t{UserInteger{2}}; + auto const user_result = operations::add(ua, ub); + if (!user_result.has_value() || user_result->value() != 42) { + std::cerr << "custom UserInteger underlying failed\n"; + return 1; + } + + // Point 6 / Step 4B: Demonstrate dispatcher rejecting invalid reps. + // Here -1 fails is_valid_rep and is mapped to domain_error. + using nonneg_t = primitive; + auto const na = nonneg_t{NonNegativeInt{-1}}; + auto const nb = nonneg_t{NonNegativeInt{2}}; + auto const nonneg_result = operations::add(na, nb); + if (nonneg_result.has_value() || + nonneg_result.error() != policy::error::kind::domain_error) { + std::cerr << "invalid rep should be rejected by dispatcher\n"; + return 1; + } + + std::cout << "custom underlying demo passed\n"; + return 0; +} diff --git a/examples/ex07_custom_policy.cpp b/examples/ex07_custom_policy.cpp new file mode 100644 index 0000000..b793012 --- /dev/null +++ b/examples/ex07_custom_policy.cpp @@ -0,0 +1,141 @@ +#include +#include +#include + +import mcpplibs.primitives; +import mcpplibs.primitives.operations.invoker; + +using namespace mcpplibs::primitives; + +namespace demo { +// Point 7 / Step 1: Define one custom tag for each policy dimension. +struct custom_value {}; +struct custom_type {}; +struct custom_error {}; +struct custom_concurrency {}; +} // namespace demo + +// Point 7 / Step 2: Register tags into policy::traits. +template <> struct mcpplibs::primitives::policy::traits { + using policy_type = demo::custom_value; + static constexpr bool enabled = true; + static constexpr auto kind = category::value; +}; + +template <> struct mcpplibs::primitives::policy::traits { + using policy_type = demo::custom_type; + static constexpr bool enabled = true; + static constexpr auto kind = category::type; +}; + +template <> struct mcpplibs::primitives::policy::traits { + using policy_type = demo::custom_error; + static constexpr bool enabled = true; + static constexpr auto kind = category::error; +}; + +template <> +struct mcpplibs::primitives::policy::traits { + using policy_type = demo::custom_concurrency; + static constexpr bool enabled = true; + static constexpr auto kind = category::concurrency; +}; + +// Point 7 / Step 3A: Implement custom type handler. +// Always allow and negotiate through std::common_type_t. +template +struct mcpplibs::primitives::policy::type::handler { + static constexpr bool enabled = true; + static constexpr bool allowed = true; + static constexpr unsigned diagnostic_id = 0; + using common_rep = std::common_type_t; +}; + +// Point 7 / Step 3B: Implement custom concurrency handler. +template +struct mcpplibs::primitives::policy::concurrency::handler< + demo::custom_concurrency, OpTag, CommonRep, ErrorPayload> { + static constexpr bool enabled = true; + using injection_type = mcpplibs::primitives::policy::concurrency::injection; + using result_type = std::expected; + + static constexpr auto inject() noexcept -> injection_type { + injection_type out{}; + out.fence_before = true; + out.fence_after = false; + return out; + } +}; + +// Point 7 / Step 3C: Implement custom value handler. +// Complex point: finalize() post-processes decision and adjusts output. +template +struct mcpplibs::primitives::policy::value::handler { + static constexpr bool enabled = true; + static constexpr bool may_adjust_value = true; + using decision_type = + mcpplibs::primitives::policy::value::decision; + using result_type = std::expected; + + static constexpr auto + finalize(decision_type decision, + mcpplibs::primitives::policy::concurrency::injection const &) + -> decision_type { + if (decision.has_value) { + decision.value = static_cast(decision.value + 1); + } + return decision; + } +}; + +// Point 7 / Step 3D: Provide binding for custom value policy + Addition. +// Without this specialization, runtime::run_value static_assert will fail. +template +struct mcpplibs::primitives::operations::runtime::op_binding< + mcpplibs::primitives::operations::Addition, demo::custom_value, CommonRep> { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> mcpplibs::primitives::policy::value::decision { + mcpplibs::primitives::policy::value::decision out{}; + out.has_value = true; + out.value = static_cast(lhs + rhs); + return out; + } +}; + +// Point 7 / Step 3E: Implement custom error handler. +template +struct mcpplibs::primitives::policy::error::handler { + static constexpr bool enabled = true; + using request_type = mcpplibs::primitives::policy::error::request; + using result_type = std::expected; + + static constexpr auto resolve(request_type const &) -> result_type { + return std::unexpected(policy::error::kind::unspecified); + } +}; + +int main() { + // Point 7 / Step 4: Compose all custom tags and execute a call path. + using custom_t = primitive; + + auto const lhs = custom_t{20}; + auto const rhs = custom_t{21}; + auto const result = operations::add(lhs, rhs); + + if (!result.has_value() || result->value() != 42) { + std::cerr << "custom policy pipeline failed\n"; + return 1; + } + + std::cout << "custom policy demo passed\n"; + return 0; +} diff --git a/examples/ex08_custom_operation.cpp b/examples/ex08_custom_operation.cpp new file mode 100644 index 0000000..902a667 --- /dev/null +++ b/examples/ex08_custom_operation.cpp @@ -0,0 +1,103 @@ +#include + +import mcpplibs.primitives; +import mcpplibs.primitives.operations.invoker; + +using namespace mcpplibs::primitives; + +namespace demo_ops { +// Point 8 / Step 1: Define custom operation tags. +struct Average {}; +struct GreaterThan {}; +struct BitAnd {}; +} // namespace demo_ops + +// Point 8 / Step 2: Register operation traits for +// arithmetic/comparison/bitwise. +template <> struct mcpplibs::primitives::operations::traits { + using op_tag = demo_ops::Average; + static constexpr bool enabled = true; + static constexpr auto arity = dimension::binary; + static constexpr auto capability_mask = capability::arithmetic; +}; + +template <> +struct mcpplibs::primitives::operations::traits { + using op_tag = demo_ops::GreaterThan; + static constexpr bool enabled = true; + static constexpr auto arity = dimension::binary; + static constexpr auto capability_mask = capability::comparison; +}; + +template <> struct mcpplibs::primitives::operations::traits { + using op_tag = demo_ops::BitAnd; + static constexpr bool enabled = true; + static constexpr auto arity = dimension::binary; + static constexpr auto capability_mask = capability::bitwise; +}; + +// Point 8 / Step 3: Provide runtime op_binding for each new operation. +// Complex point: these specializations plug directly into run_value dispatch. +template +struct mcpplibs::primitives::operations::runtime::op_binding< + demo_ops::Average, policy::value::checked, CommonRep> { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value::decision { + policy::value::decision out{}; + out.has_value = true; + out.value = static_cast((lhs + rhs) / 2); + return out; + } +}; + +template +struct mcpplibs::primitives::operations::runtime::op_binding< + demo_ops::GreaterThan, policy::value::checked, CommonRep> { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value::decision { + policy::value::decision out{}; + out.has_value = true; + out.value = static_cast(lhs > rhs); + return out; + } +}; + +template +struct mcpplibs::primitives::operations::runtime::op_binding< + demo_ops::BitAnd, policy::value::checked, CommonRep> { + static constexpr bool enabled = true; + + static constexpr auto apply(CommonRep lhs, CommonRep rhs) + -> policy::value::decision { + policy::value::decision out{}; + out.has_value = true; + out.value = static_cast(lhs & rhs); + return out; + } +}; + +int main() { + // Point 8 / Step 4: Invoke each custom operation through operations::apply. + using value_t = + primitive; + + auto const a = value_t{10}; + auto const b = value_t{6}; + + auto const avg = operations::apply(a, b); + auto const gt = operations::apply(a, b); + auto const band = operations::apply(a, b); + + if (!avg.has_value() || !gt.has_value() || !band.has_value()) { + std::cerr << "custom operation dispatch failed\n"; + return 1; + } + + std::cout << "avg=" << avg->value() << ", gt=" << gt->value() + << ", bitand=" << band->value() << '\n'; + return 0; +} From ae82f4e681a2531664de0f6bec9079c8d50097b2 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 23:49:08 +0800 Subject: [PATCH 48/57] Add CMake configuration for primitives examples --- examples/CMakeLists.txt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 examples/CMakeLists.txt diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..b5b0ea1 --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,16 @@ +set(PRIMITIVES_EXAMPLE_SOURCES + ex01_default_arithmetic.cpp + ex02_type_policy.cpp + ex03_value_policy.cpp + ex04_error_policy.cpp + ex05_concurrency_policy.cpp + ex06_custom_underlying.cpp + ex07_custom_policy.cpp + ex08_custom_operation.cpp +) + +foreach(example_source IN LISTS PRIMITIVES_EXAMPLE_SOURCES) + get_filename_component(example_name ${example_source} NAME_WE) + add_executable(${example_name} ${example_source}) + target_link_libraries(${example_name} PRIVATE mcpplibs-primitives) +endforeach() From 531ac7b790141fe195c61d0fbab6d46055185046 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 23:50:21 +0800 Subject: [PATCH 49/57] Remove temporarily disabled basic example --- examples/basic.cpp | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 examples/basic.cpp diff --git a/examples/basic.cpp b/examples/basic.cpp deleted file mode 100644 index 42abff4..0000000 --- a/examples/basic.cpp +++ /dev/null @@ -1,6 +0,0 @@ -/* - * Example temporarily disabled while operations module is rebuilt. - * Previous demo code intentionally removed from compilation path. - */ - -int main() { return 0; } From 366fc71197d342cd44cb522278101cc871be3543 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 23:50:26 +0800 Subject: [PATCH 50/57] Refactor examples to use a loop for target creation --- examples/xmake.lua | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/examples/xmake.lua b/examples/xmake.lua index 74fcb7a..7e5ecb3 100644 --- a/examples/xmake.lua +++ b/examples/xmake.lua @@ -2,8 +2,21 @@ add_rules("mode.debug", "mode.release") set_languages("c++23") -target("basic") - set_kind("binary") - add_files("basic.cpp") - add_deps("mcpplibs-primitives") - set_policy("build.c++.modules", true) +local examples = { + "ex01_default_arithmetic", + "ex02_type_policy", + "ex03_value_policy", + "ex04_error_policy", + "ex05_concurrency_policy", + "ex06_custom_underlying", + "ex07_custom_policy", + "ex08_custom_operation" +} + +for _, name in ipairs(examples) do + target(name) + set_kind("binary") + add_files(name .. ".cpp") + add_deps("mcpplibs-primitives") + set_policy("build.c++.modules", true) +end From c09eb9fb6bec019e9d99b3a7b0eb0c960cc321f3 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 23:50:30 +0800 Subject: [PATCH 51/57] Update CMake to include examples as subdirectory --- CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 17dd694..2acf57e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,8 +60,7 @@ target_include_directories(mcpplibs-primitives PUBLIC ) # Examples -add_executable(basic examples/basic.cpp) -target_link_libraries(basic PRIVATE mcpplibs-primitives) +add_subdirectory(examples EXCLUDE_FROM_ALL) # Testing option(BUILD_TESTING "Build the testing directories" ON) From 94d1c5fc7fbf70518759430b2a53ae45580b5220 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Tue, 17 Mar 2026 23:50:56 +0800 Subject: [PATCH 52/57] docs: Bump project version to 0.2.0 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2acf57e..afca2fc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) -project(mcpplibs-primitives VERSION 0.1.0 LANGUAGES CXX) +project(mcpplibs-primitives VERSION 0.2.0 LANGUAGES CXX) find_package(Threads REQUIRED) From 3d78985a8acbe3a042bceb84cebbbb6252053175 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Wed, 18 Mar 2026 00:06:17 +0800 Subject: [PATCH 53/57] fix: Fix ci test --- examples/xmake.lua | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/examples/xmake.lua b/examples/xmake.lua index 7e5ecb3..4dd4589 100644 --- a/examples/xmake.lua +++ b/examples/xmake.lua @@ -13,6 +13,13 @@ local examples = { "ex08_custom_operation" } +-- CI compatibility alias: keep `xmake run basic` working. +target("basic") + set_kind("binary") + add_files("ex01_default_arithmetic.cpp") + add_deps("mcpplibs-primitives") + set_policy("build.c++.modules", true) + for _, name in ipairs(examples) do target(name) set_kind("binary") From 4609c33175493e5a7596b2ffaf404558a436b531 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Wed, 18 Mar 2026 00:06:30 +0800 Subject: [PATCH 54/57] refactor: Rename `kind` to `code` in error handling structures --- src/operations/dispatcher.cppm | 2 +- src/operations/invoker.cppm | 4 ++-- src/policy/handler.cppm | 2 +- src/policy/impl.cppm | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/operations/dispatcher.cppm b/src/operations/dispatcher.cppm index d8ff8e9..64a5e7d 100644 --- a/src/operations/dispatcher.cppm +++ b/src/operations/dispatcher.cppm @@ -132,7 +132,7 @@ constexpr auto dispatch(Lhs const &lhs, Rhs const &rhs) !underlying::traits::is_valid_rep( rhs_rep_raw)) { policy::error::request request{}; - request.kind = policy::error::kind::domain_error; + request.code = policy::error::kind::domain_error; request.reason = "invalid underlying representation"; return runtime::resolve_error(request); diff --git a/src/operations/invoker.cppm b/src/operations/invoker.cppm index c496d5a..a38572d 100644 --- a/src/operations/invoker.cppm +++ b/src/operations/invoker.cppm @@ -27,7 +27,7 @@ constexpr auto make_error(policy::error::kind kind, char const *reason, -> policy::value::decision { policy::value::decision out{}; out.has_value = false; - out.error.kind = kind; + out.error.code = kind; out.error.reason = reason; out.error.lhs_value = lhs; out.error.rhs_value = rhs; @@ -385,7 +385,7 @@ struct op_binding { -> policy::value::decision { policy::value::decision out{}; out.has_value = false; - out.error.kind = policy::error::kind::unspecified; + out.error.code = policy::error::kind::unspecified; out.error.reason = "operation binding is not implemented"; return out; } diff --git a/src/policy/handler.cppm b/src/policy/handler.cppm index 93fd197..bacf8eb 100644 --- a/src/policy/handler.cppm +++ b/src/policy/handler.cppm @@ -28,7 +28,7 @@ enum class kind : unsigned char { }; template struct request { - kind kind = kind::none; + kind code = kind::none; std::string_view reason{}; std::optional lhs_value{}; std::optional rhs_value{}; diff --git a/src/policy/impl.cppm b/src/policy/impl.cppm index dc6a360..4f04e4a 100644 --- a/src/policy/impl.cppm +++ b/src/policy/impl.cppm @@ -280,7 +280,7 @@ struct error::handler { static constexpr auto resolve(request_type const &request) -> result_type { return std::unexpected( - details::to_error_payload(request.kind)); + details::to_error_payload(request.code)); } }; From 212c9c66eda8816e963f6b6a8dce978568267a6b Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Wed, 18 Mar 2026 00:24:14 +0800 Subject: [PATCH 55/57] refactor: Add missing `#include ` in test files --- tests/basic/test_operations.cpp | 2 +- tests/basic/test_policies.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/basic/test_operations.cpp b/tests/basic/test_operations.cpp index 6deaec9..e321cb3 100644 --- a/tests/basic/test_operations.cpp +++ b/tests/basic/test_operations.cpp @@ -1,5 +1,5 @@ #include - +#include #include #include #include diff --git a/tests/basic/test_policies.cpp b/tests/basic/test_policies.cpp index 06bed67..763073f 100644 --- a/tests/basic/test_policies.cpp +++ b/tests/basic/test_policies.cpp @@ -1,5 +1,5 @@ #include - +#include import mcpplibs.primitives; using namespace mcpplibs::primitives; From 42aadf8068960e5a5a19bab8a5ff060ad1ee7b1c Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Wed, 18 Mar 2026 00:24:25 +0800 Subject: [PATCH 56/57] fix: Properly construct `std::runtime_error` with `request.reason` and add missing include --- src/policy/impl.cppm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/policy/impl.cppm b/src/policy/impl.cppm index 4f04e4a..ff2c8a3 100644 --- a/src/policy/impl.cppm +++ b/src/policy/impl.cppm @@ -1,5 +1,5 @@ module; - +#include #include #include #include @@ -266,7 +266,7 @@ struct error::handler { using result_type = std::expected; static auto resolve(request_type const &request) -> result_type { - throw std::runtime_error(request.reason.data()); + throw std::runtime_error(std::string{request.reason}); } }; From d1006cb60c735eae7ef6d61d0870a1665fbcea2d Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Wed, 18 Mar 2026 00:24:34 +0800 Subject: [PATCH 57/57] fix: Use direct initialization for `out.value` in comparison operations --- src/operations/invoker.cppm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/operations/invoker.cppm b/src/operations/invoker.cppm index a38572d..db1788b 100644 --- a/src/operations/invoker.cppm +++ b/src/operations/invoker.cppm @@ -198,7 +198,7 @@ constexpr auto compare_equal(T lhs, T rhs) -> policy::value::decision { policy::value::decision out{}; if constexpr (requires { lhs == rhs; }) { out.has_value = true; - out.value = static_cast(lhs == rhs); + out.value = T{lhs == rhs}; return out; } @@ -212,7 +212,7 @@ constexpr auto compare_not_equal(T lhs, T rhs) -> policy::value::decision { policy::value::decision out{}; if constexpr (requires { lhs != rhs; }) { out.has_value = true; - out.value = static_cast(lhs != rhs); + out.value = T{lhs != rhs}; return out; }