@@ -202,6 +202,9 @@ inline constexpr bool is_result_v = is_result<T>::value;
202202template <typename T>
203203concept IsResult = is_result_v<T>;
204204
205+ // special type that is ignored by concepts like all_same
206+ struct ignored_type {};
207+
205208/* *
206209 * @brief Check whether all types in a pack are the same
207210 *
@@ -210,9 +213,6 @@ concept IsResult = is_result_v<T>;
210213template <typename ... Ts>
211214struct all_same : std::true_type {};
212215
213- // special type that is ignored by the all_same trait
214- struct ignored_type {};
215-
216216/* *
217217 * @brief Check whether all types in a pack are the same
218218 *
@@ -223,13 +223,8 @@ template<typename T, typename... Ts>
223223struct all_same <T, Ts...> :
224224 std::bool_constant<((std::is_same_v<T, Ts> || std::is_same_v<ignored_type, Ts>) && ...)> {};
225225
226- /* *
227- * @brief Check whether all types in a pack are the same
228- *
229- * @tparam Ts types to check
230- */
231226template <typename ... Ts>
232- inline constexpr bool all_same_v = all_same<Ts...>::value;
227+ concept AllSame = all_same<Ts...>::value;
233228
234229// Primary template: no types, no result (causes substitution failure if used)
235230template <typename Condition, typename ... Ts>
@@ -260,6 +255,38 @@ struct invoke_result<F, Args...> : std::invoke_result<F, Args...> {};
260255template <typename F, typename ... Args>
261256using invoke_result_t = invoke_result<F, Args...>::type;
262257
258+ // and_then return type helper
259+ template <typename Self, typename F>
260+ using and_then_return_t = std::invoke_result_t <F, decltype ((std::declval<Self>().value))>;
261+
262+ // or_else return type helper
263+ template <typename Self, typename F, typename E>
264+ using or_else_return_t =
265+ traits::invoke_result_t <F, decltype ((std::get<E>(std::declval<Self>().error)))>;
266+
267+ // or_else invocable callable helper
268+ template <typename Self, typename F, typename E>
269+ constexpr static bool invocable_v =
270+ std::is_invocable<F, decltype ((std::get<E>(std::declval<Self>().error)))>::value;
271+
272+ template <typename F, typename Self>
273+ struct invocable_indirect_v {
274+ template <typename U>
275+ static constexpr bool value = invocable_v<Self, F, U>;
276+ };
277+
278+ template <typename F, typename Self, typename ... Es>
279+ concept any_invocable = (traits::invocable_v<Self, F, Es> || ...);
280+
281+ template <typename T>
282+ concept ResultOrVoid =
283+ traits::IsResult<std::remove_cvref_t <T>> || std::same_as<void , std::remove_cvref_t <T>>
284+ || std::same_as<ignored_type, T>;
285+
286+ template <typename R, typename T>
287+ concept ValueTypeMatch = std::same_as<void , T> || std::same_as<ignored_type, T>
288+ || std::same_as<typename R::value_type, typename T::value_type>;
289+
263290} // namespace traits
264291
265292/* *
@@ -271,27 +298,6 @@ using invoke_result_t = invoke_result<F, Args...>::type;
271298template <typename T, traits::IsResultError... Errs>
272299 requires (traits::HasSentinel<T> || std::same_as<void , T>) && (sizeof ...(Errs) > 0 )
273300class Result {
274- private:
275- // and_then return type helper
276- template <typename Self, typename F>
277- using and_then_return_t = std::invoke_result_t <F, decltype ((std::declval<Self>().value))>;
278-
279- // or_else return type helper
280- template <typename Self, typename F, typename E>
281- using or_else_return_t =
282- traits::invoke_result_t <F, decltype ((std::get<E>(std::declval<Self>().error)))>;
283-
284- // or_else invocable callable helper
285- template <typename Self, typename F, typename E>
286- constexpr static bool or_else_invocable_v =
287- std::is_invocable<F, decltype ((std::get<E>(std::declval<Self>().error)))>::value;
288-
289- template <typename F, typename Self>
290- struct or_else_invocable_indirect_v {
291- template <typename U>
292- static constexpr bool value = or_else_invocable_v<Self, F, U>;
293- };
294-
295301 public:
296302 // instead of wrapping the variant in std::optional, we can use std::monostate
297303 std::variant<std::monostate, Errs...> error;
@@ -378,13 +384,14 @@ class Result {
378384 */
379385 template <typename Self, typename F>
380386 requires std::invocable<F, decltype ((std::declval<Self>().value))>
381- && traits::is_result_v<and_then_return_t <Self, F>>
382- && traits::
383- contains_all_v<typename and_then_return_t <Self, F>::error_types, error_types>
387+ && traits::is_result_v<traits::and_then_return_t <Self, F>>
388+ && traits::contains_all_v<
389+ typename traits::and_then_return_t <Self, F>::error_types,
390+ error_types>
384391 constexpr auto and_then (this Self&& self, F&& f) {
385392 // if there is an error, return said error immediately
386393 if (self.has_error ()) {
387- return std::visit ([](auto && arg) -> and_then_return_t <Self, F> {
394+ return std::visit ([](auto && arg) -> traits:: and_then_return_t <Self, F> {
388395 // even though this conditional is impossible, it's necessary to stop the compiler
389396 // from thinking that ReturnType could be constructed with std::monostate
390397 if constexpr (std::is_same_v<std::monostate, std::remove_cvref_t <decltype (arg)>>) {
@@ -416,47 +423,21 @@ class Result {
416423 * @param f the callable
417424 */
418425 template <typename Self, typename F>
419- requires ( // callable must be invocable with at least 1 error type
420- or_else_invocable_v<Self, F, Errs> || ...
421- )
422- // the callable must always return the same type if it is invocable
423- && traits::all_same_v<std::conditional_t <
424- or_else_invocable_v<Self, F, Errs>,
425- // if the callable is invocable
426- or_else_return_t <Self, F, Errs>,
427- // if the callable is not invocable
428- traits::ignored_type>...>
429- // the callable must return a Result or void if it is invocable with a given error
430- // type
431- && (std::conditional_t <
432- or_else_invocable_v<Self, F, Errs>,
433- // if the callable is invocable
434- std::disjunction<
435- traits::is_result<or_else_return_t <Self, F, Errs>>,
436- std::is_same<or_else_return_t <Self, F, Errs>, void >>,
437- // otherwise, if the callable is not invocable
438- std::true_type>::value
439- && ...)
440- // if F is invocable and returns a result, it must have the same value type as Self
441- && (std::conditional_t <
442- or_else_invocable_v<Self, F, Errs>,
443- // if the callable is invocable
444- std::conditional_t <
445- traits::is_result_v<or_else_return_t <Self, F, Errs>>,
446- // if the return is a Result
447- std::is_same<T, or_else_return_t <Self, F, Errs>>,
448- // otherwise, if the return is not a Result
449- std::true_type>,
450- // otherwise, if the callable is not invocable
451- std::true_type>::value
452- && ...)
426+ requires traits::any_invocable<F, Self, Errs...>
427+ // the callable must always return the same type (if it's invocable)
428+ && traits::AllSame<traits::or_else_return_t <Self, F, Errs>...>
429+ // the callable must return a Result or void (if it's invocable)
430+ && (traits::ResultOrVoid<traits::or_else_return_t <Self, F, Errs>> && ...)
431+ // if the callable returns a Result, it must have the same value type as this
432+ // Result instance
433+ && (traits::ValueTypeMatch<Self, traits::or_else_return_t <Self, F, Errs>> && ...)
453434 constexpr auto or_else (this Self&& self, F&& f) {
454435 // the return type is the same as the return type of the callable, unless the callable
455436 // returns void. In that case, the return type is Self with cv-refs removed
456437 using CallableReturnType =
457- traits::first_type_that_satisfies_t <or_else_invocable_indirect_v <F, Self>, Errs...>;
438+ traits::first_type_that_satisfies_t <traits::invocable_indirect_v <F, Self>, Errs...>;
458439 using ReturnType = std::conditional_t <
459- traits::is_result_v <CallableReturnType>,
440+ traits::IsResult <CallableReturnType>,
460441 CallableReturnType,
461442 std::remove_cvref_t <Self>>;
462443
@@ -470,7 +451,7 @@ class Result {
470451 // even though this condition is impossible, it's necessary. Otherwise the
471452 // compiler will compile a branch where f is invoked with an unsupported argument
472453 // type
473- if constexpr (!or_else_invocable_v <Self, F, decltype ((arg))>
454+ if constexpr (!traits::invocable_v <Self, F, decltype ((arg))>
474455 || std::
475456 is_same_v<std::monostate, std::remove_cvref_t <decltype (arg)>>) {
476457 throw std::logic_error (" This exception is unreachable" );
@@ -484,7 +465,7 @@ class Result {
484465 // even though this condition is impossible, it's necessary. Otherwise the
485466 // compiler will compile a branch where f is invoked with an unsupported argument
486467 // type
487- if constexpr (!or_else_invocable_v <Self, F, decltype ((arg))>
468+ if constexpr (!traits::invocable_v <Self, F, decltype ((arg))>
488469 || std::is_same_v<std::monostate, std::remove_cvref_t <decltype (arg)>>) {
489470 throw std::logic_error (" This exception is unreachable" );
490471 } else {
@@ -644,7 +625,7 @@ class Result<void, Errs...> {
644625 template <typename Self, typename F>
645626 requires (std::invocable<F, decltype ((std::get<Errs>(std::declval<Self>().error)))> && ...)
646627 && (traits::is_result_v<or_else_return_t <Self, F, Errs>> && ...)
647- && (traits::all_same_v <or_else_return_t <Self, F, Errs>...>)
628+ && (traits::AllSame <or_else_return_t <Self, F, Errs>...>)
648629 && (std::same_as<void , typename or_else_return_t <Self, F, Errs>::value_type> && ...)
649630 constexpr auto or_else (this Self&& self, F&& f) {
650631 using ReturnType = or_else_return_t <Self, F, std::tuple_element_t <0 , std::tuple<Errs...>>>;
@@ -662,5 +643,4 @@ class Result<void, Errs...> {
662643 }, std::forward<Self>(self).error );
663644 }
664645};
665-
666646} // namespace zest
0 commit comments