#ifndef Optional_h
#define Optional_h
#include <type_traits>
#include <wtf/Assertions.h>
#include <wtf/StdLibExtras.h>
namespace WTF {
enum InPlaceTag { InPlace };
enum NulloptTag { Nullopt };
template<typename T>
class Optional {
public:
Optional()
: m_isEngaged(false)
{
}
Optional(NulloptTag)
: m_isEngaged(false)
{
}
Optional(const T& value)
: m_isEngaged(true)
{
new (NotNull, &m_value) T(value);
}
Optional(const Optional& other)
: m_isEngaged(other.m_isEngaged)
{
if (m_isEngaged)
new (NotNull, &m_value) T(*other.asPtr());
}
Optional(Optional&& other)
: m_isEngaged(other.m_isEngaged)
{
if (m_isEngaged)
new (NotNull, &m_value) T(WTF::move(*other.asPtr()));
}
Optional(T&& value)
: m_isEngaged(true)
{
new (NotNull, &m_value) T(WTF::move(value));
}
template<typename... Args>
Optional(InPlaceTag, Args&&... args)
: m_isEngaged(true)
{
new (NotNull, &m_value) T(std::forward<Args>(args)...);
}
~Optional()
{
destroy();
}
Optional& operator=(NulloptTag)
{
destroy();
return *this;
}
Optional& operator=(const Optional& other)
{
if (this == &other)
return *this;
destroy();
if (other.m_isEngaged) {
new (NotNull, &m_value) T(*other.asPtr());
m_isEngaged = true;
}
return *this;
}
Optional& operator=(Optional&& other)
{
if (this == &other)
return *this;
destroy();
if (other.m_isEngaged) {
new (NotNull, &m_value) T(WTF::move(*other.asPtr()));
m_isEngaged = true;
}
return *this;
}
template<typename U, class = typename std::enable_if<std::is_same<typename std::remove_reference<U>::type, T>::value>::type>
Optional& operator=(U&& u)
{
destroy();
new (NotNull, &m_value) T(std::forward<U>(u));
m_isEngaged = true;
return *this;
}
explicit operator bool() const { return m_isEngaged; }
T& value()
{
ASSERT(m_isEngaged);
return *asPtr();
}
const T& value() const
{
ASSERT(m_isEngaged);
return *asPtr();
}
template<typename U>
T valueOr(U&& value) const
{
if (m_isEngaged)
return *asPtr();
return std::forward<U>(value);
}
template<typename U>
T valueOrCompute(U callback) const
{
if (m_isEngaged)
return *asPtr();
return callback();
}
private:
const T* asPtr() const { return reinterpret_cast<const T*>(&m_value); }
T* asPtr() { return reinterpret_cast<T*>(&m_value); }
void destroy()
{
if (m_isEngaged) {
asPtr()->~T();
m_isEngaged = false;
}
}
bool m_isEngaged;
typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type m_value;
};
}
using WTF::InPlace;
using WTF::Nullopt;
using WTF::Optional;
#endif // Optional_h