168 lines
4.2 KiB
C++
168 lines
4.2 KiB
C++
/*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* Copyright 2019 Danny Robson <danny@nerdcruft.net>
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <exception>
|
|
#include <functional>
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
namespace cruft {
|
|
class bad_expected_access : public std::exception {
|
|
public:
|
|
char const* what (void) const noexcept override
|
|
{
|
|
return "bad_expected_access";
|
|
}
|
|
};
|
|
|
|
|
|
template <typename ErrorT>
|
|
class unexpected {
|
|
public:
|
|
unexpected (ErrorT && _value): m_value (std::move (_value)) { ; }
|
|
unexpected (ErrorT const &_value): m_value (_value) { ; }
|
|
|
|
ErrorT& value (void)& { return m_value; }
|
|
ErrorT&& value (void)&& { return std::move (m_value); }
|
|
|
|
ErrorT const& value (void) const & { return m_value; }
|
|
ErrorT const&& value (void) const&& { return std::move (m_value); }
|
|
|
|
private:
|
|
ErrorT m_value;
|
|
};
|
|
|
|
|
|
template <typename ValueT, typename ErrorT>
|
|
struct [[nodiscard]] expected {
|
|
using value_type = ValueT;
|
|
using error_type = ErrorT;
|
|
|
|
expected () = delete;
|
|
expected (expected &&rhs)
|
|
{
|
|
destroy ();
|
|
if (rhs) {
|
|
m_valid = true;
|
|
::new (&m_store.value) ValueT (std::move (rhs.value ()));
|
|
} else {
|
|
m_valid = false;
|
|
::new (&m_store.error) unexpected<ErrorT> (std::move (rhs.error ()));
|
|
}
|
|
}
|
|
|
|
expected& operator=(expected &&);
|
|
expected (expected const&);
|
|
expected& operator=(expected const&);
|
|
|
|
expected (ValueT const &_value)
|
|
{
|
|
::new (&m_store.value) ValueT (_value);
|
|
m_valid = true;
|
|
}
|
|
|
|
expected (ValueT &&_value)
|
|
{
|
|
::new (&m_store.value) ValueT (std::move (_value));
|
|
m_valid = true;
|
|
}
|
|
|
|
expected (unexpected<ErrorT> &&_error)
|
|
{
|
|
::new (&m_store.error) unexpected<ErrorT> (std::move (_error));
|
|
m_valid = false;
|
|
}
|
|
|
|
~expected ()
|
|
{
|
|
destroy ();
|
|
}
|
|
|
|
ValueT&
|
|
value (void)&
|
|
{
|
|
if (!m_valid)
|
|
throw bad_expected_access {};
|
|
return m_store.value;
|
|
}
|
|
|
|
ValueT const&
|
|
value (void) const&
|
|
{
|
|
if (!m_valid)
|
|
throw bad_expected_access {};
|
|
return m_store.value;
|
|
}
|
|
|
|
ValueT&&
|
|
value (void)&&
|
|
{
|
|
if (!m_valid)
|
|
throw bad_expected_access {};
|
|
return std::move (m_store.value);
|
|
}
|
|
|
|
ErrorT&
|
|
error (void)&
|
|
{
|
|
if (m_valid)
|
|
throw bad_expected_access {};
|
|
return m_store.error.value ();
|
|
}
|
|
|
|
ErrorT const&
|
|
error (void) const&
|
|
{
|
|
if (m_valid) throw bad_expected_access {};
|
|
return m_store.error.value ();
|
|
}
|
|
|
|
ErrorT&&
|
|
error (void)&&
|
|
{
|
|
if (m_valid)
|
|
throw bad_expected_access {};
|
|
return std::move (m_store.error.value ());
|
|
}
|
|
|
|
ValueT* operator-> (void)& { return &value (); }
|
|
ValueT& operator* (void)& { return value (); }
|
|
|
|
ValueT const* operator-> (void) const& { return &value (); }
|
|
ValueT const& operator* (void) const& { return value (); }
|
|
|
|
bool has_value (void) const noexcept { return m_valid; }
|
|
|
|
explicit operator bool() const noexcept { return has_value (); }
|
|
|
|
private:
|
|
void destroy (void)
|
|
{
|
|
if (m_valid) {
|
|
m_store.value.~ValueT ();
|
|
m_valid = false;
|
|
} else {
|
|
m_store.error.~unexpected<ErrorT> ();
|
|
}
|
|
}
|
|
|
|
bool m_valid = false;
|
|
|
|
union storage_t {
|
|
storage_t () {};
|
|
~storage_t () {};
|
|
|
|
char defer;
|
|
ValueT value;
|
|
unexpected<ErrorT> error;
|
|
} m_store;
|
|
};
|
|
}
|