diff --git a/src/utils/error.hpp b/src/utils/error.hpp index 02480b1..6ffec1c 100644 --- a/src/utils/error.hpp +++ b/src/utils/error.hpp @@ -1,6 +1,7 @@ #ifndef ERROR_HPP #define ERROR_HPP +#include #include #include #include @@ -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; } } diff --git a/src/utils/optional.hpp b/src/utils/optional.hpp new file mode 100644 index 0000000..740a321 --- /dev/null +++ b/src/utils/optional.hpp @@ -0,0 +1,154 @@ +#ifndef OPTIONAL_HPP +#define OPTIONAL_HPP + +#include +#include +#include + +#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::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(std::addressof(uvalue))) T(other.uvalue); + } + } + + optional(optional&& other) + noexcept(std::is_nothrow_move_constructible::value) + : holds_value(other.holds_value) + { + if(holds_value) { + new (static_cast(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::value && std::is_nothrow_move_constructible::value) + { + reset(); + if(other.holds_value) { + new (static_cast(std::addressof(uvalue))) T(std::move(other.uvalue)); + holds_value = true; + } + return *this; + } + + template< + typename U = T, + typename std::enable_if::type, optional>::value, int>::type = 0 + > + optional(U&& value) : holds_value(true) { + new (static_cast(std::addressof(uvalue))) T(std::forward(value)); + } + + template< + typename U = T, + typename std::enable_if::type, optional>::value, int>::type = 0 + > + optional& operator=(U&& value) { + optional o(std::forward(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(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 + NODISCARD T value_or(U&& default_value) const & { + return holds_value ? uvalue : static_cast(std::forward(default_value)); + } + + template + NODISCARD T value_or(U&& default_value) && { + return holds_value ? std::move(uvalue) : static_cast(std::forward(default_value)); + } + }; +} +} + +#endif diff --git a/src/utils/result.hpp b/src/utils/result.hpp new file mode 100644 index 0000000..6293549 --- /dev/null +++ b/src/utils/result.hpp @@ -0,0 +1,129 @@ +#ifndef RESULT_HPP +#define RESULT_HPP + +#include +#include +#include + +#include "utils/common.hpp" +#include "utils/error.hpp" +#include "utils/optional.hpp" + +namespace cpptrace { +namespace detail { + template::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 value() const & { + return has_value() ? value_ : nullopt; + } + + NODISCARD optional error() const & { + return is_error() ? error_ : nullopt; + } + + NODISCARD optional value() && { + return has_value() ? std::move(value_) : nullopt; + } + + NODISCARD optional 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 + NODISCARD T value_or(U&& default_value) const & { + return has_value() ? value_ : static_cast(std::forward(default_value)); + } + + template + NODISCARD T value_or(U&& default_value) && { + return has_value() ? std::move(value_) : static_cast(std::forward(default_value)); + } + + void drop_error() const { + if(is_error()) { + std::fprintf(stderr, "%s\n", unwrap_error().what()); + } + } + }; + + struct monostate {}; +} +} + +#endif diff --git a/src/utils/utils.hpp b/src/utils/utils.hpp index e4bf228..a9cee0a 100644 --- a/src/utils/utils.hpp +++ b/src/utils/utils.hpp @@ -2,7 +2,6 @@ #define UTILS_HPP #include -#include #include #include #include @@ -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::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(std::addressof(uvalue))) T(other.uvalue); - } - } - - optional(optional&& other) - noexcept(std::is_nothrow_move_constructible::value) - : holds_value(other.holds_value) - { - if(holds_value) { - new (static_cast(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::value && std::is_nothrow_move_constructible::value) - { - reset(); - if(other.holds_value) { - new (static_cast(std::addressof(uvalue))) T(std::move(other.uvalue)); - holds_value = true; - } - return *this; - } - - template< - typename U = T, - typename std::enable_if::type, optional>::value, int>::type = 0 - > - optional(U&& value) : holds_value(true) { - new (static_cast(std::addressof(uvalue))) T(std::forward(value)); - } - - template< - typename U = T, - typename std::enable_if::type, optional>::value, int>::type = 0 - > - optional& operator=(U&& value) { - optional o(std::forward(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(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 - NODISCARD T value_or(U&& default_value) const & { - return holds_value ? uvalue : static_cast(std::forward(default_value)); - } - - template - NODISCARD T value_or(U&& default_value) && { - return holds_value ? std::move(uvalue) : static_cast(std::forward(default_value)); - } - }; - - extern std::atomic_bool absorb_trace_exceptions; - - template::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 value() const & { - return has_value() ? value_ : nullopt; - } - - NODISCARD optional error() const & { - return is_error() ? error_ : nullopt; - } - - NODISCARD optional value() && { - return has_value() ? std::move(value_) : nullopt; - } - - NODISCARD optional 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 - NODISCARD T value_or(U&& default_value) const & { - return has_value() ? value_ : static_cast(std::forward(default_value)); - } - - template - NODISCARD T value_or(U&& default_value) && { - return has_value() ? std::move(value_) : static_cast(std::forward(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::value, int>::type = 0> Result load_bytes(std::FILE* object_file, off_t offset) {