@@ -197,12 +197,36 @@ inline constexpr bool is_result_v = is_result<T>::value;
197197
198198/* *
199199 * @brief Concept to check if a type is a Result
200- *
201- * @tparam T
200+ * @tparam T Type to check
202201 */
203202template <typename T>
204203concept IsResult = is_result_v<T>;
205204
205+ /* *
206+ * @brief Check whether all types in a pack are the same
207+ *
208+ * @tparam Ts types to check
209+ */
210+ template <typename ... Ts>
211+ struct all_same : std::true_type {};
212+
213+ /* *
214+ * @brief Check whether all types in a pack are the same
215+ *
216+ * @tparam T first type
217+ * @tparam Ts the rest of the types
218+ */
219+ template <typename T, typename ... Ts>
220+ struct all_same <T, Ts...> : std::bool_constant<(std::is_same_v<T, Ts> && ...)> {};
221+
222+ /* *
223+ * @brief Check whether all types in a pack are the same
224+ *
225+ * @tparam Ts types to check
226+ */
227+ template <typename ... Ts>
228+ inline constexpr bool all_same_v = all_same<Ts...>::value;
229+
206230} // namespace traits
207231
208232/* *
@@ -215,11 +239,15 @@ template<typename T, traits::IsResultError... Errs>
215239 requires (traits::HasSentinel<T> || std::same_as<void , T>) && (sizeof ...(Errs) > 0 )
216240class Result {
217241 private:
218- // helper type
242+ // and_then return type helper
219243 template <typename Self, typename F>
220- requires std::invocable<F, decltype ((std::declval<Self>().value))>
221244 using and_then_return_t = std::invoke_result_t <F, decltype ((std::declval<Self>().value))>;
222245
246+ // or_else return type helper
247+ template <typename Self, typename F, typename E>
248+ using or_else_return_t =
249+ std::invoke_result_t <F, decltype ((std::get<E>(std::declval<Self>().error)))>;
250+
223251 public:
224252 // instead of wrapping the variant in std::optional, we can use std::monostate
225253 std::variant<std::monostate, Errs...> error;
@@ -233,8 +261,8 @@ class Result {
233261 * @tparam U Type convertible to T, and U not derived from ResultError
234262 * @param value Value to initialize the result with.
235263 */
236- template <typename U>
237- requires std::convertible_to<T, U> && (!traits::is_in_pack_v<U, std::monostate, Errs...>)
264+ template <traits::IsResultError U>
265+ requires std::convertible_to<T, U>
238266 constexpr Result (U&& value)
239267 : error(std::monostate()),
240268 value(std::forward<U>(value)) {}
@@ -245,7 +273,7 @@ class Result {
245273 * @param error Error to store.
246274 * @note Requires T to have a defined sentinel value (via SentinelValue<T>).
247275 */
248- template <typename E>
276+ template <traits::IsResultError E>
249277 requires traits::is_in_pack_v<E, Errs...>
250278 constexpr Result (E&& error)
251279 : error(std::forward<E>(error)),
@@ -305,22 +333,63 @@ class Result {
305333 * @return return type of callable
306334 */
307335 template <typename Self, typename F>
308- constexpr auto and_then (this Self&& self, F&& f)
309- requires std::invocable<F, decltype(std::forward<Self>(self).value)>
336+ requires std::invocable<F, decltype ((std::declval<Self>().value))>
310337 && traits::is_result_v<and_then_return_t <Self, F>>
311338 && traits::
312339 contains_all_v<typename and_then_return_t <Self, F>::error_types, error_types>
313- {
340+ constexpr auto and_then ( this Self&& self, F&& f) {
314341 // if there is an error, return said error immediately
315342 if (self.has_error ()) {
316- return std::visit ([](auto && var) -> and_then_return_t <Self, F> {
317- return std::forward<decltype (var)>(var);
343+ return std::visit ([](auto && arg) -> and_then_return_t <Self, F> {
344+ // even though this conditional is impossible, it's necessary to stop the compiler
345+ // from thinking that ReturnType could be constructed with std::monostate
346+ if constexpr (std::is_same_v<std::monostate, std::remove_cvref_t <decltype (arg)>>) {
347+ throw std::logic_error (" This exception is unreachable" );
348+ } else {
349+ return std::forward<decltype (arg)>(arg);
350+ }
318351 }, std::forward<Self>(self).error );
319352 }
320353 // otherwise, invoke the callable and return the result
321354 return std::invoke (f, std::forward<Self>(self).value );
322355 }
323356
357+ /* *
358+ * @brief or_else monadic function.
359+ *
360+ * The callable must be able to take a perfectly forwarded error that may be contained by this
361+ * Result instance. The callable must be able to return a Result containing any error type that
362+ * may be passed to it. The normal value type of the Result returned by the callable must be the
363+ * same as the normal value type of this Result instance.
364+ *
365+ * @tparam Self deduced self type
366+ * @tparam F the type of the callable
367+ * @param self the current Result instance
368+ * @param f the callable
369+ */
370+ template <typename Self, typename F>
371+ requires (std::invocable<F, decltype ((std::get<Errs>(std::declval<Self>().error)))> && ...)
372+ && (traits::is_result_v<or_else_return_t <Self, F, Errs>> && ...)
373+ && (traits::all_same_v<or_else_return_t <Self, F, Errs>...>)
374+ && (std::same_as<T, typename or_else_return_t <Self, F, Errs>::value_type> && ...)
375+ constexpr auto or_else (this Self&& self, F&& f) {
376+ using ReturnType = or_else_return_t <Self, F, std::tuple_element_t <1 , std::tuple<Errs...>>>;
377+ // if there isn't an error, return
378+ if (!self.has_error ()) {
379+ return ReturnType (std::forward<Self>(self).value );
380+ }
381+ // otherwise, invoke the given lambda
382+ return std::visit ([&f](auto && arg) -> ReturnType {
383+ // even though this conditional is impossible, it's necessary to stop the compiler from
384+ // thinking that ReturnType could be constructed with std::monostate
385+ if constexpr (std::is_same_v<std::monostate, std::remove_cvref_t <decltype (arg)>>) {
386+ throw std::logic_error (" This exception is unreachable" );
387+ } else {
388+ return std::invoke (f, arg);
389+ }
390+ }, std::forward<Self>(self).error );
391+ }
392+
324393 constexpr operator T&() & {
325394 return value;
326395 }
@@ -364,6 +433,12 @@ operator==(const Result<LhsT, LhsErrs...>& lhs, const Result<RhsT, RhsErrs...>&
364433template <traits::IsResultError... Errs>
365434 requires (sizeof ...(Errs) > 0 )
366435class Result <void , Errs...> {
436+ private:
437+ // or_else return type helper
438+ template <typename Self, typename F, typename E>
439+ using or_else_return_t =
440+ std::invoke_result_t <F, decltype ((std::get<E>(std::declval<Self>().error)))>;
441+
367442 public:
368443 using value_type = void ;
369444 using error_types = traits::type_pack<Errs...>;
@@ -376,8 +451,8 @@ class Result<void, Errs...> {
376451 * @tparam E Error type (must be in Errs).
377452 * @param error Error to store.
378453 */
379- template <typename E>
380- requires traits::is_in_pack_v<E, std::monostate, Errs...>
454+ template <traits::IsResultError E>
455+ requires traits::is_in_pack_v<E, Errs...>
381456 constexpr Result (E&& error)
382457 : error(std::forward<E>(error)) {}
383458
@@ -429,20 +504,59 @@ class Result<void, Errs...> {
429504 * @return return type of callable
430505 */
431506 template <std::invocable F, typename Self>
432- constexpr auto and_then (this Self&& self, F&& f)
433507 requires traits::is_result_v<std::invoke_result_t <F>>
434508 && traits::
435509 contains_all_v<typename std::invoke_result_t <F>::error_types, error_types>
436- {
510+ constexpr auto and_then ( this Self&& self, F&& f) {
437511 // if there is an error, return said error immediately
438512 if (self.has_error ()) {
439- return std::visit ([](auto && var) -> std::invoke_result_t <F> {
440- return std::forward<decltype (var)>(var);
513+ return std::visit ([](auto && arg) -> std::invoke_result_t <F> {
514+ // even though this conditional is impossible, it's necessary to stop the compiler
515+ // from thinking that ReturnType could be constructed with std::monostate
516+ if constexpr (std::is_same_v<std::monostate, std::remove_cvref_t <decltype (arg)>>) {
517+ throw std::logic_error (" This exception is unreachable" );
518+ } else {
519+ return std::forward<decltype (arg)>(arg);
520+ }
441521 }, std::forward<Self>(self).error );
442522 }
443523 // otherwise, invoke the callable and return the result
444524 return std::invoke (f);
445525 }
526+
527+ /* *
528+ * @brief or_else monadic function.
529+ *
530+ * The callable must be able to take a perfectly forwarded error that may be contained by this
531+ * Result instance. The callable must be able to return a Result containing any error type that
532+ * may be passed to it. The normal value type of the Result returned by the callable must be the
533+ * same as the normal value type of this Result instance.
534+ *
535+ * @tparam Self deduced self type
536+ * @tparam F the type of the callable
537+ * @param self the current Result instance
538+ * @param f the callable
539+ */
540+ template <typename Self, typename F>
541+ requires (std::invocable<F, decltype ((std::get<Errs>(std::declval<Self>().error)))> && ...)
542+ && (traits::is_result_v<or_else_return_t <Self, F, Errs>> && ...)
543+ && (traits::all_same_v<or_else_return_t <Self, F, Errs>...>)
544+ && (std::same_as<void , typename or_else_return_t <Self, F, Errs>::value_type> && ...)
545+ constexpr auto or_else (this Self&& self, F&& f) {
546+ using ReturnType = or_else_return_t <Self, F, std::tuple_element_t <1 , std::tuple<Errs...>>>;
547+ // if there isn't an error, return
548+ return ReturnType ();
549+ // otherwise, invoke the given lambda
550+ return std::visit ([&f](auto && arg) -> ReturnType {
551+ // even though this conditional is impossible, it's necessary to stop the compiler from
552+ // thinking that ReturnType could be constructed with std::monostate
553+ if constexpr (std::is_same_v<std::monostate, std::remove_cvref_t <decltype (arg)>>) {
554+ throw std::logic_error (" This exception is unreachable" );
555+ } else {
556+ return std::invoke (f, arg);
557+ }
558+ }, std::forward<Self>(self).error );
559+ }
446560};
447561
448562} // namespace zest
0 commit comments