#pragma once #include #include #include #include #include #include extern const char* error_string(int error); struct Error { Error(int err) { error = err; } int error; }; template class Result { public: Result(const T& value) : m_value(value) { } Result(T&& value) : m_value(move(value)) { } Result(const Result& other) : m_value(other.m_value), m_error(other.m_error) { } Result(Result&& other) : m_value(move(other.m_value)), m_error(other.m_error) { } Result(const Error& err) : m_value(), m_error(err.error) { } Result& operator=(const Result& other) { if (this == &other) return *this; m_error = other.m_error; m_value = other.m_value; return *this; } Result& operator=(Result&& other) { if (this == &other) return *this; m_error = other.m_error; m_value = move(other.m_value); return *this; } bool has_error() const { return !m_value.has_value(); } bool has_value() const { return m_value.has_value(); } int error(SourceLocation caller = SourceLocation::current()) const { expect_at(has_error(), caller, "Result::error() called on a Result that holds a value"); return m_error; } Error release_error(SourceLocation caller = SourceLocation::current()) const { expect_at(has_error(), caller, "Result::release_error() called on a Result that holds a value"); return { m_error }; } const char* error_string(SourceLocation caller = SourceLocation::current()) const { expect_at(has_error(), caller, "Result::error_string() called on a Result that holds a value"); return ::error_string(m_error); } T value(SourceLocation caller = SourceLocation::current()) const { expect_at(has_value(), caller, "Result::value() called on a Result that holds an error"); return m_value.unchecked_value({}); } T expect_value(const char* reason, SourceLocation caller = SourceLocation::current()) const { expect_at(has_value(), caller, reason); return m_value.unchecked_value({}); } T value_or(const T& other) const { return m_value.value_or(other); } T value_or(T&& other) const { return m_value.value_or(move(other)); } bool try_set_value(T& ref) const { return m_value.try_set_value(ref); } bool try_set_value_or_error(T& ref, int& err) const { bool ok = m_value.try_set_value(ref); if (!ok) err = m_error; return ok; } bool try_move_value(T& ref) { return m_value.try_move_value(ref); } bool try_move_value_or_error(T& ref, int& err) { bool ok = m_value.try_move_value(ref); if (!ok) err = m_error; return ok; } T release_value(SourceLocation caller = SourceLocation::current()) { expect_at(has_value(), caller, "Result::release_value() called on a Result that holds an error"); return m_value.unchecked_release_value({}); } T expect_release_value(const char* reason, SourceLocation caller = SourceLocation::current()) { expect_at(has_value(), caller, reason); return m_value.unchecked_release_value({}); } static Result from_option(Option&& option, int error) { if (option.has_value()) { return option.unchecked_release_value({}); } else return Error { error }; } static Result from_syscall(long rc) { if (rc < 0) return Error { (int)-rc }; else return (T)rc; } private: Option m_value; int m_error; }; template <> class Result { public: Result() : m_has_error(false) { } Result(const Result& other) : m_has_error(other.m_has_error), m_error(other.m_error) { } Result(Result&& other) : m_has_error(other.m_has_error), m_error(other.m_error) { } Result(const Error& err) : m_has_error(true), m_error(err.error) { } Result& operator=(const Result& other) { if (this == &other) return *this; m_has_error = other.m_has_error; m_error = other.m_error; return *this; } Result& operator=(Result&& other) { if (this == &other) return *this; m_has_error = other.m_has_error; m_error = other.m_error; return *this; } static Result from_syscall(long rc) { if (rc < 0) return Error { (int)-rc }; return {}; } bool has_error() const { return m_has_error; } bool has_value() const { return !m_has_error; } int error(SourceLocation caller = SourceLocation::current()) const { expect_at(has_error(), caller, "Result::error() called on a Result that holds a value"); return m_error; } Error release_error(SourceLocation caller = SourceLocation::current()) const { expect_at(has_error(), caller, "Result::release_error() called on a Result that holds a value"); return { m_error }; } const char* error_string(SourceLocation caller = SourceLocation::current()) const { expect_at(has_error(), caller, "Result::error_string() called on a Result that holds a value"); return ::error_string(m_error); } void value(SourceLocation caller = SourceLocation::current()) const { expect_at(has_value(), caller, "Result::value() called on a Result that holds an error"); } void expect_value(const char* reason, SourceLocation caller = SourceLocation::current()) const { expect_at(has_value(), caller, reason); } void release_value(SourceLocation caller = SourceLocation::current()) const { expect_at(has_value(), caller, "Result::release_value() called on a Result that holds an error"); } void expect_release_value(const char* reason, SourceLocation caller = SourceLocation::current()) const { expect_at(has_value(), caller, reason); } private: bool m_has_error; int m_error; }; // clang-format off #define err(x) Error{x} // clang-format on #define TRY(expr) \ ({ \ auto _expr_rc = (expr); \ if (!_expr_rc.has_value()) return _expr_rc.release_error(); \ _expr_rc.release_value(); \ }) template inline Result nonnull_or_error(T* ptr, int error) { if (ptr == nullptr) return err(error); else return ptr; }