Moved from Result<void> to std::optional<Error>

This commit is contained in:
Robert Bendun 2022-09-23 13:59:35 +02:00
parent d9c0468729
commit 038de0fc27
5 changed files with 93 additions and 25 deletions

View File

@ -2,7 +2,6 @@
#define Musique_Header_HH
#include <chrono>
#include <concepts>
#include <cstdint>
#include <cstring>
#include <functional>
@ -390,17 +389,85 @@ struct [[nodiscard("This value may contain critical error, so it should NOT be i
}
};
/// 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)
{
assert(not err.has_value(), "Trying to yield value from optional that contains error");
return std::nullopt;
}
static Error yield_error(std::optional<Error>&& err)
{
assert(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)
{
assert(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)
{
assert(not val.has_value(), "Trying to yield error from expected with value");
return std::move(val.error());
}
};
/// 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); \
if (not try_value.has_value()) [[unlikely]] \
return tl::unexpected(try_value.error()); \
std::move(try_value).value(); \
#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)); \
})
/// Drop in replacement for bool when C++ implcit conversions stand in your way
@ -1035,7 +1102,7 @@ struct Interpreter
/// There is always at least one context
std::vector<Context> context_stack;
std::function<Result<void>(Interpreter&, Value)> default_action;
std::function<std::optional<Error>(Interpreter&, Value)> default_action;
struct Incoming_Midi_Callbacks;
std::unique_ptr<Incoming_Midi_Callbacks> callbacks;
@ -1056,7 +1123,7 @@ struct Interpreter
void leave_scope();
/// Play note resolving any missing parameters with context via `midi_connection` member.
Result<void> play(Chord);
std::optional<Error> play(Chord);
/// Add to global interpreter scope all builtin function definitions
///
@ -1150,7 +1217,7 @@ struct Value_Formatter
Value_Formatter nest(Context nested = Free) const;
Result<void> format(std::ostream& os, Interpreter &interpreter, Value const& value);
std::optional<Error> format(std::ostream& os, Interpreter &interpreter, Value const& value);
};
Result<std::string> format(Interpreter &i, Value const& value);

View File

@ -27,14 +27,14 @@ struct Guard
return Error { std::move(error) };
}
inline Result<void> yield_result() const
inline std::optional<Error> yield_result() const
{
return yield_error();
}
inline Result<void> operator()(bool(*predicate)(Value::Type), Value const& v) const
inline std::optional<Error> operator()(bool(*predicate)(Value::Type), Value const& v) const
{
return predicate(v.type) ? Result<void>{} : yield_result();
return predicate(v.type) ? std::optional<Error>{} : yield_result();
}
};
@ -98,7 +98,7 @@ struct Interpreter::Incoming_Midi_Callbacks
};
enum class Midi_Connection_Type { Output, Input };
Result<void> ensure_midi_connection_available(Interpreter&, Midi_Connection_Type, std::string_view operation_name);
std::optional<Error> ensure_midi_connection_available(Interpreter&, Midi_Connection_Type, std::string_view operation_name);
constexpr std::size_t hash_combine(std::size_t lhs, std::size_t rhs) {
return lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2);

View File

@ -35,7 +35,7 @@ concept Iterable = (With_Index_Method<T> || With_Index_Operator<T>) && requires
/// Create chord out of given notes
template<Iterable T>
static inline Result<void> create_chord(std::vector<Note> &chord, Interpreter &interpreter, T args)
static inline std::optional<Error> create_chord(std::vector<Note> &chord, Interpreter &interpreter, T args)
{
for (auto i = 0u; i < args.size(); ++i) {
Value arg;
@ -229,7 +229,7 @@ static auto builtin_program_change(Interpreter &i, std::vector<Value> args) -> R
/// Plays sequentialy notes walking into arrays and evaluation blocks
///
/// @invariant default_action is play one
static inline Result<void> sequential_play(Interpreter &i, Value v)
static inline std::optional<Error> sequential_play(Interpreter &i, Value v)
{
switch (v.type) {
break; case Value::Type::Array:
@ -250,7 +250,7 @@ static inline Result<void> sequential_play(Interpreter &i, Value v)
}
/// Play what's given
static Result<void> action_play(Interpreter &i, Value v)
static std::optional<Error> action_play(Interpreter &i, Value v)
{
Try(sequential_play(i, std::move(v)));
return {};
@ -335,7 +335,7 @@ static Result<Value> builtin_sim(Interpreter &interpreter, std::vector<Value> ar
struct {
Interpreter &interpreter;
Result<void> operator()(std::vector<Chord> &track, Value &arg)
std::optional<Error> operator()(std::vector<Chord> &track, Value &arg)
{
if (arg.type == Value::Type::Music) {
track.push_back(std::move(arg).chord);
@ -351,13 +351,13 @@ static Result<Value> builtin_sim(Interpreter &interpreter, std::vector<Value> ar
}
// Invalid type for sim function
return errors::Unsupported_Types_For {
return Error{errors::Unsupported_Types_For {
.type = errors::Unsupported_Types_For::Function,
.name = "sim",
.possibilities = {
"(music | array of music)+"
},
};
}};
}
} append { interpreter };

View File

@ -217,7 +217,7 @@ void Interpreter::leave_scope()
env = env->leave();
}
Result<void> Interpreter::play(Chord chord)
std::optional<Error> Interpreter::play(Chord chord)
{
Try(ensure_midi_connection_available(*this, Midi_Connection_Type::Output, "play"));
auto &ctx = context_stack.back();
@ -256,7 +256,7 @@ Result<void> Interpreter::play(Chord chord)
return {};
}
Result<void> ensure_midi_connection_available(Interpreter &i, Midi_Connection_Type m, std::string_view operation_name)
std::optional<Error> ensure_midi_connection_available(Interpreter &i, Midi_Connection_Type m, std::string_view operation_name)
{
switch (m) {
break; case Midi_Connection_Type::Output:

View File

@ -153,7 +153,7 @@ struct Runner
}
/// Run given source
Result<void> run(std::string_view source, std::string_view filename, bool output = false)
std::optional<Error> run(std::string_view source, std::string_view filename, bool output = false)
{
auto ast = Try(Parser::parse(source, filename, repl_line_number));
@ -326,9 +326,10 @@ static Result<void> Main(std::span<char const*> args)
Lines::the.add_line("<repl>", raw, repl_line_number);
auto result = runner.run(raw, "<repl>", true);
if (not result.has_value()) {
using Traits = Try_Traits<std::decay_t<decltype(result)>>;
if (not Traits::is_ok(result)) {
std::cout << std::flush;
std::cerr << result.error() << std::flush;
std::cerr << Traits::yield_error(std::move(result)) << std::flush;
}
repl_line_number++;
// We don't free input line since there could be values that still relay on it