-
Notifications
You must be signed in to change notification settings - Fork 0
Add compound assignment and ThreeWayCompare operations with tests #11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
f8da408
ad91b25
a67d35a
17b46dd
6b9ac67
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,9 @@ | ||
| module; | ||
|
|
||
| #include <compare> | ||
| #include <expected> | ||
| #include <type_traits> | ||
| #include <utility> | ||
|
|
||
| export module mcpplibs.primitives.operations.operators; | ||
|
|
||
|
|
@@ -10,9 +13,51 @@ import mcpplibs.primitives.operations.impl; | |
| import mcpplibs.primitives.primitive.impl; | ||
| import mcpplibs.primitives.primitive.traits; | ||
| import mcpplibs.primitives.policy.handler; | ||
| import mcpplibs.primitives.underlying.traits; | ||
|
|
||
| export namespace mcpplibs::primitives::operations { | ||
|
|
||
| namespace details { | ||
| template <typename CommonRep, typename = void> struct three_way_ordering { | ||
| using type = std::strong_ordering; | ||
| }; | ||
|
|
||
| template <typename CommonRep> | ||
| struct three_way_ordering< | ||
| CommonRep, | ||
| std::void_t<decltype(std::declval<CommonRep const &>() <=> | ||
| std::declval<CommonRep const &>())>> { | ||
| using type = std::remove_cvref_t<decltype(std::declval<CommonRep const &>() <=> | ||
| std::declval<CommonRep const &>())>; | ||
| }; | ||
|
|
||
| template <typename CommonRep> | ||
| using three_way_ordering_t = typename three_way_ordering<CommonRep>::type; | ||
|
|
||
| template <typename Ordering, typename CommonRep> | ||
| constexpr auto decode_three_way_code(CommonRep const &code) -> Ordering { | ||
| if (code == static_cast<CommonRep>(0)) { | ||
| return Ordering::less; | ||
| } | ||
| if (code == static_cast<CommonRep>(2)) { | ||
| return Ordering::greater; | ||
| } | ||
|
|
||
| if constexpr (std::is_same_v<Ordering, std::partial_ordering>) { | ||
| if (code == static_cast<CommonRep>(3)) { | ||
| return std::partial_ordering::unordered; | ||
| } | ||
| return std::partial_ordering::equivalent; | ||
| } | ||
|
|
||
| if constexpr (std::is_same_v<Ordering, std::strong_ordering>) { | ||
| return std::strong_ordering::equal; | ||
| } | ||
|
|
||
| return Ordering::equivalent; | ||
| } | ||
| } // namespace details | ||
|
|
||
| template <operation OpTag, primitive_instance Lhs, primitive_instance Rhs, | ||
| typename ErrorPayload = policy::error::kind> | ||
| using primitive_dispatch_result_t = std::expected< | ||
|
|
@@ -21,6 +66,14 @@ using primitive_dispatch_result_t = std::expected< | |
| typename mcpplibs::primitives::traits::primitive_traits<Lhs>::policies>, | ||
| ErrorPayload>; | ||
|
|
||
| template <primitive_instance Lhs, primitive_instance Rhs, | ||
| typename ErrorPayload = policy::error::kind> | ||
| using three_way_dispatch_result_t = std::expected< | ||
| details::three_way_ordering_t< | ||
| typename dispatcher_meta<ThreeWayCompare, Lhs, Rhs, | ||
| ErrorPayload>::common_rep>, | ||
| ErrorPayload>; | ||
|
|
||
| template <operation OpTag, primitive_instance Lhs, primitive_instance Rhs, | ||
| typename ErrorPayload = policy::error::kind> | ||
| constexpr auto apply(Lhs const &lhs, Rhs const &rhs) | ||
|
|
@@ -79,6 +132,70 @@ constexpr auto not_equal(Lhs const &lhs, Rhs const &rhs) | |
| return apply<NotEqual, Lhs, Rhs, ErrorPayload>(lhs, rhs); | ||
| } | ||
|
|
||
| template <primitive_instance Lhs, primitive_instance Rhs, | ||
| typename ErrorPayload = policy::error::kind> | ||
| constexpr auto three_way_compare(Lhs const &lhs, Rhs const &rhs) | ||
| -> three_way_dispatch_result_t<Lhs, Rhs, ErrorPayload> { | ||
| using common_rep = | ||
| typename dispatcher_meta<ThreeWayCompare, Lhs, Rhs, | ||
| ErrorPayload>::common_rep; | ||
| using ordering = | ||
| typename three_way_dispatch_result_t<Lhs, Rhs, ErrorPayload>::value_type; | ||
|
|
||
| auto const raw = dispatch<ThreeWayCompare, Lhs, Rhs, ErrorPayload>(lhs, rhs); | ||
| if (!raw.has_value()) { | ||
| return std::unexpected(raw.error()); | ||
| } | ||
|
|
||
| return details::decode_three_way_code<ordering, common_rep>(*raw); | ||
| } | ||
|
|
||
| template <operation OpTag, primitive_instance Lhs, primitive_instance Rhs, | ||
| typename ErrorPayload = policy::error::kind> | ||
| constexpr auto apply_assign(Lhs &lhs, Rhs const &rhs) | ||
| -> primitive_dispatch_result_t<OpTag, Lhs, Rhs, ErrorPayload> { | ||
| using lhs_value_type = | ||
| typename mcpplibs::primitives::traits::primitive_traits<Lhs>::value_type; | ||
| using lhs_rep = typename underlying::traits<lhs_value_type>::rep_type; | ||
|
|
||
| auto out = apply<OpTag, Lhs, Rhs, ErrorPayload>(lhs, rhs); | ||
| if (!out.has_value()) { | ||
| return std::unexpected(out.error()); | ||
| } | ||
|
|
||
| auto const assigned_rep = static_cast<lhs_rep>(out->load()); | ||
| lhs.store(underlying::traits<lhs_value_type>::from_rep(assigned_rep)); | ||
| return out; | ||
| } | ||
|
Comment on lines
+161
to
+169
|
||
|
|
||
| template <primitive_instance Lhs, primitive_instance Rhs, | ||
| typename ErrorPayload = policy::error::kind> | ||
| constexpr auto add_assign(Lhs &lhs, Rhs const &rhs) | ||
| -> primitive_dispatch_result_t<Addition, Lhs, Rhs, ErrorPayload> { | ||
| return apply_assign<Addition, Lhs, Rhs, ErrorPayload>(lhs, rhs); | ||
| } | ||
|
|
||
| template <primitive_instance Lhs, primitive_instance Rhs, | ||
| typename ErrorPayload = policy::error::kind> | ||
| constexpr auto sub_assign(Lhs &lhs, Rhs const &rhs) | ||
| -> primitive_dispatch_result_t<Subtraction, Lhs, Rhs, ErrorPayload> { | ||
| return apply_assign<Subtraction, Lhs, Rhs, ErrorPayload>(lhs, rhs); | ||
| } | ||
|
|
||
| template <primitive_instance Lhs, primitive_instance Rhs, | ||
| typename ErrorPayload = policy::error::kind> | ||
| constexpr auto mul_assign(Lhs &lhs, Rhs const &rhs) | ||
| -> primitive_dispatch_result_t<Multiplication, Lhs, Rhs, ErrorPayload> { | ||
| return apply_assign<Multiplication, Lhs, Rhs, ErrorPayload>(lhs, rhs); | ||
| } | ||
|
|
||
| template <primitive_instance Lhs, primitive_instance Rhs, | ||
| typename ErrorPayload = policy::error::kind> | ||
| constexpr auto div_assign(Lhs &lhs, Rhs const &rhs) | ||
| -> primitive_dispatch_result_t<Division, Lhs, Rhs, ErrorPayload> { | ||
| return apply_assign<Division, Lhs, Rhs, ErrorPayload>(lhs, rhs); | ||
| } | ||
|
|
||
| } // namespace mcpplibs::primitives::operations | ||
|
|
||
| export namespace mcpplibs::primitives::operators { | ||
|
|
@@ -127,4 +244,41 @@ constexpr auto operator!=(Lhs const &lhs, Rhs const &rhs) | |
| return operations::not_equal(lhs, rhs); | ||
| } | ||
|
|
||
| } // namespace mcpplibs::primitives::operators | ||
| template <operations::primitive_instance Lhs, | ||
| operations::primitive_instance Rhs> | ||
| constexpr auto operator<=>(Lhs const &lhs, Rhs const &rhs) | ||
| -> operations::three_way_dispatch_result_t<Lhs, Rhs> { | ||
| return operations::three_way_compare(lhs, rhs); | ||
| } | ||
|
|
||
| template <operations::primitive_instance Lhs, | ||
| operations::primitive_instance Rhs> | ||
| constexpr auto operator+=(Lhs &lhs, Rhs const &rhs) | ||
| -> operations::primitive_dispatch_result_t<operations::Addition, Lhs, Rhs> { | ||
| return operations::add_assign(lhs, rhs); | ||
| } | ||
|
|
||
| template <operations::primitive_instance Lhs, | ||
| operations::primitive_instance Rhs> | ||
| constexpr auto operator-=(Lhs &lhs, Rhs const &rhs) | ||
| -> operations::primitive_dispatch_result_t<operations::Subtraction, Lhs, | ||
| Rhs> { | ||
| return operations::sub_assign(lhs, rhs); | ||
| } | ||
|
|
||
| template <operations::primitive_instance Lhs, | ||
| operations::primitive_instance Rhs> | ||
| constexpr auto operator*=(Lhs &lhs, Rhs const &rhs) | ||
| -> operations::primitive_dispatch_result_t<operations::Multiplication, Lhs, | ||
| Rhs> { | ||
| return operations::mul_assign(lhs, rhs); | ||
| } | ||
|
|
||
| template <operations::primitive_instance Lhs, | ||
| operations::primitive_instance Rhs> | ||
| constexpr auto operator/=(Lhs &lhs, Rhs const &rhs) | ||
| -> operations::primitive_dispatch_result_t<operations::Division, Lhs, Rhs> { | ||
| return operations::div_assign(lhs, rhs); | ||
| } | ||
|
|
||
| } // namespace mcpplibs::primitives::operators | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
apply_assignconverts the computedcommon_represult tolhs_repviastatic_castand then stores it intolhs. If the negotiatedcommon_repis wider thanlhs_rep(e.g.policy::type::compatiblewithint8_t += int), this can silently narrow/wrap (or become implementation-defined for signed) and bypass the library’s checked-value overflow/underflow guarantees. Consider either constrainingapply_assignto cases wherecommon_rep==lhs_rep, or validating that the result is representable inlhs_value_type(and returning an overflow/underflow error) before mutatinglhs.