247 lines
4.6 KiB
C++
247 lines
4.6 KiB
C++
#pragma once
|
|
#include <Check.h>
|
|
#include <Move.h>
|
|
#include <PlacementNew.h>
|
|
#include <Types.h>
|
|
|
|
struct Error
|
|
{
|
|
Error(int err)
|
|
{
|
|
error = err;
|
|
}
|
|
|
|
int error;
|
|
};
|
|
|
|
template <typename T> class Result
|
|
{
|
|
public:
|
|
Result(const T& value)
|
|
{
|
|
m_storage.store_reference(value);
|
|
m_has_value = true;
|
|
m_has_error = false;
|
|
}
|
|
|
|
Result(T&& value)
|
|
{
|
|
m_storage.store_movable_reference(move(value));
|
|
m_has_value = true;
|
|
m_has_error = false;
|
|
}
|
|
|
|
Result(const Result<T>& other)
|
|
{
|
|
if (!other.m_has_error)
|
|
{
|
|
m_storage.store_reference(other.m_storage.fetch_reference());
|
|
m_has_value = true;
|
|
m_has_error = false;
|
|
}
|
|
else
|
|
{
|
|
m_has_error = true;
|
|
m_has_value = false;
|
|
m_error = other.m_error;
|
|
}
|
|
}
|
|
|
|
Result(Result<T>&& other)
|
|
{
|
|
if (!other.m_has_error)
|
|
{
|
|
m_storage.store_movable_reference(move(other.m_storage.fetch_reference()));
|
|
m_has_value = true;
|
|
m_has_error = false;
|
|
}
|
|
else
|
|
{
|
|
m_has_error = true;
|
|
m_has_value = false;
|
|
m_error = other.m_error;
|
|
}
|
|
}
|
|
|
|
Result(const Error& err)
|
|
{
|
|
m_error = err.error;
|
|
m_has_error = true;
|
|
m_has_value = false;
|
|
}
|
|
|
|
bool has_error()
|
|
{
|
|
return m_has_error;
|
|
}
|
|
|
|
bool has_value()
|
|
{
|
|
return m_has_value;
|
|
}
|
|
|
|
int error()
|
|
{
|
|
check(has_error());
|
|
return m_error;
|
|
}
|
|
|
|
Error release_error()
|
|
{
|
|
check(has_error());
|
|
return {m_error};
|
|
}
|
|
|
|
T value()
|
|
{
|
|
check(has_value());
|
|
return m_storage.fetch_reference();
|
|
}
|
|
|
|
T value_or(T other)
|
|
{
|
|
if (has_value()) return m_storage.fetch_reference();
|
|
return other;
|
|
}
|
|
|
|
T release_value()
|
|
{
|
|
check(has_value());
|
|
T item = m_storage.fetch_reference();
|
|
m_has_value = false;
|
|
m_storage.destroy();
|
|
return move(item);
|
|
}
|
|
|
|
~Result()
|
|
{
|
|
if (has_value()) m_storage.destroy();
|
|
}
|
|
|
|
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_ptr(T* ptr)
|
|
{
|
|
new (buffer) T(*ptr);
|
|
}
|
|
|
|
void store_reference(const T& ref)
|
|
{
|
|
new (buffer) T(ref);
|
|
}
|
|
|
|
void store_movable_reference(T&& ref)
|
|
{
|
|
new (buffer) T(ref);
|
|
}
|
|
|
|
void destroy()
|
|
{
|
|
fetch_reference().~T();
|
|
}
|
|
};
|
|
Storage m_storage;
|
|
int m_error;
|
|
bool m_has_error;
|
|
bool m_has_value;
|
|
};
|
|
|
|
template <> class Result<void>
|
|
{
|
|
public:
|
|
Result()
|
|
{
|
|
m_has_error = false;
|
|
}
|
|
|
|
Result(const Result<void>& other)
|
|
{
|
|
m_has_error = other.m_has_error;
|
|
m_error = other.m_error;
|
|
}
|
|
|
|
Result(Result<void>&& other)
|
|
{
|
|
m_has_error = other.m_has_error;
|
|
m_error = other.m_error;
|
|
}
|
|
|
|
Result(const Error& err)
|
|
{
|
|
m_error = err.error;
|
|
m_has_error = true;
|
|
}
|
|
|
|
bool has_error()
|
|
{
|
|
return m_has_error;
|
|
}
|
|
|
|
bool has_value()
|
|
{
|
|
return !m_has_error;
|
|
}
|
|
|
|
int error()
|
|
{
|
|
check(has_error());
|
|
return m_error;
|
|
}
|
|
|
|
Error release_error()
|
|
{
|
|
check(has_error());
|
|
return {m_error};
|
|
}
|
|
|
|
void value()
|
|
{
|
|
check(has_value());
|
|
return;
|
|
}
|
|
|
|
void release_value()
|
|
{
|
|
check(has_value());
|
|
return;
|
|
}
|
|
|
|
private:
|
|
int m_error;
|
|
bool m_has_error;
|
|
};
|
|
|
|
// clang-format off
|
|
#define err Error{0}
|
|
// clang-format on
|
|
|
|
#define TRY(expr) \
|
|
({ \
|
|
auto _expr_rc = (expr); \
|
|
if (_expr_rc.has_error()) return _expr_rc.release_error(); \
|
|
_expr_rc.release_value(); \
|
|
})
|