Add a new Option class which behaves like Result, but has no error number, just a value or no value

This commit is contained in:
apio 2022-12-08 16:08:02 +01:00
parent 6cee208e62
commit b58eba63f1
Signed by: apio
GPG Key ID: B8A7D06E42258954

139
luna/include/luna/Option.h Normal file
View File

@ -0,0 +1,139 @@
#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");
T item = m_storage.fetch_reference();
m_has_value = false;
m_storage.destroy();
return move(item);
}
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)
{
new (buffer) T(ref);
}
void destroy()
{
fetch_reference().~T();
}
};
Storage m_storage;
bool m_has_value{false};
};