@@ -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 */
4964template <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 */
5671template <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 {};
102136template <typename T, typename ... Errs>
103137struct is_result <Result<T, Errs...>> : std::true_type {};
104138
105- template <typename ... Errs>
106- struct is_result <Result<void , Errs...>> : std::true_type {};
107-
108139template <typename T>
109140inline 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 )
119150class 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 )
442473class 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