Skip to content
This repository was archived by the owner on May 3, 2026. It is now read-only.

Commit 0e05111

Browse files
committed
improve Result type constraints
1 parent 58841df commit 0e05111

2 files changed

Lines changed: 40 additions & 12 deletions

File tree

include/common/result.hpp

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,13 @@ namespace zest {
1313
*
1414
* @note the "normal" value is always valid, but an "error" value may or may not be contained.
1515
*
16-
* @tparam T Type of the expected value. Must have
17-
* @tparam Errs List of possible error types (must inherit from ResultError).
16+
* @tparam T Type of the expected value. Must have a Sentinel Value.
17+
* @tparam Errs List of possible error types. Must inherit from ResultError. Must contain at least 1
18+
* type. All types must be unique.
1819
*/
19-
template<typename T, traits::IsResultError... Errs>
20-
requires(traits::HasSentinel<T> || std::same_as<void, T>) && (sizeof...(Errs) > 0)
21-
class Result {
20+
template<traits::HasSentinel T, traits::IsResultError... Errs>
21+
requires traits::NotEmpty<Errs...> && traits::AllUnique<Errs...>
22+
class Result<T, Errs...> {
2223
public:
2324
// instead of wrapping the variant in std::optional, we can use std::monostate
2425
std::variant<std::monostate, Errs...> error;
@@ -211,12 +212,9 @@ operator==(const Result<LhsT, LhsErrs...>& lhs, const Result<RhsT, RhsErrs...>&
211212
return lhs.get_value() == rhs.get_value();
212213
}
213214

214-
/**
215-
* @brief Result specialization for void value type (no stored value).
216-
* @tparam Errs List of possible error types (must inherit from ResultError).
217-
*/
215+
// Result void specialization
218216
template<traits::IsResultError... Errs>
219-
requires(sizeof...(Errs) > 0)
217+
requires traits::NotEmpty<Errs...> && traits::AllUnique<Errs...>
220218
class Result<void, Errs...> {
221219
public:
222220
using value_type = void;

include/common/result_impl.hpp

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,37 @@ class ResultError {
7979

8080
namespace traits {
8181

82+
/**
83+
* @brief Check if a type pack is empty
84+
*
85+
* @tparam Ts the type pack to check
86+
*/
87+
template<typename... Ts>
88+
concept NotEmpty = sizeof...(Ts) > 0;
89+
90+
/**
91+
* @brief Check if all types in a type pack are unique
92+
*
93+
* @tparam Ts types to check
94+
*/
95+
template<typename... Ts>
96+
struct all_unique : std::true_type {};
97+
98+
template<typename T, typename... Ts>
99+
struct all_unique<T, Ts...> :
100+
std::conditional_t<
101+
(!std::same_as<T, Ts> && ...) && all_unique<Ts...>::type,
102+
std::true_type,
103+
std::false_type> {};
104+
105+
/**
106+
* @brief Check if all types in a type pack are unique
107+
*
108+
* @tparam Ts types to check
109+
*/
110+
template<typename... Ts>
111+
concept AllUnique = all_unique<Ts...>::value;
112+
82113
/**
83114
* @brief Check if a given type is derived from ResultError
84115
*/
@@ -310,8 +341,7 @@ struct invocable_indirect_v {
310341
} // namespace traits
311342

312343
// forward-declare Result
313-
template<typename T, traits::IsResultError... Errs>
314-
requires(traits::HasSentinel<T> || std::same_as<void, T>) && (sizeof...(Errs) > 0)
344+
template<typename T, typename... Errs>
315345
class Result;
316346

317347
namespace traits {

0 commit comments

Comments
 (0)