122 lines
3.7 KiB
C++
122 lines
3.7 KiB
C++
#pragma once
|
|
|
|
#include <string>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
#include <variant>
|
|
|
|
namespace rk3588 {
|
|
|
|
// Error type for Result
|
|
class Error {
|
|
public:
|
|
Error() = default;
|
|
explicit Error(std::string message) : message_(std::move(message)) {}
|
|
Error(std::string message, int code) : message_(std::move(message)), code_(code) {}
|
|
|
|
const std::string& Message() const { return message_; }
|
|
int Code() const { return code_; }
|
|
bool Empty() const { return message_.empty(); }
|
|
|
|
explicit operator bool() const { return !message_.empty(); }
|
|
|
|
private:
|
|
std::string message_;
|
|
int code_ = 0;
|
|
};
|
|
|
|
// Result<T> for functions that return a value or error
|
|
template <typename T>
|
|
class Result {
|
|
public:
|
|
// NOLINTNEXTLINE: implicit conversion is intentional for ergonomic API
|
|
Result(T value) noexcept(std::is_nothrow_move_constructible_v<T>)
|
|
: data_(std::move(value)) {}
|
|
// NOLINTNEXTLINE: implicit conversion is intentional for ergonomic API
|
|
Result(Error error) noexcept : data_(std::move(error)) {}
|
|
|
|
bool Ok() const noexcept { return std::holds_alternative<T>(data_); }
|
|
bool Failed() const noexcept { return !Ok(); }
|
|
explicit operator bool() const noexcept { return Ok(); }
|
|
|
|
const T& Value() const& { return std::get<T>(data_); }
|
|
T& Value() & { return std::get<T>(data_); }
|
|
T&& Value() && { return std::get<T>(std::move(data_)); }
|
|
|
|
const T& ValueOr(const T& def) const& {
|
|
return Ok() ? std::get<T>(data_) : def;
|
|
}
|
|
|
|
const Error& Err() const { return std::get<Error>(data_); }
|
|
const std::string& ErrMessage() const {
|
|
static const std::string kEmpty;
|
|
return Failed() ? std::get<Error>(data_).Message() : kEmpty;
|
|
}
|
|
|
|
// Convenience: extract value, moving it out
|
|
T Take() { return std::get<T>(std::move(data_)); }
|
|
|
|
private:
|
|
std::variant<T, Error> data_;
|
|
};
|
|
|
|
// Result<void> specialization for functions that only succeed/fail
|
|
template <>
|
|
class Result<void> {
|
|
public:
|
|
Result() noexcept : error_() {}
|
|
// NOLINTNEXTLINE: implicit conversion is intentional for ergonomic API
|
|
Result(Error error) noexcept : error_(std::move(error)) {}
|
|
|
|
static Result Ok() noexcept { return Result(); }
|
|
static Result Fail(std::string message) { return Result(Error(std::move(message))); }
|
|
static Result Fail(std::string message, int code) {
|
|
return Result(Error(std::move(message), code));
|
|
}
|
|
|
|
bool IsOk() const noexcept { return error_.Empty(); }
|
|
bool Failed() const noexcept { return !IsOk(); }
|
|
explicit operator bool() const noexcept { return IsOk(); }
|
|
|
|
const Error& Err() const { return error_; }
|
|
const std::string& ErrMessage() const { return error_.Message(); }
|
|
|
|
private:
|
|
Error error_;
|
|
};
|
|
|
|
// Type aliases for common patterns
|
|
using Status = Result<void>;
|
|
|
|
// Helper macros for early return on error
|
|
#define RK_TRY(expr) \
|
|
do { \
|
|
auto _result = (expr); \
|
|
if (_result.Failed()) { \
|
|
return _result.Err(); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define RK_TRY_ASSIGN(var, expr) \
|
|
auto _result_##var = (expr); \
|
|
if (_result_##var.Failed()) { \
|
|
return _result_##var.Err(); \
|
|
} \
|
|
var = std::move(_result_##var).Take()
|
|
|
|
// Helper functions
|
|
inline Status OkStatus() { return Status::Ok(); }
|
|
inline Status FailStatus(std::string msg) { return Status::Fail(std::move(msg)); }
|
|
|
|
template <typename T>
|
|
Result<T> MakeResult(T value) {
|
|
return Result<T>(std::move(value));
|
|
}
|
|
|
|
template <typename T>
|
|
Result<T> MakeError(std::string message) {
|
|
return Result<T>(Error(std::move(message)));
|
|
}
|
|
|
|
} // namespace rk3588
|