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:
parent
6cee208e62
commit
b58eba63f1
139
luna/include/luna/Option.h
Normal file
139
luna/include/luna/Option.h
Normal 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};
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user