Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 28 additions & 15 deletions include/rvarago/refined.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,36 @@ concept policy = requires(Policy p, Refinement refined) {
typename Refinement::predicate_type;

// On success.
{ p.ok(refined) } -> std::same_as<typename Policy::return_type>;
{
p.template ok<Refinement>(refined)
} -> std::same_as<typename Policy::template wrapper_type<Refinement>>;

// On error.
{ p.err() } -> std::same_as<typename Policy::return_type>;
{
p.template err<Refinement>()
} -> std::same_as<typename Policy::template wrapper_type<Refinement>>;
};

// Report errors as `std::optional<Refinement>`.
template <typename Refinement> struct to_optional {
using return_type = std::optional<Refinement>;
struct to_optional {
template <typename Refinement> using wrapper_type = std::optional<Refinement>;

// Returns an engaged optional.
constexpr auto ok(Refinement refinement) const -> return_type {
template <typename Refinement>
constexpr auto ok(Refinement refinement) const -> wrapper_type<Refinement> {
return std::optional{std::move(refinement)};
}

// Returns nullopt.
constexpr auto err() const -> return_type { return std::nullopt; }
template <typename Refinement>
constexpr auto err() const -> wrapper_type<Refinement> {
return std::nullopt;
}
};

// Report errors as exceptions.
template <typename Refinement> struct to_exception {
using return_type = Refinement;
struct to_exception {
template <typename Refinement> using wrapper_type = Refinement;

struct refinement_exception : std::exception {
const char *what() const noexcept override {
Expand All @@ -46,13 +54,18 @@ template <typename Refinement> struct to_exception {
};

// Returns argument unchanged.
constexpr auto ok(Refinement refinement) const -> return_type {
template <typename Refinement>
constexpr auto ok(Refinement refinement) const -> wrapper_type<Refinement> {
return refinement;
}

// Throws a `refinement_exception`.
constexpr auto err() const -> return_type { throw refinement_exception{}; }
template <typename Refinement>
constexpr auto err() const -> wrapper_type<Refinement> {
throw refinement_exception{};
}
};

} // namespace error

// `refinement<T, Pred>` constraints values `t: T` where `Pred{}(t)` holds.
Expand All @@ -67,14 +80,14 @@ template <typename T, std::predicate<T const &> Pred> struct refinement {
//
// If `pred(value)` holds, then produces a valid instance by delegating to
// `policy.ok`. Else reports error via `policy.err`.
template <error::policy<refinement<T, Pred>> Policy =
error::to_optional<refinement<T, Pred>>>
template <error::policy<refinement<T, Pred>> Policy = error::to_optional>
static constexpr auto make(T value, Pred pred = {}, Policy policy = {})
-> Policy::return_type {
-> Policy::template wrapper_type<refinement<T, Pred>> {
if (std::invoke(std::move(pred), value)) {
return policy.ok({refinement<T, Pred>{std::move(value)}});
return policy.template ok<refinement<T, Pred>>(
{refinement<T, Pred>{std::move(value)}});
} else {
return policy.err();
return policy.template err<refinement<T, Pred>>();
}
}

Expand Down
12 changes: 6 additions & 6 deletions test/refined_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ TEST_CASE(
STATIC_REQUIRE(std::is_same_v<decltype(even::make(0)), std::optional<even>>);

STATIC_REQUIRE(
std::is_same_v<decltype(even::make<refined::error::to_optional<even>>(0)),
std::is_same_v<decltype(even::make<refined::error::to_optional>(0)),
std::optional<even>>);

STATIC_REQUIRE(even::make(0)->value == 0);
Expand All @@ -27,10 +27,10 @@ TEST_CASE("A to_exception policy should throw on invalid argument",
"[error_policy][to_exception]") {

STATIC_REQUIRE(
std::is_same_v<
decltype(even::make<refined::error::to_exception<even>>(10)), even>);
std::is_same_v<decltype(even::make<refined::error::to_exception>(10)),
even>);

STATIC_REQUIRE(even::make<refined::error::to_exception<even>>(0).value == 0);
REQUIRE_THROWS_AS(even::make<refined::error::to_exception<even>>(1),
refined::error::to_exception<even>::refinement_exception);
STATIC_REQUIRE(even::make<refined::error::to_exception>(0).value == 0);
REQUIRE_THROWS_AS(even::make<refined::error::to_exception>(1),
refined::error::to_exception::refinement_exception);
}