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 #define Musique_Header_HH
#include <chrono> #include <chrono>
#include <concepts>
#include <cstdint> #include <cstdint>
#include <cstring> #include <cstring>
#include <functional> #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. /// Shorthand for forwarding error values with Result type family.
/// ///
/// This implementation requires C++ language extension: statement expressions /// This implementation requires C++ language extension: statement expressions
/// It's supported by GCC and Clang, other compilers i don't know. /// It's supported by GCC and Clang, other compilers i don't know.
/// Inspired by SerenityOS TRY macro /// Inspired by SerenityOS TRY macro
#define Try(Value) \ #define Try(Value) \
({ \ ({ \
auto try_value = (Value); \ auto try_value = (Value); \
if (not try_value.has_value()) [[unlikely]] \ using Trait [[maybe_unused]] = Try_Traits<std::decay_t<decltype(try_value)>>; \
return tl::unexpected(try_value.error()); \ if (not Trait::is_ok(try_value)) [[unlikely]] \
std::move(try_value).value(); \ 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 /// 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 /// There is always at least one context
std::vector<Context> context_stack; 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; struct Incoming_Midi_Callbacks;
std::unique_ptr<Incoming_Midi_Callbacks> callbacks; std::unique_ptr<Incoming_Midi_Callbacks> callbacks;
@ -1056,7 +1123,7 @@ struct Interpreter
void leave_scope(); void leave_scope();
/// Play note resolving any missing parameters with context via `midi_connection` member. /// 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 /// Add to global interpreter scope all builtin function definitions
/// ///
@ -1150,7 +1217,7 @@ struct Value_Formatter
Value_Formatter nest(Context nested = Free) const; 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); Result<std::string> format(Interpreter &i, Value const& value);

View File

@ -27,14 +27,14 @@ struct Guard
return Error { std::move(error) }; return Error { std::move(error) };
} }
inline Result<void> yield_result() const inline std::optional<Error> yield_result() const
{ {
return yield_error(); 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 }; 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) { constexpr std::size_t hash_combine(std::size_t lhs, std::size_t rhs) {
return lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2); 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 /// Create chord out of given notes
template<Iterable T> 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) { for (auto i = 0u; i < args.size(); ++i) {
Value arg; 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 /// Plays sequentialy notes walking into arrays and evaluation blocks
/// ///
/// @invariant default_action is play one /// @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) { switch (v.type) {
break; case Value::Type::Array: break; case Value::Type::Array:
@ -250,7 +250,7 @@ static inline Result<void> sequential_play(Interpreter &i, Value v)
} }
/// Play what's given /// 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))); Try(sequential_play(i, std::move(v)));
return {}; return {};
@ -335,7 +335,7 @@ static Result<Value> builtin_sim(Interpreter &interpreter, std::vector<Value> ar
struct { struct {
Interpreter &interpreter; 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) { if (arg.type == Value::Type::Music) {
track.push_back(std::move(arg).chord); 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 // Invalid type for sim function
return errors::Unsupported_Types_For { return Error{errors::Unsupported_Types_For {
.type = errors::Unsupported_Types_For::Function, .type = errors::Unsupported_Types_For::Function,
.name = "sim", .name = "sim",
.possibilities = { .possibilities = {
"(music | array of music)+" "(music | array of music)+"
}, },
}; }};
} }
} append { interpreter }; } append { interpreter };

View File

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

View File

@ -153,7 +153,7 @@ struct Runner
} }
/// Run given source /// 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)); 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); Lines::the.add_line("<repl>", raw, repl_line_number);
auto result = runner.run(raw, "<repl>", true); 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::cout << std::flush;
std::cerr << result.error() << std::flush; std::cerr << Traits::yield_error(std::move(result)) << std::flush;
} }
repl_line_number++; repl_line_number++;
// We don't free input line since there could be values that still relay on it // We don't free input line since there could be values that still relay on it