185 lines
4.1 KiB
C++
185 lines
4.1 KiB
C++
#pragma once
|
|
|
|
#include "memory/pointer.h"
|
|
#include "util/number.h"
|
|
#include "util/utility.h"
|
|
|
|
// Ported from legacy/CrackOS3 (include/features/optional.h), adapted to the
|
|
// PrOSess freestanding environment: uses the in-tree move/forward (util/utility.h)
|
|
// instead of <utility>. The CrackOS3 value_or_panic() helper is omitted because
|
|
// there is no global panic() in this tree yet.
|
|
|
|
struct nullopt_t {};
|
|
inline constexpr nullopt_t nullopt{};
|
|
|
|
template<typename T>
|
|
struct optional {
|
|
private:
|
|
char buffer[sizeof(T)];
|
|
bool initialized;
|
|
|
|
void destroy() {
|
|
if (initialized) {
|
|
((T*)buffer)->~T();
|
|
initialized = false;
|
|
}
|
|
}
|
|
|
|
public:
|
|
using value_type = T;
|
|
|
|
constexpr optional() : initialized(false) {}
|
|
constexpr optional(nullopt_t) : initialized(false) {}
|
|
optional(const T& value) : initialized(true) {
|
|
new (buffer) T(value);
|
|
}
|
|
optional(T&& value) : initialized(true) {
|
|
new (buffer) T(move(value));
|
|
}
|
|
|
|
optional(const optional& other) : initialized(other.initialized) {
|
|
if (initialized) {
|
|
new (buffer) T(*other);
|
|
}
|
|
}
|
|
|
|
optional(optional&& other) : initialized(other.initialized) {
|
|
if (initialized) {
|
|
new (buffer) T(move(*other));
|
|
}
|
|
other.initialized = false;
|
|
}
|
|
|
|
~optional() {
|
|
destroy();
|
|
}
|
|
|
|
T* operator->() {
|
|
return (T*)buffer;
|
|
}
|
|
|
|
T& operator*() {
|
|
return *((T*)buffer);
|
|
}
|
|
|
|
const T* operator->() const {
|
|
return (const T*)buffer;
|
|
}
|
|
|
|
const T& operator*() const {
|
|
return *((const T*)buffer);
|
|
}
|
|
|
|
optional& operator=(const optional& other) {
|
|
if (this == &other)
|
|
return *this;
|
|
destroy();
|
|
if (other.initialized) {
|
|
new (buffer) T(*other);
|
|
initialized = true;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
optional& operator=(optional&& other) noexcept {
|
|
if (this == &other)
|
|
return *this;
|
|
destroy();
|
|
if (other.initialized) {
|
|
new (buffer) T(move(*other));
|
|
initialized = true;
|
|
}
|
|
other.initialized = false;
|
|
return *this;
|
|
}
|
|
|
|
[[nodiscard]] bool has_value() const {
|
|
return initialized;
|
|
}
|
|
|
|
T& value() {
|
|
return *((T*)buffer);
|
|
}
|
|
|
|
const T& value() const {
|
|
return *((const T*)buffer);
|
|
}
|
|
|
|
template<typename Func, typename... ArgT>
|
|
T value_or_create(Func func, ArgT... args) {
|
|
if (initialized) {
|
|
return value();
|
|
} else {
|
|
return func(args...);
|
|
}
|
|
}
|
|
|
|
template<typename Func, typename... ArgT>
|
|
T value_or_create(Func func, ArgT... args) const {
|
|
if (initialized) {
|
|
return value();
|
|
} else {
|
|
return func(args...);
|
|
}
|
|
}
|
|
|
|
const T& value_or(const T& default_value) const {
|
|
if (initialized) {
|
|
return *((const T*)buffer);
|
|
} else {
|
|
return default_value;
|
|
}
|
|
}
|
|
|
|
operator bool() const {
|
|
return initialized;
|
|
}
|
|
|
|
bool operator!() const {
|
|
return !initialized;
|
|
}
|
|
|
|
template<typename Func>
|
|
auto map(Func&& func) -> optional<decltype(func(value()))> {
|
|
if (initialized) {
|
|
return func(value());
|
|
} else {
|
|
return {};
|
|
}
|
|
}
|
|
|
|
template<typename Func>
|
|
auto map(Func&& func) const -> optional<decltype(func(value()))> {
|
|
if (initialized) {
|
|
return func(value());
|
|
} else {
|
|
return {};
|
|
}
|
|
}
|
|
|
|
template<typename Func>
|
|
optional or_else(Func&& func) const {
|
|
if (initialized) {
|
|
return *this;
|
|
}
|
|
return func();
|
|
}
|
|
|
|
template<typename Func>
|
|
auto bind(Func&& func) -> decltype(func(value())) {
|
|
if (initialized) {
|
|
return func(value());
|
|
} else {
|
|
return {};
|
|
}
|
|
}
|
|
|
|
template<typename Func>
|
|
auto bind(Func&& func) const -> decltype(func(value())) {
|
|
if (initialized) {
|
|
return func(value());
|
|
} else {
|
|
return {};
|
|
}
|
|
}
|
|
};
|