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 )
172173class 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 )
215216class 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