Remove dangerous result/option macros

This commit is contained in:
Jackson Coxson
2025-09-03 19:48:43 -06:00
parent ca56575c6c
commit b0e3c5769a
8 changed files with 215 additions and 201 deletions

View File

@@ -8,7 +8,6 @@
#pragma once
#include <cstdio>
#include <stdexcept>
#include <type_traits>
#include <utility>
@@ -106,6 +105,8 @@ template <typename T> class Option {
// unwrap_or / unwrap_or_else
T unwrap_or(T default_value) const& { return has_ ? *ptr() : std::move(default_value); }
T unwrap_or(T default_value) && { return has_ ? std::move(*ptr()) : std::move(default_value); }
T unwrap_or(const T& default_value) const& { return has_ ? *ptr() : default_value; }
T unwrap_or(T&& default_value) const& { return has_ ? *ptr() : std::move(default_value); }
template <typename F> T unwrap_or_else(F&& f) const& {
return has_ ? *ptr() : static_cast<T>(f());
@@ -123,6 +124,16 @@ template <typename T> class Option {
}
return Option<U>(None);
}
template <typename F>
auto map(F&& f) && -> Option<typename std::decay<decltype(f(std::move(*ptr())))>::type> {
using U = typename std::decay<decltype(f(std::move(*ptr())))>::type;
if (has_) {
// Move the value into the function
return Option<U>(f(std::move(*ptr())));
}
return Option<U>(None);
}
};
// Helpers
@@ -131,21 +142,17 @@ template <typename T> inline Option<typename std::decay<T>::type> Some(T&& v) {
}
inline Option<void> Some() = delete; // no Option<void>
// template <typename T> inline Option<T> None() {
// return Option<T>(none);
// } // still needs T specified
// Prefer this at call sites (lets return-type drive the type):
// return none;
#define match_option(opt, SOME, NONE) \
if ((opt).is_some()) { \
auto&& SOME = (opt).unwrap();
#define or_else \
} \
else { \
NONE; \
}
#define match_option(opt, some_name, some_block, none_block) \
/* NOTE: you may return in a block, but not break/continue */ \
do { \
auto&& _option_val = (opt); \
if (_option_val.is_some()) { \
auto&& some_name = _option_val.unwrap(); \
some_block \
} else { \
none_block \
} \
} while (0)
// --- Option helpers: if_let_some / if_let_some_move / if_let_none ---
@@ -154,6 +161,7 @@ inline Option<void> Some() = delete; // no Option<void>
/* Bind a reference to the contained value if Some(...) */
#define if_let_some(expr, name, block) \
/* NOTE: you may return in a block, but not break/continue */ \
do { \
auto _opt_unique(_opt_) = (expr); \
if (_opt_unique(_opt_).is_some()) { \
@@ -164,6 +172,7 @@ inline Option<void> Some() = delete; // no Option<void>
/* Move the contained value out (consumes the Option) if Some(...) */
#define if_let_some_move(expr, name, block) \
/* NOTE: you may return in a block, but not break/continue */ \
do { \
auto _opt_unique(_opt_) = (expr); \
if (_opt_unique(_opt_).is_some()) { \
@@ -172,13 +181,4 @@ inline Option<void> Some() = delete; // no Option<void>
} \
} while (0)
/* Run a block if the option is None */
#define if_let_none(expr, block) \
do { \
auto _opt_unique(_opt_) = (expr); \
if (_opt_unique(_opt_).is_none()) { \
block \
} \
} while (0)
} // namespace IdeviceFFI

View File

@@ -76,6 +76,57 @@ template <typename T, typename E> class Result {
}
}
// Copy Assignment
Result& operator=(const Result& other) {
// Prevent self-assignment
if (this == &other) {
return *this;
}
// Destroy the current value
if (is_ok_) {
ok_value_.~T();
} else {
err_value_.~E();
}
is_ok_ = other.is_ok_;
// Construct the new value
if (is_ok_) {
new (&ok_value_) T(other.ok_value_);
} else {
new (&err_value_) E(other.err_value_);
}
return *this;
}
// Move Assignment
Result& operator=(Result&& other) noexcept {
if (this == &other) {
return *this;
}
// Destroy the current value
if (is_ok_) {
ok_value_.~T();
} else {
err_value_.~E();
}
is_ok_ = other.is_ok_;
// Construct the new value by moving
if (is_ok_) {
new (&ok_value_) T(std::move(other.ok_value_));
} else {
new (&err_value_) E(std::move(other.err_value_));
}
return *this;
}
bool is_ok() const { return is_ok_; }
bool is_err() const { return !is_ok_; }
@@ -132,6 +183,32 @@ template <typename T, typename E> class Result {
T unwrap_or(T&& default_value) const { return is_ok_ ? ok_value_ : std::move(default_value); }
T expect(const char* message) && {
if (is_err()) {
std::fprintf(stderr, "Fatal (expect) error: %s\n", message);
std::terminate();
}
return std::move(ok_value_);
}
// Returns a mutable reference from an lvalue Result
T& expect(const char* message) & {
if (is_err()) {
std::fprintf(stderr, "Fatal (expect) error: %s\n", message);
std::terminate();
}
return ok_value_;
}
// Returns a const reference from a const lvalue Result
const T& expect(const char* message) const& {
if (is_err()) {
std::fprintf(stderr, "Fatal (expect) error: %s\n", message);
std::terminate();
}
return ok_value_;
}
template <typename F> T unwrap_or_else(F&& f) & {
return is_ok_ ? ok_value_ : static_cast<T>(f(err_value_));
}
@@ -206,26 +283,24 @@ template <typename E> class Result<void, E> {
}
return err_value_;
}
void expect(const char* message) const {
if (is_err()) {
std::fprintf(stderr, "Fatal (expect) error: %s\n", message);
std::terminate();
}
}
};
#define match_result(res, ok_name, ok_block, err_name, err_block) \
if ((res).is_ok()) { \
auto&& ok_name = (res).unwrap(); \
ok_block \
} else { \
auto&& err_name = (res).unwrap_err(); \
err_block \
}
#define if_let_err(res, name, block) \
if ((res).is_err()) { \
auto&& name = (res).unwrap_err(); \
block \
}
#define if_let_ok(res, name, block) \
if ((res).is_ok()) { \
auto&& name = (res).unwrap(); \
block \
}
do { \
auto&& _result_val = (res); \
if (_result_val.is_ok()) { \
auto&& ok_name = _result_val.unwrap(); \
ok_block \
} else { \
auto&& err_name = _result_val.unwrap_err(); \
err_block \
} \
} while (0)
} // namespace IdeviceFFI