#pragma once
#include <type_traits>
namespace
panda {
template
<
typename
T>
struct
optional {
~optional() { reset(); }
optional() : nullable_val(
nullptr
) {}
optional(
const
T& val) : nullable_val(
new
(storage) T(val)) {}
optional(
const
optional& oth) : nullable_val(oth ?
new
(storage) T(*oth) :
nullptr
) {}
optional& operator=(optional
const
& oth) {
if
(&oth !=
this
) {
reset();
if
(oth)
nullable_val =
new
(storage) T(*oth);
}
return
*
this
;
}
optional& operator=(
const
T& val) {
reset();
nullable_val =
new
(storage) T(val);
return
*
this
;
}
void
reset() {
if
(nullable_val)
nullable_val->~T();
nullable_val =
nullptr
;
}
T& operator*() {
return
*nullable_val; }
const
T& operator*()
const
{
return
*nullable_val; }
T* operator->() {
return
nullable_val; }
const
T* operator->()
const
{
return
nullable_val; }
T value_or(
const
T& default_val)
const
{
return
nullable_val ? *nullable_val : default_val; }
T value()
const
{
return
*nullable_val; }
explicit
operator
bool
()
const
{
return
nullable_val !=
nullptr
; }
template
<
class
F >
constexpr
auto
and_then( F&& f )
const
{
if
(nullable_val) {
return
f(*nullable_val);
}
else
{
return
decltype
(f(*nullable_val)){};
}
}
template
<
class
F >
constexpr
auto
transform( F&& f )
const
{
using
Ret = optional<
typename
std::remove_cv<
decltype
(f(*nullable_val))>::type>;
if
(nullable_val) {
return
Ret(f(*nullable_val));
}
else
{
return
Ret();
}
}
template
<
class
F >
constexpr
optional or_else( F&& f )
const
{
if
(nullable_val) {
return
*nullable_val;
}
else
{
return
f();
}
}
private
:
T* nullable_val;
alignas
(
alignof
(T))
char
storage[
sizeof
(T)];
};
template
<
typename
T>
struct
optional_tools {
using
type = optional<T>;
static
type default_value () {
return
type{}; }
};
template
<>
struct
optional_tools<
void
> {
using
type =
void
;
static
void
default_value () {}
};
template
<
class
T,
class
U>
inline
constexpr
bool
operator== (
const
optional<T>& lhs,
const
optional<U>& rhs) {
return
(lhs && rhs) ? (*lhs == *rhs) : (lhs || rhs ?
false
:
true
);
}
template
<
class
T,
class
U>
inline
constexpr
bool
operator!= (
const
optional<T>& lhs,
const
optional<U>& rhs) {
return
!operator==(lhs, rhs); }
template
<
class
T,
class
U>
inline
constexpr
bool
operator< (
const
optional<T>& lhs,
const
optional<U>& rhs) {
return
(lhs && rhs) ? (*lhs < *rhs) : (rhs ?
true
:
false
);
}
template
<
class
T,
class
U>
constexpr
bool
operator<= (
const
optional<T>& lhs,
const
optional<U>& rhs) {
return
(lhs && rhs) ? (*lhs < *rhs) : (lhs ?
false
:
true
);
}
template
<
class
T,
class
U>
constexpr
bool
operator> (
const
optional<T>& lhs,
const
optional<U>& rhs) {
return
(lhs && rhs) ? (*lhs < *rhs) : (lhs ?
true
:
false
);
}
template
<
class
T,
class
U>
constexpr
bool
operator>= (
const
optional<T>& lhs,
const
optional<U>& rhs) {
return
(lhs && rhs) ? (*lhs < *rhs) : (rhs ?
false
:
true
);
}
template
<
class
T,
class
U>
constexpr
bool
operator== (
const
optional<T>& opt,
const
U& value) {
return
opt && *opt == value; }
template
<
class
T,
class
U>
constexpr
bool
operator== (
const
T& value,
const
optional<U>& opt) {
return
opt && value == *opt; }
template
<
class
T,
class
U>
constexpr
bool
operator!= (
const
optional<T>& opt,
const
U& value) {
return
!operator==(opt, value); }
template
<
class
T,
class
U>
constexpr
bool
operator!= (
const
T& value,
const
optional<U>& opt) {
return
!operator==(value, opt); }
template
<
class
T,
class
U>
constexpr
bool
operator< (
const
optional<T>& opt,
const
U& value) {
return
opt ? *opt < value :
true
; }
template
<
class
T,
class
U>
constexpr
bool
operator< (
const
T& value,
const
optional<U>& opt) {
return
opt && value < *opt; }
template
<
class
T,
class
U>
constexpr
bool
operator<= (
const
optional<T>& opt,
const
U& value) {
return
opt ? *opt <= value :
true
; }
template
<
class
T,
class
U>
constexpr
bool
operator<= (
const
T& value,
const
optional<U>& opt) {
return
opt && value <= *opt; }
template
<
class
T,
class
U>
constexpr
bool
operator> (
const
optional<T>& opt,
const
U& value) {
return
opt && *opt > value; }
template
<
class
T,
class
U>
constexpr
bool
operator> (
const
T& value,
const
optional<U>& opt) {
return
opt ? value > *opt :
true
; }
template
<
class
T,
class
U>
constexpr
bool
operator>= (
const
optional<T>& opt,
const
U& value) {
return
opt && *opt >= value; }
template
<
class
T,
class
U>
constexpr
bool
operator>= (
const
T& value,
const
optional<U>& opt) {
return
opt ? value >= *opt :
true
; }
}