Skip to content

Commit 7b7743c

Browse files
committed
implement Result::and_then monadic function
1 parent 0d5d1e4 commit 7b7743c

File tree

1 file changed

+71
-68
lines changed

1 file changed

+71
-68
lines changed

include/common/result.hpp

Lines changed: 71 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#pragma once
22

3+
#include <cerrno>
34
#include <chrono>
45
#include <concepts>
56
#include <functional>
@@ -167,7 +168,7 @@ inline constexpr T sentinel_v = SentinelValue<T>::value;
167168
} // namespace traits
168169

169170
// forward declarations, necessary to declare traits
170-
template<typename T, traits::IsResultError... Errs>
171+
template<traits::HasSentinel T, traits::IsResultError... Errs>
171172
requires(sizeof...(Errs) > 0)
172173
class Result;
173174

@@ -210,53 +211,36 @@ concept IsResult = is_result_v<T>;
210211
* @tparam Errs List of possible error types (must inherit from ResultError).
211212
* @note Errors are stored in a variant, and the value is always initialized.
212213
*/
213-
template<typename T, traits::IsResultError... Errs>
214+
template<traits::HasSentinel T, traits::IsResultError... Errs>
214215
requires(sizeof...(Errs) > 0)
215216
class Result {
216217
public:
218+
// instead of wrapping the variant in std::optional, we can use std::monostate
219+
std::variant<std::monostate, Errs...> error;
220+
T value;
221+
217222
using value_type = T;
218223
using error_types = traits::type_pack<Errs...>;
219224

220-
template<traits::IsResult U>
221-
requires traits::contains_all_v<typename Result::error_types, typename U::error_types>
222-
&& std::constructible_from<typename Result::value_type, typename U::value_type>
223-
static constexpr Result from_other(U&& other) {
224-
return Result(std::forward<U>(other).value, std::forward<U>(other).error);
225-
}
226-
227225
/**
228226
* @brief Construct a Result with a normal value (no error).
229227
* @tparam U Type convertible to T, and U not derived from ResultError
230228
* @param value Value to initialize the result with.
231229
*/
232230
template<typename U>
233-
requires std::constructible_from<T, U> && (!traits::IsResultError<U>)
231+
requires std::convertible_to<T, U> && (!traits::IsResultError<U>)
234232
constexpr Result(U&& value)
235233
: error(std::monostate()),
236234
value(std::forward<U>(value)) {}
237235

238-
/**
239-
* @brief Construct a Result with a value and an error.
240-
* @tparam U Type convertible to T, and U not derived from ResultError
241-
* @tparam E Error type, must be in Errs and must be derived from ResultError
242-
* @param value Value to store.
243-
* @param error Error to store.
244-
*/
245-
template<typename U, traits::IsResultError E>
246-
requires std::constructible_from<T, U> && (!traits::IsResultError<U>)
247-
&& traits::is_in_pack_v<E, Errs...>
248-
constexpr Result(U&& value, E&& error)
249-
: value(std::forward<U>(value)),
250-
error(std::forward<E>(error)) {}
251-
252236
/**
253237
* @brief Construct a Result with an error, initializing the value to its sentinel.
254238
* @tparam E Error type, must be in Errs and derived from ResultError.
255239
* @param error Error to store.
256240
* @note Requires T to have a defined sentinel value (via SentinelValue<T>).
257241
*/
258242
template<traits::IsResultError E>
259-
requires traits::HasSentinel<T> && traits::is_in_pack_v<E, Errs...>
243+
requires traits::is_in_pack_v<E, Errs...>
260244
constexpr Result(E&& error)
261245
: error(std::forward<E>(error)),
262246
value(traits::sentinel_v<T>) {}
@@ -279,45 +263,6 @@ class Result {
279263
}
280264
}
281265

282-
// type rules:
283-
// - a Result must be returned
284-
// - the return type must be able to contain any error type that could be contained by this
285-
template<typename Self, std::invocable F>
286-
constexpr auto and_then(this Self&& self, F&& f)
287-
requires
288-
// the callable must return a Result
289-
traits::is_result_v<std::invoke_result_t<F, decltype(std::forward<Self>(self).m_value)>>
290-
// the return type must be able to contain any error passed to the callable
291-
&& traits::contains_all_v<
292-
std::invoke_result_t<F, decltype(std::forward<Self>(self).m_value)>,
293-
typename Self::error_types>
294-
// the callable can only have a different value type if SentinelValue is specialized
295-
&& (traits::HasSentinel<typename std::invoke_result_t<
296-
F,
297-
decltype(std::forward<Self>(self).m_value)>::value_type>
298-
|| std::same_as<
299-
typename Self::value_type,
300-
typename std::invoke_result_t<F, decltype(self.m_value)>::value_type>)
301-
{
302-
using ReturnType = std::invoke_result_t<F, decltype(std::forward<Self>(self).m_value)>;
303-
if (self.has_error()) {
304-
// if the callable return value type is the same as the Self value type,
305-
// return a Result with the same value and error
306-
// otherwise, return the same error but with the sentinel value
307-
if constexpr (std::
308-
same_as<typename Self::value_type, typename ReturnType::value_type>) {
309-
return Result::from_other(std::invoke(f, std::forward<Self>(self)));
310-
} else {
311-
return Result{
312-
traits::sentinel_v<typename ReturnType::value_type>,
313-
std::forward<Self>(self).m_error
314-
};
315-
}
316-
} else {
317-
return std::invoke(f, std::forward<Self>(self).m_value);
318-
}
319-
}
320-
321266
/**
322267
* @brief Get the normal value
323268
*
@@ -340,6 +285,37 @@ class Result {
340285
return !std::holds_alternative<std::monostate>(error);
341286
}
342287

288+
/**
289+
* @brief and_then monadic function
290+
*
291+
* The callable must be able to return any error passed to it.
292+
* The callable must return a Result.
293+
*
294+
* @tparam Self deduced self type
295+
* @tparam F the type of the callable
296+
* @param self the current Result instance
297+
* @param f the callable
298+
* @return return type of callable
299+
*/
300+
template<typename Self, std::invocable<Self> F>
301+
constexpr auto and_then(this Self&& self, F&& f)
302+
requires traits::is_result_v<
303+
std::invoke_result_t<F, decltype(std::forward<Self>(self).m_value)>>
304+
&& traits::contains_all_v<
305+
std::invoke_result_t<F, decltype(std::forward<Self>(self).m_value)>,
306+
typename Self::error_types>
307+
{
308+
using ReturnType = std::invoke_result_t<F, decltype(std::forward<Self>(self).m_value)>;
309+
// if there is an error, return said error immediately
310+
if (self.has_error()) {
311+
return std::visit([](auto&& var) {
312+
return ReturnType(std::forward<decltype(var)>(var));
313+
}, std::forward<Self>(self));
314+
}
315+
// otherwise, invoke the callable and return the result
316+
return std::invoke(f, std::forward<Self>(self).m_value);
317+
}
318+
343319
constexpr operator T&() & {
344320
return value;
345321
}
@@ -355,10 +331,6 @@ class Result {
355331
constexpr operator const T&&() const&& {
356332
return std::move(value);
357333
}
358-
359-
// instead of wrapping the variant in std::optional, we can use std::monostate
360-
std::variant<std::monostate, Errs...> error;
361-
T value;
362334
}; // namespace zest
363335

364336
/**
@@ -432,6 +404,37 @@ class Result<void, Errs...> {
432404
return !std::holds_alternative<std::monostate>(error);
433405
}
434406

407+
/**
408+
* @brief and_then monadic function
409+
*
410+
* The callable must be able to return any error passed to it.
411+
* The callable must return a Result.
412+
*
413+
* @tparam Self deduced self type
414+
* @tparam F the type of the callable
415+
* @param self the current Result instance
416+
* @param f the callable
417+
* @return return type of callable
418+
*/
419+
template<typename Self, std::invocable<Self> F>
420+
constexpr auto and_then(this Self&& self, F&& f)
421+
requires traits::is_result_v<
422+
std::invoke_result_t<F, decltype(std::forward<Self>(self).m_value)>>
423+
&& traits::contains_all_v<
424+
std::invoke_result_t<F, decltype(std::forward<Self>(self).m_value)>,
425+
typename Self::error_types>
426+
{
427+
using ReturnType = std::invoke_result_t<F, decltype(std::forward<Self>(self).m_value)>;
428+
// if there is an error, return said error immediately
429+
if (self.has_error()) {
430+
return std::visit([](auto&& var) {
431+
return ReturnType(std::forward<decltype(var)>(var));
432+
}, std::forward<Self>(self));
433+
}
434+
// otherwise, invoke the callable and return the result
435+
return std::invoke(f, std::forward<Self>(self).m_value);
436+
}
437+
435438
private:
436439
// instead of wrapping the variant in std::optional, we can use std::monostate
437440
std::variant<std::monostate, Errs...> error; ///< Variant holding an error or monostate.

0 commit comments

Comments
 (0)