Refactor out optional and result
This commit is contained in:
parent
1bcfcff021
commit
76a21d266a
@ -1,6 +1,7 @@
|
||||
#ifndef ERROR_HPP
|
||||
#define ERROR_HPP
|
||||
|
||||
#include <atomic>
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
@ -164,6 +165,8 @@ namespace detail {
|
||||
// Check condition in both debug. std::runtime_error on failure.
|
||||
#define ASSERT(...) PHONY_USE(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
extern std::atomic_bool absorb_trace_exceptions;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
154
src/utils/optional.hpp
Normal file
154
src/utils/optional.hpp
Normal file
@ -0,0 +1,154 @@
|
||||
#ifndef OPTIONAL_HPP
|
||||
#define OPTIONAL_HPP
|
||||
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "utils/common.hpp"
|
||||
#include "utils/error.hpp"
|
||||
|
||||
namespace cpptrace {
|
||||
namespace detail {
|
||||
struct nullopt_t {};
|
||||
|
||||
static constexpr nullopt_t nullopt;
|
||||
|
||||
template<
|
||||
typename T,
|
||||
typename std::enable_if<!std::is_same<typename std::decay<T>::type, void>::value, int>::type = 0
|
||||
>
|
||||
class optional {
|
||||
union {
|
||||
char x;
|
||||
T uvalue;
|
||||
};
|
||||
|
||||
bool holds_value = false;
|
||||
|
||||
public:
|
||||
optional() noexcept {}
|
||||
|
||||
optional(nullopt_t) noexcept {}
|
||||
|
||||
~optional() {
|
||||
reset();
|
||||
}
|
||||
|
||||
optional(const optional& other) : holds_value(other.holds_value) {
|
||||
if(holds_value) {
|
||||
new (static_cast<void*>(std::addressof(uvalue))) T(other.uvalue);
|
||||
}
|
||||
}
|
||||
|
||||
optional(optional&& other)
|
||||
noexcept(std::is_nothrow_move_constructible<T>::value)
|
||||
: holds_value(other.holds_value)
|
||||
{
|
||||
if(holds_value) {
|
||||
new (static_cast<void*>(std::addressof(uvalue))) T(std::move(other.uvalue));
|
||||
}
|
||||
}
|
||||
|
||||
optional& operator=(const optional& other) {
|
||||
optional copy(other);
|
||||
swap(copy);
|
||||
return *this;
|
||||
}
|
||||
|
||||
optional& operator=(optional&& other)
|
||||
noexcept(std::is_nothrow_move_assignable<T>::value && std::is_nothrow_move_constructible<T>::value)
|
||||
{
|
||||
reset();
|
||||
if(other.holds_value) {
|
||||
new (static_cast<void*>(std::addressof(uvalue))) T(std::move(other.uvalue));
|
||||
holds_value = true;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<
|
||||
typename U = T,
|
||||
typename std::enable_if<!std::is_same<typename std::decay<U>::type, optional<T>>::value, int>::type = 0
|
||||
>
|
||||
optional(U&& value) : holds_value(true) {
|
||||
new (static_cast<void*>(std::addressof(uvalue))) T(std::forward<U>(value));
|
||||
}
|
||||
|
||||
template<
|
||||
typename U = T,
|
||||
typename std::enable_if<!std::is_same<typename std::decay<U>::type, optional<T>>::value, int>::type = 0
|
||||
>
|
||||
optional& operator=(U&& value) {
|
||||
optional o(std::forward<U>(value));
|
||||
swap(o);
|
||||
return *this;
|
||||
}
|
||||
|
||||
optional& operator=(nullopt_t) noexcept {
|
||||
reset();
|
||||
return *this;
|
||||
}
|
||||
|
||||
void swap(optional& other) noexcept {
|
||||
if(holds_value && other.holds_value) {
|
||||
std::swap(uvalue, other.uvalue);
|
||||
} else if(holds_value && !other.holds_value) {
|
||||
new (&other.uvalue) T(std::move(uvalue));
|
||||
uvalue.~T();
|
||||
} else if(!holds_value && other.holds_value) {
|
||||
new (static_cast<void*>(std::addressof(uvalue))) T(std::move(other.uvalue));
|
||||
other.uvalue.~T();
|
||||
}
|
||||
std::swap(holds_value, other.holds_value);
|
||||
}
|
||||
|
||||
bool has_value() const {
|
||||
return holds_value;
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
return holds_value;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
if(holds_value) {
|
||||
uvalue.~T();
|
||||
}
|
||||
holds_value = false;
|
||||
}
|
||||
|
||||
NODISCARD T& unwrap() & {
|
||||
ASSERT(holds_value, "Optional does not contain a value");
|
||||
return uvalue;
|
||||
}
|
||||
|
||||
NODISCARD const T& unwrap() const & {
|
||||
ASSERT(holds_value, "Optional does not contain a value");
|
||||
return uvalue;
|
||||
}
|
||||
|
||||
NODISCARD T&& unwrap() && {
|
||||
ASSERT(holds_value, "Optional does not contain a value");
|
||||
return std::move(uvalue);
|
||||
}
|
||||
|
||||
NODISCARD const T&& unwrap() const && {
|
||||
ASSERT(holds_value, "Optional does not contain a value");
|
||||
return std::move(uvalue);
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
NODISCARD T value_or(U&& default_value) const & {
|
||||
return holds_value ? uvalue : static_cast<T>(std::forward<U>(default_value));
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
NODISCARD T value_or(U&& default_value) && {
|
||||
return holds_value ? std::move(uvalue) : static_cast<T>(std::forward<U>(default_value));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
129
src/utils/result.hpp
Normal file
129
src/utils/result.hpp
Normal file
@ -0,0 +1,129 @@
|
||||
#ifndef RESULT_HPP
|
||||
#define RESULT_HPP
|
||||
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "utils/common.hpp"
|
||||
#include "utils/error.hpp"
|
||||
#include "utils/optional.hpp"
|
||||
|
||||
namespace cpptrace {
|
||||
namespace detail {
|
||||
template<typename T, typename E, typename std::enable_if<!std::is_same<T, E>::value, int>::type = 0>
|
||||
class Result {
|
||||
union {
|
||||
T value_;
|
||||
E error_;
|
||||
};
|
||||
enum class member { value, error };
|
||||
member active;
|
||||
public:
|
||||
Result(T&& value) : value_(std::move(value)), active(member::value) {}
|
||||
Result(E&& error) : error_(std::move(error)), active(member::error) {
|
||||
if(!absorb_trace_exceptions.load()) {
|
||||
std::fprintf(stderr, "%s\n", unwrap_error().what());
|
||||
}
|
||||
}
|
||||
Result(T& value) : value_(T(value)), active(member::value) {}
|
||||
Result(E& error) : error_(E(error)), active(member::error) {
|
||||
if(!absorb_trace_exceptions.load()) {
|
||||
std::fprintf(stderr, "%s\n", unwrap_error().what());
|
||||
}
|
||||
}
|
||||
Result(Result&& other) : active(other.active) {
|
||||
if(other.active == member::value) {
|
||||
new (&value_) T(std::move(other.value_));
|
||||
} else {
|
||||
new (&error_) E(std::move(other.error_));
|
||||
}
|
||||
}
|
||||
~Result() {
|
||||
if(active == member::value) {
|
||||
value_.~T();
|
||||
} else {
|
||||
error_.~E();
|
||||
}
|
||||
}
|
||||
|
||||
bool has_value() const {
|
||||
return active == member::value;
|
||||
}
|
||||
|
||||
bool is_error() const {
|
||||
return active == member::error;
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
return has_value();
|
||||
}
|
||||
|
||||
NODISCARD optional<T> value() const & {
|
||||
return has_value() ? value_ : nullopt;
|
||||
}
|
||||
|
||||
NODISCARD optional<E> error() const & {
|
||||
return is_error() ? error_ : nullopt;
|
||||
}
|
||||
|
||||
NODISCARD optional<T> value() && {
|
||||
return has_value() ? std::move(value_) : nullopt;
|
||||
}
|
||||
|
||||
NODISCARD optional<E> error() && {
|
||||
return is_error() ? std::move(error_) : nullopt;
|
||||
}
|
||||
|
||||
NODISCARD T& unwrap_value() & {
|
||||
ASSERT(has_value(), "Result does not contain a value");
|
||||
return value_;
|
||||
}
|
||||
|
||||
NODISCARD const T& unwrap_value() const & {
|
||||
ASSERT(has_value(), "Result does not contain a value");
|
||||
return value_;
|
||||
}
|
||||
|
||||
NODISCARD T unwrap_value() && {
|
||||
ASSERT(has_value(), "Result does not contain a value");
|
||||
return std::move(value_);
|
||||
}
|
||||
|
||||
NODISCARD E& unwrap_error() & {
|
||||
ASSERT(is_error(), "Result does not contain an error");
|
||||
return error_;
|
||||
}
|
||||
|
||||
NODISCARD const E& unwrap_error() const & {
|
||||
ASSERT(is_error(), "Result does not contain an error");
|
||||
return error_;
|
||||
}
|
||||
|
||||
NODISCARD E unwrap_error() && {
|
||||
ASSERT(is_error(), "Result does not contain an error");
|
||||
return std::move(error_);
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
NODISCARD T value_or(U&& default_value) const & {
|
||||
return has_value() ? value_ : static_cast<T>(std::forward<U>(default_value));
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
NODISCARD T value_or(U&& default_value) && {
|
||||
return has_value() ? std::move(value_) : static_cast<T>(std::forward<U>(default_value));
|
||||
}
|
||||
|
||||
void drop_error() const {
|
||||
if(is_error()) {
|
||||
std::fprintf(stderr, "%s\n", unwrap_error().what());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct monostate {};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -2,7 +2,6 @@
|
||||
#define UTILS_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
@ -16,6 +15,8 @@
|
||||
|
||||
#include "utils/common.hpp"
|
||||
#include "utils/error.hpp"
|
||||
#include "utils/optional.hpp"
|
||||
#include "utils/result.hpp"
|
||||
|
||||
namespace cpptrace {
|
||||
namespace detail {
|
||||
@ -147,260 +148,6 @@ namespace detail {
|
||||
static_assert(n_digits(11) == 2, "n_digits utility producing the wrong result");
|
||||
static_assert(n_digits(1024) == 4, "n_digits utility producing the wrong result");
|
||||
|
||||
struct nullopt_t {};
|
||||
|
||||
static constexpr nullopt_t nullopt;
|
||||
|
||||
template<
|
||||
typename T,
|
||||
typename std::enable_if<!std::is_same<typename std::decay<T>::type, void>::value, int>::type = 0
|
||||
>
|
||||
class optional {
|
||||
union {
|
||||
char x;
|
||||
T uvalue;
|
||||
};
|
||||
|
||||
bool holds_value = false;
|
||||
|
||||
public:
|
||||
optional() noexcept {}
|
||||
|
||||
optional(nullopt_t) noexcept {}
|
||||
|
||||
~optional() {
|
||||
reset();
|
||||
}
|
||||
|
||||
optional(const optional& other) : holds_value(other.holds_value) {
|
||||
if(holds_value) {
|
||||
new (static_cast<void*>(std::addressof(uvalue))) T(other.uvalue);
|
||||
}
|
||||
}
|
||||
|
||||
optional(optional&& other)
|
||||
noexcept(std::is_nothrow_move_constructible<T>::value)
|
||||
: holds_value(other.holds_value)
|
||||
{
|
||||
if(holds_value) {
|
||||
new (static_cast<void*>(std::addressof(uvalue))) T(std::move(other.uvalue));
|
||||
}
|
||||
}
|
||||
|
||||
optional& operator=(const optional& other) {
|
||||
optional copy(other);
|
||||
swap(copy);
|
||||
return *this;
|
||||
}
|
||||
|
||||
optional& operator=(optional&& other)
|
||||
noexcept(std::is_nothrow_move_assignable<T>::value && std::is_nothrow_move_constructible<T>::value)
|
||||
{
|
||||
reset();
|
||||
if(other.holds_value) {
|
||||
new (static_cast<void*>(std::addressof(uvalue))) T(std::move(other.uvalue));
|
||||
holds_value = true;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<
|
||||
typename U = T,
|
||||
typename std::enable_if<!std::is_same<typename std::decay<U>::type, optional<T>>::value, int>::type = 0
|
||||
>
|
||||
optional(U&& value) : holds_value(true) {
|
||||
new (static_cast<void*>(std::addressof(uvalue))) T(std::forward<U>(value));
|
||||
}
|
||||
|
||||
template<
|
||||
typename U = T,
|
||||
typename std::enable_if<!std::is_same<typename std::decay<U>::type, optional<T>>::value, int>::type = 0
|
||||
>
|
||||
optional& operator=(U&& value) {
|
||||
optional o(std::forward<U>(value));
|
||||
swap(o);
|
||||
return *this;
|
||||
}
|
||||
|
||||
optional& operator=(nullopt_t) noexcept {
|
||||
reset();
|
||||
return *this;
|
||||
}
|
||||
|
||||
void swap(optional& other) noexcept {
|
||||
if(holds_value && other.holds_value) {
|
||||
std::swap(uvalue, other.uvalue);
|
||||
} else if(holds_value && !other.holds_value) {
|
||||
new (&other.uvalue) T(std::move(uvalue));
|
||||
uvalue.~T();
|
||||
} else if(!holds_value && other.holds_value) {
|
||||
new (static_cast<void*>(std::addressof(uvalue))) T(std::move(other.uvalue));
|
||||
other.uvalue.~T();
|
||||
}
|
||||
std::swap(holds_value, other.holds_value);
|
||||
}
|
||||
|
||||
bool has_value() const {
|
||||
return holds_value;
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
return holds_value;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
if(holds_value) {
|
||||
uvalue.~T();
|
||||
}
|
||||
holds_value = false;
|
||||
}
|
||||
|
||||
NODISCARD T& unwrap() & {
|
||||
ASSERT(holds_value, "Optional does not contain a value");
|
||||
return uvalue;
|
||||
}
|
||||
|
||||
NODISCARD const T& unwrap() const & {
|
||||
ASSERT(holds_value, "Optional does not contain a value");
|
||||
return uvalue;
|
||||
}
|
||||
|
||||
NODISCARD T&& unwrap() && {
|
||||
ASSERT(holds_value, "Optional does not contain a value");
|
||||
return std::move(uvalue);
|
||||
}
|
||||
|
||||
NODISCARD const T&& unwrap() const && {
|
||||
ASSERT(holds_value, "Optional does not contain a value");
|
||||
return std::move(uvalue);
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
NODISCARD T value_or(U&& default_value) const & {
|
||||
return holds_value ? uvalue : static_cast<T>(std::forward<U>(default_value));
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
NODISCARD T value_or(U&& default_value) && {
|
||||
return holds_value ? std::move(uvalue) : static_cast<T>(std::forward<U>(default_value));
|
||||
}
|
||||
};
|
||||
|
||||
extern std::atomic_bool absorb_trace_exceptions;
|
||||
|
||||
template<typename T, typename E, typename std::enable_if<!std::is_same<T, E>::value, int>::type = 0>
|
||||
class Result {
|
||||
union {
|
||||
T value_;
|
||||
E error_;
|
||||
};
|
||||
enum class member { value, error };
|
||||
member active;
|
||||
public:
|
||||
Result(T&& value) : value_(std::move(value)), active(member::value) {}
|
||||
Result(E&& error) : error_(std::move(error)), active(member::error) {
|
||||
if(!absorb_trace_exceptions.load()) {
|
||||
std::fprintf(stderr, "%s\n", unwrap_error().what());
|
||||
}
|
||||
}
|
||||
Result(T& value) : value_(T(value)), active(member::value) {}
|
||||
Result(E& error) : error_(E(error)), active(member::error) {
|
||||
if(!absorb_trace_exceptions.load()) {
|
||||
std::fprintf(stderr, "%s\n", unwrap_error().what());
|
||||
}
|
||||
}
|
||||
Result(Result&& other) : active(other.active) {
|
||||
if(other.active == member::value) {
|
||||
new (&value_) T(std::move(other.value_));
|
||||
} else {
|
||||
new (&error_) E(std::move(other.error_));
|
||||
}
|
||||
}
|
||||
~Result() {
|
||||
if(active == member::value) {
|
||||
value_.~T();
|
||||
} else {
|
||||
error_.~E();
|
||||
}
|
||||
}
|
||||
|
||||
bool has_value() const {
|
||||
return active == member::value;
|
||||
}
|
||||
|
||||
bool is_error() const {
|
||||
return active == member::error;
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
return has_value();
|
||||
}
|
||||
|
||||
NODISCARD optional<T> value() const & {
|
||||
return has_value() ? value_ : nullopt;
|
||||
}
|
||||
|
||||
NODISCARD optional<E> error() const & {
|
||||
return is_error() ? error_ : nullopt;
|
||||
}
|
||||
|
||||
NODISCARD optional<T> value() && {
|
||||
return has_value() ? std::move(value_) : nullopt;
|
||||
}
|
||||
|
||||
NODISCARD optional<E> error() && {
|
||||
return is_error() ? std::move(error_) : nullopt;
|
||||
}
|
||||
|
||||
NODISCARD T& unwrap_value() & {
|
||||
ASSERT(has_value(), "Result does not contain a value");
|
||||
return value_;
|
||||
}
|
||||
|
||||
NODISCARD const T& unwrap_value() const & {
|
||||
ASSERT(has_value(), "Result does not contain a value");
|
||||
return value_;
|
||||
}
|
||||
|
||||
NODISCARD T unwrap_value() && {
|
||||
ASSERT(has_value(), "Result does not contain a value");
|
||||
return std::move(value_);
|
||||
}
|
||||
|
||||
NODISCARD E& unwrap_error() & {
|
||||
ASSERT(is_error(), "Result does not contain an error");
|
||||
return error_;
|
||||
}
|
||||
|
||||
NODISCARD const E& unwrap_error() const & {
|
||||
ASSERT(is_error(), "Result does not contain an error");
|
||||
return error_;
|
||||
}
|
||||
|
||||
NODISCARD E unwrap_error() && {
|
||||
ASSERT(is_error(), "Result does not contain an error");
|
||||
return std::move(error_);
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
NODISCARD T value_or(U&& default_value) const & {
|
||||
return has_value() ? value_ : static_cast<T>(std::forward<U>(default_value));
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
NODISCARD T value_or(U&& default_value) && {
|
||||
return has_value() ? std::move(value_) : static_cast<T>(std::forward<U>(default_value));
|
||||
}
|
||||
|
||||
void drop_error() const {
|
||||
if(is_error()) {
|
||||
std::fprintf(stderr, "%s\n", unwrap_error().what());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct monostate {};
|
||||
|
||||
// TODO: Re-evaluate use of off_t
|
||||
template<typename T, typename std::enable_if<std::is_trivial<T>::value, int>::type = 0>
|
||||
Result<T, internal_error> load_bytes(std::FILE* object_file, off_t offset) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user