musique/musique/try.hh
2022-10-14 14:42:37 +02:00

88 lines
2.4 KiB
C++

#ifndef MUSIQUE_TRY_HH
#define MUSIQUE_TRY_HH
#include <musique/result.hh>
/// Shorthand for forwarding error values with Result type family.
///
/// This implementation requires C++ language extension: statement expressions
/// It's supported by GCC and Clang, other compilers i don't know.
/// Inspired by SerenityOS TRY macro
#define Try(Value) \
({ \
auto try_value = (Value); \
using Trait [[maybe_unused]] = Try_Traits<std::decay_t<decltype(try_value)>>; \
if (not Trait::is_ok(try_value)) [[unlikely]] \
return Trait::yield_error(std::move(try_value)); \
Trait::yield_value(std::move(try_value)); \
})
/// Abstraction over any value that are either value or error
///
/// Inspired by P2561R0
template<typename = void>
struct Try_Traits
{
template<typename T>
static constexpr bool is_ok(T const& v) { return Try_Traits<T>::is_ok(v); }
template<typename T>
static constexpr auto yield_value(T&& v) { return Try_Traits<T>::yield_value(std::forward<T>(v)); }
template<typename T>
static constexpr auto yield_error(T&& v) { return Try_Traits<T>::yield_error(std::forward<T>(v)); }
};
template<>
struct Try_Traits<std::optional<Error>>
{
using Value_Type = std::nullopt_t;
using Error_Type = Error;
static constexpr bool is_ok(std::optional<Error> const& o)
{
return not o.has_value();
}
static std::nullopt_t yield_value(std::optional<Error>&& err)
{
ensure(not err.has_value(), "Trying to yield value from optional that contains error");
return std::nullopt;
}
static Error yield_error(std::optional<Error>&& err)
{
ensure(err.has_value(), "Trying to yield value from optional that NOT constains error");
return std::move(*err);
}
};
template<typename T>
struct Try_Traits<Result<T>>
{
using Value_Type = T;
using Error_Type = Error;
static constexpr bool is_ok(Result<T> const& o)
{
return o.has_value();
}
static auto yield_value(Result<T> val)
{
ensure(val.has_value(), "Trying to yield value from expected that contains error");
if constexpr (std::is_void_v<T>) {
} else {
return std::move(*val);
}
}
static Error yield_error(Result<T>&& val)
{
ensure(not val.has_value(), "Trying to yield error from expected with value");
return std::move(val.error());
}
};
#endif