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

Commit f9fc447

Browse files
committed
clean up result.hpp traits
1 parent fd89269 commit f9fc447

1 file changed

Lines changed: 82 additions & 51 deletions

File tree

include/common/result.hpp

Lines changed: 82 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -41,27 +41,49 @@ class ResultError {
4141
std::optional<RuntimeData> runtime_data;
4242
};
4343

44+
namespace traits {
45+
4446
/**
45-
* @brief Trait to define a "sentinel" value for types indicating an error state.
46-
* @tparam T Type to provide a sentinel value for.
47-
* @note Specialize this template for custom types if needed.
47+
* @brief Check whether a given type is in a typename pack
48+
*
49+
* @tparam T the type to check
50+
* @tparam Ts the typename pack to check
51+
*/
52+
template<typename T, typename... Ts>
53+
inline constexpr bool is_in_pack_v = (std::is_same_v<std::remove_cvref_t<T>, Ts> || ...);
54+
55+
/**
56+
* @brief type trait for checking whether a type is derived from the ResultError class
57+
*/
58+
template<typename>
59+
struct is_result_error : std::false_type {};
60+
61+
/**
62+
* @brief type trait for checking whether a type is derived from the ResultError class
4863
*/
4964
template<typename T>
50-
class SentinelValue;
65+
requires std::derived_from<ResultError, T>
66+
struct is_result_error<T> : std::true_type {};
5167

5268
/**
53-
* @brief Concept to check if a type has a defined sentinel value.
54-
* @tparam T Type to check.
69+
* @brief whether the given type is derived from the ResultError class
5570
*/
5671
template<typename T>
57-
concept Sentinel = requires(const T& val) { SentinelValue<T>::value; };
72+
inline constexpr bool is_result_error_v = is_result_error<T>::value;
5873

5974
/**
60-
* @brief Helper variable to simplify access to a type's sentinel value.
61-
* @tparam T Type with a defined sentinel (must satisfy Sentinel concept).
75+
* @brief IsResultError concept. Enforces that the given type is derived from the ResultError class
6276
*/
63-
template<Sentinel T>
64-
constexpr T sentinel_v = SentinelValue<T>::value;
77+
template<typename T>
78+
concept IsResultError = is_result_error_v<std::remove_cvref_t<T>>;
79+
80+
/**
81+
* @brief Trait to define a "sentinel" value for types indicating an error state.
82+
* @tparam T Type to provide a sentinel value for.
83+
* @note Specialize this template for custom types if needed.
84+
*/
85+
template<typename T>
86+
class SentinelValue;
6587

6688
/**
6789
* @brief Partial specialization of SentinelValue for integral and floating-point types.
@@ -83,14 +105,26 @@ class SentinelValue<T> {
83105
static constexpr T value = get(); ///< Precomputed sentinel value for type T.
84106
};
85107

86-
// forward declarations
87-
template<typename T, typename... Errs>
88-
requires(sizeof...(Errs) > 0) && (std::derived_from<Errs, ResultError> && ...)
89-
class Result;
108+
/**
109+
* @brief Concept to check if a type has a defined sentinel value.
110+
* @tparam T Type to check.
111+
*/
112+
template<typename T>
113+
concept HasSentinel = requires(const T& val) { SentinelValue<T>::value; };
114+
115+
/**
116+
* @brief Helper variable to simplify access to a type's sentinel value.
117+
* @tparam T Type with a defined sentinel (must satisfy Sentinel concept).
118+
*/
119+
template<HasSentinel T>
120+
inline constexpr T sentinel_v = SentinelValue<T>::value;
121+
122+
} // namespace traits
90123

91-
template<typename... Errs>
92-
requires(sizeof...(Errs) > 0) && (std::derived_from<Errs, ResultError> && ...)
93-
class Result<void, Errs...>;
124+
// forward declarations, necessary to declare traits
125+
template<typename T, traits::IsResultError... Errs>
126+
requires(sizeof...(Errs) > 0)
127+
class Result;
94128

95129
/**
96130
* @brief Type trait to check if a type is a Result.
@@ -102,9 +136,6 @@ struct is_result : std::false_type {};
102136
template<typename T, typename... Errs>
103137
struct is_result<Result<T, Errs...>> : std::true_type {};
104138

105-
template<typename... Errs>
106-
struct is_result<Result<void, Errs...>> : std::true_type {};
107-
108139
template<typename T>
109140
inline constexpr bool is_result_v = is_result<T>::value;
110141

@@ -114,54 +145,54 @@ inline constexpr bool is_result_v = is_result<T>::value;
114145
* @tparam Errs List of possible error types (must inherit from ResultError).
115146
* @note Errors are stored in a variant, and the value is always initialized.
116147
*/
117-
template<typename T, typename... Errs>
118-
requires(sizeof...(Errs) > 0) && (std::derived_from<Errs, ResultError> && ...)
148+
template<typename T, traits::IsResultError... Errs>
149+
requires(sizeof...(Errs) > 0)
119150
class Result {
120151
public:
121152
/**
122153
* @brief Construct a Result with a normal value (no error).
123-
* @tparam U Type convertible to T.
154+
* @tparam U Type convertible to T, and U not derived from ResultError
124155
* @param value Value to initialize the result with.
125156
*/
126157
template<typename U>
127-
requires std::constructible_from<T, U>
158+
requires std::constructible_from<T, U> && (!traits::IsResultError<U>)
128159
constexpr Result(U&& value)
129160
: m_error(std::monostate()),
130161
m_value(std::forward<U>(value)) {}
131162

132163
/**
133164
* @brief Construct a Result with a value and an error.
134-
* @tparam U Type convertible to T.
135-
* @tparam E Error type (must be in Errs).
165+
* @tparam U Type convertible to T, and U not derived from ResultError
166+
* @tparam E Error type, must be in Errs and must be derived from ResultError
136167
* @param value Value to store.
137168
* @param error Error to store.
138169
*/
139-
template<typename U, typename E>
140-
requires std::constructible_from<T, U>
141-
&& (std::same_as<std::remove_cvref_t<E>, Errs> || ...)
170+
template<typename U, traits::IsResultError E>
171+
requires std::constructible_from<T, U> && (!traits::IsResultError<U>)
172+
&& traits::is_in_pack_v<E, Errs...>
142173
constexpr Result(U&& value, E&& error)
143174
: m_value(std::forward<U>(value)),
144175
m_error(std::forward<E>(error)) {}
145176

146177
/**
147178
* @brief Construct a Result with an error, initializing the value to its sentinel.
148-
* @tparam E Error type (must be in Errs).
179+
* @tparam E Error type, must be in Errs and derived from ResultError.
149180
* @param error Error to store.
150181
* @note Requires T to have a defined sentinel value (via SentinelValue<T>).
151182
*/
152-
template<typename E>
153-
requires Sentinel<T> && (std::same_as<std::remove_cvref_t<E>, Errs> || ...)
183+
template<traits::IsResultError E>
184+
requires traits::HasSentinel<T> && traits::is_in_pack_v<E, Errs...>
154185
constexpr Result(E&& error)
155186
: m_error(std::forward<E>(error)),
156-
m_value(sentinel_v<T>) {}
187+
m_value(traits::sentinel_v<T>) {}
157188

158189
/**
159190
* @brief Get an error of type E if present (const-qualified overload).
160-
* @tparam E Error type to retrieve.
191+
* @tparam E Error type to retrieve, must be in Errs and derived from ResultError.
161192
* @return std::optional<E> Contains the error if present; otherwise nullopt.
162193
*/
163-
template<typename E>
164-
requires(std::same_as<E, Errs> || ...)
194+
template<traits::IsResultError E>
195+
requires traits::is_in_pack_v<E, Errs...>
165196
constexpr std::optional<E> get() const& {
166197
if (std::holds_alternative<E>(m_error)) {
167198
return std::get<E>(m_error);
@@ -175,8 +206,8 @@ class Result {
175206
* @tparam E Error type to retrieve.
176207
* @return std::optional<E> Contains the error if present; otherwise nullopt.
177208
*/
178-
template<typename E>
179-
requires(std::same_as<E, Errs> || ...)
209+
template<traits::IsResultError E>
210+
requires traits::is_in_pack_v<E, Errs...>
180211
constexpr std::optional<E> get() && {
181212
if (std::holds_alternative<E>(m_error)) {
182213
return std::move(std::get<E>(m_error));
@@ -190,8 +221,8 @@ class Result {
190221
* @tparam E Error type to retrieve.
191222
* @return std::optional<E> Contains the error if present; otherwise nullopt.
192223
*/
193-
template<typename E>
194-
requires(std::same_as<E, Errs> || ...)
224+
template<traits::IsResultError E>
225+
requires traits::is_in_pack_v<E, Errs...>
195226
constexpr const std::optional<E> get() const&& {
196227
if (std::holds_alternative<E>(m_error)) {
197228
return std::move(std::get<E>(m_error));
@@ -437,17 +468,17 @@ operator==(const Result<LhsT, LhsErrs...>& lhs, const Result<RhsT, RhsErrs...>&
437468
* @brief Result specialization for void value type (no stored value).
438469
* @tparam Errs List of possible error types (must inherit from ResultError).
439470
*/
440-
template<typename... Errs>
441-
requires(sizeof...(Errs) > 0) && (std::derived_from<Errs, ResultError> && ...)
471+
template<traits::IsResultError... Errs>
472+
requires(sizeof...(Errs) > 0)
442473
class Result<void, Errs...> {
443474
public:
444475
/**
445476
* @brief Construct a Result with an error.
446477
* @tparam E Error type (must be in Errs).
447478
* @param error Error to store.
448479
*/
449-
template<typename E>
450-
requires(std::same_as<std::remove_cvref_t<E>, Errs> || ...)
480+
template<traits::IsResultError E>
481+
requires traits::is_in_pack_v<E, Errs...>
451482
constexpr Result(E&& error)
452483
: m_error(std::forward<E>(error)) {}
453484

@@ -462,8 +493,8 @@ class Result<void, Errs...> {
462493
* @tparam E Error type to retrieve.
463494
* @return std::optional<E> Contains the error if present; otherwise nullopt.
464495
*/
465-
template<typename E>
466-
requires(std::same_as<E, Errs> || ...)
496+
template<traits::IsResultError E>
497+
requires traits::is_in_pack_v<E, Errs...>
467498
constexpr std::optional<E> get() const& {
468499
if (std::holds_alternative<E>(m_error)) {
469500
return std::get<E>(m_error);
@@ -477,8 +508,8 @@ class Result<void, Errs...> {
477508
* @tparam E Error type to retrieve.
478509
* @return std::optional<E> Contains the error if present; otherwise nullopt.
479510
*/
480-
template<typename E>
481-
requires(std::same_as<E, Errs> || ...)
511+
template<traits::IsResultError E>
512+
requires traits::is_in_pack_v<E, Errs...>
482513
constexpr std::optional<E> get() && {
483514
if (std::holds_alternative<E>(m_error)) {
484515
return std::move(std::get<E>(m_error));
@@ -492,8 +523,8 @@ class Result<void, Errs...> {
492523
* @tparam E Error type to retrieve.
493524
* @return std::optional<E> Contains the error if present; otherwise nullopt.
494525
*/
495-
template<typename E>
496-
requires(std::same_as<E, Errs> || ...)
526+
template<traits::IsResultError E>
527+
requires traits::is_in_pack_v<E, Errs...>
497528
constexpr const std::optional<E> get() const&& {
498529
if (std::holds_alternative<E>(m_error)) {
499530
return std::move(std::get<E>(m_error));

0 commit comments

Comments
 (0)