From 1fa78de15bb52e0a5e628588f53516eaa91bffe9 Mon Sep 17 00:00:00 2001 From: Rafael Varago Date: Mon, 21 Apr 2025 13:08:03 +0200 Subject: [PATCH 1/2] Add traits for richer-types (with sorted) --- README.md | 5 ++--- include/rvarago/refined.hpp | 13 ++++++++++++- test/refined_test.cpp | 13 ++++++++++++- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 5a12ba1..f23ec87 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,8 @@ With this example, we notice that not all functions need access to the underlyin Although we reported errors via `std::optional` in the example, we can customise it, e.g. to throw an exception with the built-in `even::make` or define a whole user-provided policy. +Furthermore, with `rvarago::refined::traits` we can extend a refinement with properties, such as ordered with the spaceship operator. + ## Requirements C++20 @@ -63,9 +65,6 @@ C++20 This is a header-only library. See [`refined.hpp`](include/rvarago/refined.hpp). -- (Optional) Link against the INTERFACE `rvarago::refined` target in your CMake build. -- Include `rvarago/refined.hpp` - ## Contributing This repository has a [`flake.nix`](./flake.nix) with everything I need for development/CI (toolchain, language server, etc). diff --git a/include/rvarago/refined.hpp b/include/rvarago/refined.hpp index 3d92774..cc3bd1e 100644 --- a/include/rvarago/refined.hpp +++ b/include/rvarago/refined.hpp @@ -6,7 +6,14 @@ namespace rvarago::refined { -template auto Pred, typename... Bases> +// Properties a refinement implements. +struct traits { + // Comparison operations with the spaceship operator. + bool ordered{false}; +}; + +template auto Pred, traits Traits = {}, + typename... Bases> requires((std::is_same_v && ...) && (std::predicate && ...)) class refinement { @@ -57,6 +64,10 @@ class refinement { return refinement{std::move(value)}; } + friend constexpr auto operator<=>(refinement const &, refinement const &) + requires(Traits.ordered) + = default; + private: explicit constexpr refinement(T value) : value_{std::move(value)} {} diff --git a/test/refined_test.cpp b/test/refined_test.cpp index 49e8a08..e4dc1c8 100644 --- a/test/refined_test.cpp +++ b/test/refined_test.cpp @@ -34,11 +34,13 @@ TEST_CASE("Two refinement types with same ground types and nominally defined " "subtyping are implicily convertible", "[predicate_subtyping]") { using even_lt_10 = - refined::refinement; + refined::refinement; STATIC_REQUIRE(std::is_constructible_v); constexpr even valid = *even_lt_10::make(8); + STATIC_REQUIRE(valid.value() == 8); constexpr std::optional invalid = even_lt_10::make(10); @@ -56,3 +58,12 @@ TEST_CASE("A to_exception policy should throw on invalid argument", REQUIRE_THROWS_AS(even::make(1), refined::error::to_exception::refinement_exception); } + +TEST_CASE("A refinement can be ordered", "[traits][sorted]") { + + using ref_cmp = refined::refinement; + + STATIC_REQUIRE(*ref_cmp::make(1) < *ref_cmp::make(2)); + STATIC_REQUIRE(!(*ref_cmp::make(2) < *ref_cmp::make(1))); +} From 432e75ab5bd2eb5014a9dbc51c43098fa2934908 Mon Sep 17 00:00:00 2001 From: Rafael Varago Date: Mon, 21 Apr 2025 13:20:24 +0200 Subject: [PATCH 2/2] Add equality comparison --- include/rvarago/refined.hpp | 10 +++++++++- test/refined_test.cpp | 9 +++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/include/rvarago/refined.hpp b/include/rvarago/refined.hpp index cc3bd1e..45ffbf7 100644 --- a/include/rvarago/refined.hpp +++ b/include/rvarago/refined.hpp @@ -8,7 +8,10 @@ namespace rvarago::refined { // Properties a refinement implements. struct traits { - // Comparison operations with the spaceship operator. + // Equality comparison (`==`, `!=`). + bool equality{false}; + + // Comparison operations (`<`, `>`, etc) with the spaceship operator. bool ordered{false}; }; @@ -64,6 +67,11 @@ class refinement { return refinement{std::move(value)}; } + friend constexpr auto operator==(refinement const &, refinement const &) + -> bool + requires(Traits.equality) + = default; + friend constexpr auto operator<=>(refinement const &, refinement const &) requires(Traits.ordered) = default; diff --git a/test/refined_test.cpp b/test/refined_test.cpp index e4dc1c8..93a9184 100644 --- a/test/refined_test.cpp +++ b/test/refined_test.cpp @@ -59,6 +59,15 @@ TEST_CASE("A to_exception policy should throw on invalid argument", refined::error::to_exception::refinement_exception); } +TEST_CASE("A refinement can be equality comparable", "[traits][equality]") { + + using ref_cmp = refined::refinement; + + STATIC_REQUIRE(*ref_cmp::make(1) == *ref_cmp::make(1)); + STATIC_REQUIRE(*ref_cmp::make(1) != *ref_cmp::make(2)); +} + TEST_CASE("A refinement can be ordered", "[traits][sorted]") { using ref_cmp = refined::refinement