From fff80c31c610ea20b5e68d943300265f24f1fb47 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee <1115306170@qq.com> Date: Mon, 30 Mar 2026 09:14:32 +0800 Subject: [PATCH 1/4] feat: Implement comparison operators for LessThan, GreaterThan, LessThanOrEqual, and GreaterThanOrEqual Signed-off-by: FrozenlemonTee <1115306170@qq.com> --- src/operations/impl.cppm | 36 ++++++ src/operations/invoker.cppm | 234 ++++++++++++++++++++++++++++++++++ src/operations/operators.cppm | 172 +++++++++++++++++++++++++ 3 files changed, 442 insertions(+) diff --git a/src/operations/impl.cppm b/src/operations/impl.cppm index 60ac12d..d13495f 100644 --- a/src/operations/impl.cppm +++ b/src/operations/impl.cppm @@ -27,6 +27,10 @@ struct BitwiseXor {}; struct Equal {}; struct NotEqual {}; struct ThreeWayCompare {}; +struct LessThan {}; +struct GreaterThan {}; +struct LessThanOrEqual {}; +struct GreaterThanOrEqual {}; template <> struct traits { using op_tag = Increment; @@ -172,4 +176,36 @@ template <> struct traits { static constexpr auto capability_mask = capability::comparison; }; +template <> struct traits { + using op_tag = LessThan; + + static constexpr bool enabled = true; + static constexpr auto arity = dimension::binary; + static constexpr auto capability_mask = capability::comparison; +}; + +template <> struct traits { + using op_tag = GreaterThan; + + static constexpr bool enabled = true; + static constexpr auto arity = dimension::binary; + static constexpr auto capability_mask = capability::comparison; +}; + +template <> struct traits { + using op_tag = LessThanOrEqual; + + static constexpr bool enabled = true; + static constexpr auto arity = dimension::binary; + static constexpr auto capability_mask = capability::comparison; +}; + +template <> struct traits { + using op_tag = GreaterThanOrEqual; + + static constexpr bool enabled = true; + static constexpr auto arity = dimension::binary; + static constexpr auto capability_mask = capability::comparison; +}; + } // namespace mcpplibs::primitives::operations diff --git a/src/operations/invoker.cppm b/src/operations/invoker.cppm index 7bd53fa..8fc6378 100644 --- a/src/operations/invoker.cppm +++ b/src/operations/invoker.cppm @@ -487,6 +487,120 @@ constexpr auto compare_three_way(T lhs, T rhs) -> policy::value::decision { "common type"); } +template +constexpr auto compare_less_than(T lhs, T rhs) -> policy::value::decision { + policy::value::decision out{}; + if constexpr (!(requires { T{0}; T{1}; T{2}; T{3}; })) { + return make_error( + policy::error::kind::unspecified, + "less than comparison codes are not representable for common type"); + } + + if constexpr (std::same_as, bool>) { + return make_error( + policy::error::kind::unspecified, + "less than comparison is not representable for bool common type"); + } else if constexpr (requires { lhs < rhs; }) { + out.has_value = true; + out.value = T{lhs < rhs}; + return out; + } else if constexpr (requires { lhs <=> rhs; }) { + auto const cmp = lhs <=> rhs; + out.has_value = true; + out.value = T{cmp < 0}; + return out; + } + + return make_error(policy::error::kind::unspecified, + "less than not supported for negotiated common type"); +} + +template +constexpr auto compare_greater_than(T lhs, T rhs) -> policy::value::decision { + policy::value::decision out{}; + if constexpr (!(requires { T{0}; T{1}; T{2}; T{3}; })) { + return make_error( + policy::error::kind::unspecified, + "greater than comparison codes are not representable for common type"); + } + + if constexpr (std::same_as, bool>) { + return make_error( + policy::error::kind::unspecified, + "greater than comparison is not representable for bool common type"); + } else if constexpr (requires { lhs > rhs; }) { + out.has_value = true; + out.value = T{lhs > rhs}; + return out; + } else if constexpr (requires { lhs <=> rhs; }) { + auto const cmp = lhs <=> rhs; + out.has_value = true; + out.value = T{cmp > 0}; + return out; + } + + return make_error(policy::error::kind::unspecified, + "greater than not supported for negotiated common type"); +} + +template +constexpr auto compare_less_than_or_equal(T lhs, T rhs) + -> policy::value::decision { + policy::value::decision out{}; + if constexpr (!(requires { T{0}; T{1}; T{2}; T{3}; })) { + return make_error( + policy::error::kind::unspecified, + "less than or equal comparison codes are not representable for common type"); + } + + if constexpr (std::same_as, bool>) { + return make_error( + policy::error::kind::unspecified, + "less than or equal comparison is not representable for bool common type"); + } else if constexpr (requires { lhs <= rhs; }) { + out.has_value = true; + out.value = T{lhs <= rhs}; + return out; + } else if constexpr (requires { lhs <=> rhs; }) { + auto const cmp = lhs <=> rhs; + out.has_value = true; + out.value = T{cmp <= 0}; + return out; + } + + return make_error(policy::error::kind::unspecified, + "less than or equal not supported for negotiated common type"); +} + +template +constexpr auto compare_greater_than_or_equal(T lhs, T rhs) + -> policy::value::decision { + policy::value::decision out{}; + if constexpr (!(requires { T{0}; T{1}; T{2}; T{3}; })) { + return make_error( + policy::error::kind::unspecified, + "greater than or equal comparison codes are not representable for common type"); + } + + if constexpr (std::same_as, bool>) { + return make_error( + policy::error::kind::unspecified, + "greater than or equal comparison is not representable for bool common type"); + } else if constexpr (requires { lhs >= rhs; }) { + out.has_value = true; + out.value = T{lhs >= rhs}; + return out; + } else if constexpr (requires { lhs <=> rhs; }) { + auto const cmp = lhs <=> rhs; + out.has_value = true; + out.value = T{cmp >= 0}; + return out; + } + + return make_error(policy::error::kind::unspecified, + "greater than or equal not supported for negotiated common type"); +} + template constexpr auto unchecked_add(T lhs, T rhs) -> policy::value::decision { policy::value::decision out{}; @@ -1444,6 +1558,126 @@ 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_less_than(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_less_than(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_less_than(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_greater_than(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_greater_than(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_greater_than(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_less_than_or_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_less_than_or_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_less_than_or_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_greater_than_or_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_greater_than_or_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_greater_than_or_equal(lhs, rhs); + } +}; + template concept op_binding_available = requires { requires operation; diff --git a/src/operations/operators.cppm b/src/operations/operators.cppm index 8aa5976..378cc8a 100644 --- a/src/operations/operators.cppm +++ b/src/operations/operators.cppm @@ -524,6 +524,94 @@ constexpr auto three_way_compare(Lhs const &lhs, Rhs const &rhs) return three_way_compare(bridge_lhs, rhs); } +template +constexpr auto less_than(Lhs const &lhs, Rhs const &rhs) + -> primitive_dispatch_result_t { + return apply(lhs, rhs); +} + +template +constexpr auto less_than(Lhs const &lhs, Rhs const &rhs) + -> mixed_primitive_dispatch_result_t { + return apply(lhs, rhs); +} + +template +constexpr auto less_than(Lhs const &lhs, Rhs const &rhs) + -> flipped_mixed_primitive_dispatch_result_t { + return apply(lhs, rhs); +} + +template +constexpr auto greater_than(Lhs const &lhs, Rhs const &rhs) + -> primitive_dispatch_result_t { + return apply(lhs, rhs); +} + +template +constexpr auto greater_than(Lhs const &lhs, Rhs const &rhs) + -> mixed_primitive_dispatch_result_t { + return apply(lhs, rhs); +} + +template +constexpr auto greater_than(Lhs const &lhs, Rhs const &rhs) + -> flipped_mixed_primitive_dispatch_result_t { + return apply(lhs, rhs); +} + +template +constexpr auto less_than_or_equal(Lhs const &lhs, Rhs const &rhs) + -> primitive_dispatch_result_t { + return apply(lhs, rhs); +} + +template +constexpr auto less_than_or_equal(Lhs const &lhs, Rhs const &rhs) + -> mixed_primitive_dispatch_result_t { + return apply(lhs, rhs); +} + +template +constexpr auto less_than_or_equal(Lhs const &lhs, Rhs const &rhs) + -> flipped_mixed_primitive_dispatch_result_t { + return apply(lhs, rhs); +} + +template +constexpr auto greater_than_or_equal(Lhs const &lhs, Rhs const &rhs) + -> primitive_dispatch_result_t { + return apply(lhs, rhs); +} + +template +constexpr auto greater_than_or_equal(Lhs const &lhs, Rhs const &rhs) + -> mixed_primitive_dispatch_result_t { + return apply(lhs, rhs); +} + +template +constexpr auto greater_than_or_equal(Lhs const &lhs, Rhs const &rhs) + -> flipped_mixed_primitive_dispatch_result_t { + return apply(lhs, rhs); +} + template constexpr auto apply_assign(Lhs &lhs, Rhs const &rhs) @@ -966,6 +1054,90 @@ constexpr auto operator<=>(Lhs const &lhs, Rhs const &rhs) return operations::three_way_compare(lhs, rhs); } +template +constexpr auto operator<(Lhs const &lhs, Rhs const &rhs) + -> operations::primitive_dispatch_result_t { + return operations::less_than(lhs, rhs); +} + +template +constexpr auto operator<(Lhs const &lhs, Rhs const &rhs) + -> operations::mixed_primitive_dispatch_result_t { + return operations::less_than(lhs, rhs); +} + +template +constexpr auto operator<(Lhs const &lhs, Rhs const &rhs) + -> operations::flipped_mixed_primitive_dispatch_result_t { + return operations::less_than(lhs, rhs); +} + +template +constexpr auto operator>(Lhs const &lhs, Rhs const &rhs) + -> operations::primitive_dispatch_result_t { + return operations::greater_than(lhs, rhs); +} + +template +constexpr auto operator>(Lhs const &lhs, Rhs const &rhs) + -> operations::mixed_primitive_dispatch_result_t { + return operations::greater_than(lhs, rhs); +} + +template +constexpr auto operator>(Lhs const &lhs, Rhs const &rhs) + -> operations::flipped_mixed_primitive_dispatch_result_t { + return operations::greater_than(lhs, rhs); +} + +template +constexpr auto operator<=(Lhs const &lhs, Rhs const &rhs) + -> operations::primitive_dispatch_result_t { + return operations::less_than_or_equal(lhs, rhs); +} + +template +constexpr auto operator<=(Lhs const &lhs, Rhs const &rhs) + -> operations::mixed_primitive_dispatch_result_t { + return operations::less_than_or_equal(lhs, rhs); +} + +template +constexpr auto operator<=(Lhs const &lhs, Rhs const &rhs) + -> operations::flipped_mixed_primitive_dispatch_result_t { + return operations::less_than_or_equal(lhs, rhs); +} + +template +constexpr auto operator>=(Lhs const &lhs, Rhs const &rhs) + -> operations::primitive_dispatch_result_t { + return operations::greater_than_or_equal(lhs, rhs); +} + +template +constexpr auto operator>=(Lhs const &lhs, Rhs const &rhs) + -> operations::mixed_primitive_dispatch_result_t { + return operations::greater_than_or_equal(lhs, rhs); +} + +template +constexpr auto operator>=(Lhs const &lhs, Rhs const &rhs) + -> operations::flipped_mixed_primitive_dispatch_result_t { + return operations::greater_than_or_equal(lhs, rhs); +} + template constexpr auto operator+=(Lhs &lhs, Rhs const &rhs) From b602a8412628a7eea01b094b197d2dcefe6b292f Mon Sep 17 00:00:00 2001 From: FrozenlemonTee <1115306170@qq.com> Date: Mon, 30 Mar 2026 09:14:39 +0800 Subject: [PATCH 2/4] test: Add unit tests for comparison operators in operations module Signed-off-by: FrozenlemonTee <1115306170@qq.com> --- .../operators/test_framework_and_compound.cpp | 191 ++++++++++++++++++ 1 file changed, 191 insertions(+) diff --git a/tests/basic/operations/operators/test_framework_and_compound.cpp b/tests/basic/operations/operators/test_framework_and_compound.cpp index 8200afa..ff69c84 100644 --- a/tests/basic/operations/operators/test_framework_and_compound.cpp +++ b/tests/basic/operations/operators/test_framework_and_compound.cpp @@ -380,3 +380,194 @@ TEST(OperationsTest, EXPECT_EQ(add_result.error(), policy::error::kind::overflow); EXPECT_EQ(value.load(), static_cast(32000)); } + +TEST(OperationsTest, LessThanReturnsCorrectResultForIntegers) { + using namespace mcpplibs::primitives::operators; + using value_t = + primitive; + + auto const less_result = operations::less_than(value_t{3}, value_t{7}); + auto const greater_result = operations::less_than(value_t{9}, value_t{1}); + auto const equal_result = operations::less_than(value_t{5}, value_t{5}); + + ASSERT_TRUE(less_result.has_value()); + ASSERT_TRUE(greater_result.has_value()); + ASSERT_TRUE(equal_result.has_value()); + EXPECT_EQ(less_result->value(), 1); + EXPECT_EQ(greater_result->value(), 0); + EXPECT_EQ(equal_result->value(), 0); +} + +TEST(OperationsTest, GreaterThanReturnsCorrectResultForIntegers) { + using namespace mcpplibs::primitives::operators; + using value_t = + primitive; + + auto const less_result = operations::greater_than(value_t{3}, value_t{7}); + auto const greater_result = operations::greater_than(value_t{9}, value_t{1}); + auto const equal_result = operations::greater_than(value_t{5}, value_t{5}); + + ASSERT_TRUE(less_result.has_value()); + ASSERT_TRUE(greater_result.has_value()); + ASSERT_TRUE(equal_result.has_value()); + EXPECT_EQ(less_result->value(), 0); + EXPECT_EQ(greater_result->value(), 1); + EXPECT_EQ(equal_result->value(), 0); +} + +TEST(OperationsTest, LessThanOrEqualReturnsCorrectResultForIntegers) { + using namespace mcpplibs::primitives::operators; + using value_t = + primitive; + + auto const less_result = operations::less_than_or_equal(value_t{3}, value_t{7}); + auto const greater_result = operations::less_than_or_equal(value_t{9}, value_t{1}); + auto const equal_result = operations::less_than_or_equal(value_t{5}, value_t{5}); + + ASSERT_TRUE(less_result.has_value()); + ASSERT_TRUE(greater_result.has_value()); + ASSERT_TRUE(equal_result.has_value()); + EXPECT_EQ(less_result->value(), 1); + EXPECT_EQ(greater_result->value(), 0); + EXPECT_EQ(equal_result->value(), 1); +} + +TEST(OperationsTest, GreaterThanOrEqualReturnsCorrectResultForIntegers) { + using namespace mcpplibs::primitives::operators; + using value_t = + primitive; + + auto const less_result = operations::greater_than_or_equal(value_t{3}, value_t{7}); + auto const greater_result = operations::greater_than_or_equal(value_t{9}, value_t{1}); + auto const equal_result = operations::greater_than_or_equal(value_t{5}, value_t{5}); + + ASSERT_TRUE(less_result.has_value()); + ASSERT_TRUE(greater_result.has_value()); + ASSERT_TRUE(equal_result.has_value()); + EXPECT_EQ(less_result->value(), 0); + EXPECT_EQ(greater_result->value(), 1); + EXPECT_EQ(equal_result->value(), 1); +} + +TEST(OperationsTest, OperatorLessThanDelegatesToDispatcher) { + using namespace mcpplibs::primitives::operators; + using value_t = + primitive; + + auto const result = (value_t{3} < value_t{7}); + + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(result->value(), 1); +} + +TEST(OperationsTest, OperatorGreaterThanDelegatesToDispatcher) { + using namespace mcpplibs::primitives::operators; + using value_t = + primitive; + + auto const result = (value_t{9} > value_t{1}); + + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(result->value(), 1); +} + +TEST(OperationsTest, OperatorLessThanOrEqualDelegatesToDispatcher) { + using namespace mcpplibs::primitives::operators; + using value_t = + primitive; + + auto const less_result = (value_t{3} <= value_t{7}); + auto const equal_result = (value_t{5} <= value_t{5}); + + ASSERT_TRUE(less_result.has_value()); + ASSERT_TRUE(equal_result.has_value()); + EXPECT_EQ(less_result->value(), 1); + EXPECT_EQ(equal_result->value(), 1); +} + +TEST(OperationsTest, OperatorGreaterThanOrEqualDelegatesToDispatcher) { + using namespace mcpplibs::primitives::operators; + using value_t = + primitive; + + auto const greater_result = (value_t{9} >= value_t{1}); + auto const equal_result = (value_t{5} >= value_t{5}); + + ASSERT_TRUE(greater_result.has_value()); + ASSERT_TRUE(equal_result.has_value()); + EXPECT_EQ(greater_result->value(), 1); + EXPECT_EQ(equal_result->value(), 1); +} + +TEST(OperationsTest, FourWayComparisonOperatorsWorkTogether) { + using namespace mcpplibs::primitives::operators; + using value_t = + primitive; + + auto const a = value_t{10}; + auto const b = value_t{20}; + auto const c = value_t{10}; + + auto const a_lt_b = (a < b); + auto const a_gt_b = (a > b); + auto const a_le_b = (a <= b); + auto const a_ge_b = (a >= b); + auto const a_lt_c = (a < c); + auto const a_le_c = (a <= c); + auto const a_ge_c = (a >= c); + auto const a_gt_c = (a > c); + + ASSERT_TRUE(a_lt_b.has_value()); + ASSERT_TRUE(a_gt_b.has_value()); + ASSERT_TRUE(a_le_b.has_value()); + ASSERT_TRUE(a_ge_b.has_value()); + ASSERT_TRUE(a_lt_c.has_value()); + ASSERT_TRUE(a_le_c.has_value()); + ASSERT_TRUE(a_ge_c.has_value()); + ASSERT_TRUE(a_gt_c.has_value()); + + EXPECT_EQ(a_lt_b->value(), 1); + EXPECT_EQ(a_gt_b->value(), 0); + EXPECT_EQ(a_le_b->value(), 1); + EXPECT_EQ(a_ge_b->value(), 0); + EXPECT_EQ(a_lt_c->value(), 0); + EXPECT_EQ(a_le_c->value(), 1); + EXPECT_EQ(a_ge_c->value(), 1); + EXPECT_EQ(a_gt_c->value(), 0); +} + +TEST(OperationsTest, ComparisonOperatorsWorkWithMixedTypes) { + using namespace mcpplibs::primitives::operators; + using value_t = primitive; + + auto const lhs = value_t{10}; + short const rhs = 20; + + auto const lt_lr = lhs < rhs; + auto const lt_rl = rhs < lhs; + auto const gt_lr = lhs > rhs; + auto const gt_rl = rhs > lhs; + auto const le_lr = lhs <= rhs; + auto const le_rl = rhs <= lhs; + auto const ge_lr = lhs >= rhs; + auto const ge_rl = rhs >= lhs; + + ASSERT_TRUE(lt_lr.has_value()); + ASSERT_TRUE(lt_rl.has_value()); + ASSERT_TRUE(gt_lr.has_value()); + ASSERT_TRUE(gt_rl.has_value()); + ASSERT_TRUE(le_lr.has_value()); + ASSERT_TRUE(le_rl.has_value()); + ASSERT_TRUE(ge_lr.has_value()); + ASSERT_TRUE(ge_rl.has_value()); + + EXPECT_EQ(lt_lr->value(), 1); + EXPECT_EQ(lt_rl->value(), 0); + EXPECT_EQ(gt_lr->value(), 0); + EXPECT_EQ(gt_rl->value(), 1); + EXPECT_EQ(le_lr->value(), 1); + EXPECT_EQ(le_rl->value(), 0); + EXPECT_EQ(ge_lr->value(), 0); + EXPECT_EQ(ge_rl->value(), 1); +} From e87a21b4144a158eab385e5045269b06e70d0ebc Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Wed, 1 Apr 2026 18:46:31 +0800 Subject: [PATCH 3/4] refactor: Update error messages for comparison result representation in invoker module --- src/operations/invoker.cppm | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/operations/invoker.cppm b/src/operations/invoker.cppm index 8fc6378..82c6200 100644 --- a/src/operations/invoker.cppm +++ b/src/operations/invoker.cppm @@ -490,10 +490,10 @@ constexpr auto compare_three_way(T lhs, T rhs) -> policy::value::decision { template constexpr auto compare_less_than(T lhs, T rhs) -> policy::value::decision { policy::value::decision out{}; - if constexpr (!(requires { T{0}; T{1}; T{2}; T{3}; })) { + if constexpr (!(requires { T{0}; T{1}; })) { return make_error( policy::error::kind::unspecified, - "less than comparison codes are not representable for common type"); + "less than comparison result is not representable for common type"); } if constexpr (std::same_as, bool>) { @@ -518,10 +518,10 @@ constexpr auto compare_less_than(T lhs, T rhs) -> policy::value::decision { template constexpr auto compare_greater_than(T lhs, T rhs) -> policy::value::decision { policy::value::decision out{}; - if constexpr (!(requires { T{0}; T{1}; T{2}; T{3}; })) { + if constexpr (!(requires { T{0}; T{1}; })) { return make_error( policy::error::kind::unspecified, - "greater than comparison codes are not representable for common type"); + "greater than comparison result is not representable for common type"); } if constexpr (std::same_as, bool>) { @@ -547,10 +547,10 @@ template constexpr auto compare_less_than_or_equal(T lhs, T rhs) -> policy::value::decision { policy::value::decision out{}; - if constexpr (!(requires { T{0}; T{1}; T{2}; T{3}; })) { + if constexpr (!(requires { T{0}; T{1}; })) { return make_error( policy::error::kind::unspecified, - "less than or equal comparison codes are not representable for common type"); + "less than or equal comparison result is not representable for common type"); } if constexpr (std::same_as, bool>) { @@ -576,10 +576,10 @@ template constexpr auto compare_greater_than_or_equal(T lhs, T rhs) -> policy::value::decision { policy::value::decision out{}; - if constexpr (!(requires { T{0}; T{1}; T{2}; T{3}; })) { + if constexpr (!(requires { T{0}; T{1}; })) { return make_error( policy::error::kind::unspecified, - "greater than or equal comparison codes are not representable for common type"); + "greater than or equal comparison result is not representable for common type"); } if constexpr (std::same_as, bool>) { From 4041dddeaac858c4c704ff2a67d8e52a9725f8dd Mon Sep 17 00:00:00 2001 From: FrozenlemonTee Date: Wed, 1 Apr 2026 18:46:53 +0800 Subject: [PATCH 4/4] test: Add unit test for ordered comparison on boolean values --- .../operators/test_framework_and_compound.cpp | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/basic/operations/operators/test_framework_and_compound.cpp b/tests/basic/operations/operators/test_framework_and_compound.cpp index ff69c84..cbc05a2 100644 --- a/tests/basic/operations/operators/test_framework_and_compound.cpp +++ b/tests/basic/operations/operators/test_framework_and_compound.cpp @@ -280,6 +280,26 @@ TEST(OperationsTest, ThreeWayCompareOnBoolReturnsError) { EXPECT_EQ(result.error(), policy::error::kind::unspecified); } +TEST(OperationsTest, OrderedComparisonOnBoolReturnsError) { + using namespace mcpplibs::primitives::operators; + using value_t = + primitive; + + auto const less_result = (value_t{false} < value_t{true}); + auto const greater_result = (value_t{true} > value_t{false}); + auto const less_or_equal_result = (value_t{false} <= value_t{true}); + auto const greater_or_equal_result = (value_t{true} >= value_t{false}); + + ASSERT_FALSE(less_result.has_value()); + ASSERT_FALSE(greater_result.has_value()); + ASSERT_FALSE(less_or_equal_result.has_value()); + ASSERT_FALSE(greater_or_equal_result.has_value()); + EXPECT_EQ(less_result.error(), policy::error::kind::unspecified); + EXPECT_EQ(greater_result.error(), policy::error::kind::unspecified); + EXPECT_EQ(less_or_equal_result.error(), policy::error::kind::unspecified); + EXPECT_EQ(greater_or_equal_result.error(), policy::error::kind::unspecified); +} + TEST(OperationsTest, CompoundAssignmentOperatorsMutateLhsOnSuccess) { using namespace mcpplibs::primitives::operators; using value_t =