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