2022-12-08 15:08:02 +00:00
|
|
|
#pragma once
|
|
|
|
#include <luna/Check.h>
|
|
|
|
#include <luna/Move.h>
|
|
|
|
#include <luna/PlacementNew.h>
|
|
|
|
|
|
|
|
template <typename T> class Option
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
Option(const T& value)
|
|
|
|
{
|
|
|
|
m_storage.store_reference(value);
|
|
|
|
m_has_value = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
Option(T&& value)
|
|
|
|
{
|
|
|
|
m_storage.store_moved_reference(move(value));
|
|
|
|
m_has_value = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
Option(const Option<T>& other)
|
|
|
|
{
|
|
|
|
m_has_value = other.has_value();
|
|
|
|
if (m_has_value) { m_storage.store_reference(other.m_storage.fetch_reference()); }
|
|
|
|
}
|
|
|
|
|
|
|
|
Option(Option<T>&& other)
|
|
|
|
{
|
|
|
|
m_has_value = other.has_value();
|
|
|
|
other.m_has_value = false;
|
|
|
|
if (m_has_value) { m_storage.store_moved_reference(move(other.m_storage.fetch_reference())); }
|
|
|
|
}
|
|
|
|
|
|
|
|
Option()
|
|
|
|
{
|
|
|
|
m_has_value = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool has_value() const
|
|
|
|
{
|
|
|
|
return m_has_value;
|
|
|
|
}
|
|
|
|
|
|
|
|
T value() const
|
|
|
|
{
|
|
|
|
expect(has_value(), "Option::value called on an empty Option");
|
|
|
|
return m_storage.fetch_reference();
|
|
|
|
}
|
|
|
|
|
|
|
|
T release_value()
|
|
|
|
{
|
|
|
|
expect(has_value(), "Option::release_value called on an empty Option");
|
|
|
|
m_has_value = false;
|
2022-12-16 17:13:40 +00:00
|
|
|
return move(m_storage.fetch_reference());
|
2022-12-08 15:08:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
T value_or(const T& other) const
|
|
|
|
{
|
|
|
|
if (has_value()) return m_storage.fetch_reference();
|
|
|
|
return other;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool try_set_value(T& ref) const
|
|
|
|
{
|
|
|
|
if (!has_value()) return false;
|
|
|
|
ref = m_storage.fetch_reference();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
~Option()
|
|
|
|
{
|
|
|
|
if (has_value()) m_storage.destroy();
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ErrorHandle
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
explicit ErrorHandle()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Option(ErrorHandle)
|
|
|
|
{
|
|
|
|
m_has_value = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// For compatibility with TRY()
|
|
|
|
ErrorHandle release_error()
|
|
|
|
{
|
|
|
|
expect(!has_value(), "Option::release_error called on a non-empty Option");
|
|
|
|
return ErrorHandle{};
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
struct Storage
|
|
|
|
{
|
|
|
|
u8 buffer[sizeof(T)];
|
|
|
|
|
|
|
|
T* fetch_ptr()
|
|
|
|
{
|
|
|
|
return (T*)buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
T& fetch_reference()
|
|
|
|
{
|
|
|
|
return *fetch_ptr();
|
|
|
|
}
|
|
|
|
|
|
|
|
const T* fetch_ptr() const
|
|
|
|
{
|
|
|
|
return (const T*)buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
const T& fetch_reference() const
|
|
|
|
{
|
|
|
|
return *fetch_ptr();
|
|
|
|
}
|
|
|
|
|
|
|
|
void store_reference(const T& ref)
|
|
|
|
{
|
|
|
|
new (buffer) T(ref);
|
|
|
|
}
|
|
|
|
|
|
|
|
void store_moved_reference(T&& ref)
|
|
|
|
{
|
2022-12-16 17:11:17 +00:00
|
|
|
new (buffer) T(move(ref));
|
2022-12-08 15:08:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void destroy()
|
|
|
|
{
|
|
|
|
fetch_reference().~T();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
Storage m_storage;
|
|
|
|
bool m_has_value{false};
|
|
|
|
};
|