currently broken and untested but almost have c++23 std::expected in BLT (c++17)

v1
Brett 2024-02-03 13:11:59 -05:00
parent d638edf012
commit f3b92c3c2c
1 changed files with 205 additions and 55 deletions

View File

@ -21,7 +21,8 @@
#include <string> #include <string>
#include <blt/compatibility.h> #include <blt/compatibility.h>
#include <optional> #include <variant>
#include <utility>
#if defined(__GNUC__) #if defined(__GNUC__)
@ -211,118 +212,255 @@ namespace blt
} }
}; };
template<typename T, typename E> struct unexpect_t
class expected {
explicit unexpect_t() = default;
};
inline constexpr unexpect_t unexpect{};
template<typename T>
using remove_cvref_t = std::remove_reference_t<std::remove_cv_t<T>>;
template<class E>
class unexpected
{ {
private: private:
std::optional<T> t; E e;
std::optional<E> e;
public: public:
constexpr expected() noexcept: t(T()) constexpr unexpected(const unexpected&) = default;
constexpr unexpected(unexpected&&) = default;
template<class Err = E, std::enable_if_t<
!std::is_same_v<remove_cvref_t<Err>, unexpected> && !std::is_same_v<remove_cvref_t<Err>, std::in_place_t> &&
std::is_constructible_v<E, Err>, bool> = true>
constexpr explicit unexpected(Err&& e): e(std::forward(e))
{} {}
template<typename U, std::enable_if_t<std::is_convertible_v<U, T>, bool> = true> template<class... Args, std::enable_if_t<std::is_constructible_v<E, Args...>, bool> = true>
constexpr explicit expected(U&& t): t(std::forward<U>(t)) constexpr explicit unexpected(std::in_place_t, Args&& ... args): e(std::forward<Args>(args)...)
{} {}
template<typename U, std::enable_if_t<std::is_convertible_v<U, E>, bool> = true> template<class U, class... Args, std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&, Args...>, bool> = true>
constexpr explicit expected(U&& e): e(std::forward<U>(e)) constexpr explicit unexpected(std::in_place_t, std::initializer_list<U> il, Args&& ... args): e(il, std::forward<Args>(args)...)
{} {}
// template<typename U, std::enable_if_t<std::is_convertible_v<U, T>, bool> = true> constexpr const E& error() const& noexcept
// constexpr expected(std::initializer_list<U> t): t(std::move(*t.begin())) {
// {} return e;
// }
// template<typename U, std::enable_if_t<std::is_convertible_v<U, E>, bool> = true>
// constexpr expected(std::initializer_list<U> e): e(std::move(*e.begin()))
// {}
template<class U, class G, std::enable_if_t<std::is_convertible_v<U, T> && std::is_convertible_v<G, E>, bool> = true> constexpr E& error()& noexcept
{
return e;
}
constexpr const E&& error() const&& noexcept
{
return e;
}
constexpr E&& error()&& noexcept
{
return e;
}
constexpr void swap(unexpected& other) noexcept(std::is_nothrow_swappable_v<E>)
{
std::swap(error(), other.error());
}
template<typename E2>
inline friend constexpr bool operator==(const unexpected& x, const unexpected <E2>& y)
{
return x.error() == y.error();
}
friend constexpr void swap(unexpected& x, unexpected& y) noexcept(noexcept(x.swap(y)))
{}
};
template<class E>
unexpected(E) -> unexpected<E>;
template<class E>
class bad_expected_access : public std::exception
{
private:
E e;
public:
explicit bad_expected_access(E e): e(std::move(e))
{}
const E& error() const& noexcept
{ return e; }
E& error()& noexcept
{ return e; }
const E&& error() const&& noexcept
{ return e; }
E&& error()&& noexcept
{ return e; }
[[nodiscard]] const char* what() const noexcept override
{ return "blt::expected does not contain a value!"; }
};
template<typename T, typename E, bool = std::is_copy_constructible_v<T>>
class expected
{
protected:
std::variant<T, E> v;
public:
template<typename std::enable_if_t<std::is_default_constructible_v<T>, bool> = true>
constexpr expected() noexcept: v(T())
{}
constexpr expected(const expected& copy) = delete;
constexpr expected(expected&& move) noexcept : v(move ? std::move(*move) : std::move(move.error()))
{}
/*
* (4)...(5)
*/
template<class U, class G>
constexpr explicit expected(const expected<U, G>& other) constexpr explicit expected(const expected<U, G>& other)
{ {}
if (other.has_value())
t = other.value();
else
e = other.error();
}
template<class U, class G, std::enable_if_t<std::is_convertible_v<U, T> && std::is_convertible_v<G, E>, bool> = true> template<class U, class G>
constexpr explicit expected(expected<U, G>&& other) constexpr explicit expected(expected<U, G>&& other)
{
if (other.has_value())
t = other.value();
else
e = other.error();
}
constexpr expected(const T& t): t(t)
{} {}
constexpr expected(T&& t): t(std::move(t))
/*
* (6)
*/
template<class U = T, std::enable_if_t<!std::is_convertible_v<U, T>, bool> = true>
constexpr explicit expected(U&& v)
{} {}
constexpr expected(const E& e): e(e) template<class U = T, std::enable_if_t<std::is_convertible_v<U, T>, bool> = true>
constexpr expected(U&& v)
{} {}
constexpr expected(E&& e): e(std::move(e)) /*
* (7)
*/
template<class G, std::enable_if_t<!std::is_convertible_v<const G&, E>, bool> = true>
constexpr explicit expected(const unexpected<G>& e)
{} {}
constexpr expected(const expected& copy) = default; template<class G, std::enable_if_t<std::is_convertible_v<const G&, E>, bool> = true>
constexpr expected(const unexpected<G>& e)
{}
constexpr expected(expected&& move) = default; /*
* (8)
*/
expected& operator=(const expected& copy) = default; template<class G, std::enable_if_t<!std::is_convertible_v<G, E>, bool> = true>
constexpr explicit expected(const unexpected<G>& e)
{}
template<class G, std::enable_if_t<std::is_convertible_v<G, E>, bool> = true>
constexpr expected(const unexpected<G>& e)
{}
/*
* (9)...(13)
*/
template<class... Args>
constexpr explicit expected(std::in_place_t, Args&& ... args)
{}
template<class U, class... Args>
constexpr explicit expected(std::in_place_t, std::initializer_list<U> il, Args&& ... args)
{}
template<class... Args>
constexpr explicit expected(std::in_place_t) noexcept
{}
template<class... Args>
constexpr explicit expected(unexpect_t, Args&& ... args)
{}
template<class U, class... Args>
constexpr explicit expected(unexpect_t, std::initializer_list<U> il, Args&& ... args)
{}
expected& operator=(const expected& copy) = delete;
expected& operator=(expected&& move) = default; expected& operator=(expected&& move) = default;
[[nodiscard]] constexpr explicit operator bool() const noexcept [[nodiscard]] constexpr explicit operator bool() const noexcept
{ {
return t.has_value(); return std::holds_alternative<T>(v);
} }
[[nodiscard]] constexpr inline bool has_value() const noexcept [[nodiscard]] constexpr inline bool has_value() const noexcept
{ {
return t.has_value(); return std::holds_alternative<T>(v);
} }
constexpr T& value()& constexpr T& value()&
{ {
return t.value(); if (*this)
return std::get<T>(v);
else
throw bad_expected_access(std::as_const(error()));
} }
constexpr const T& value() const& constexpr const T& value() const&
{ {
return t.value(); if (*this)
return std::get<T>(v);
else
throw bad_expected_access(std::as_const(error()));
} }
constexpr T&& value()&& constexpr T&& value()&&
{ {
return t.value(); if (*this)
return std::get<T>(v);
else
throw bad_expected_access(std::move(error()));
} }
constexpr const T&& value() const&& constexpr const T&& value() const&&
{ {
return t.value(); if (*this)
return std::get<T>(v);
else
throw bad_expected_access(std::move(error()));
} }
constexpr const E& error() const& noexcept constexpr const E& error() const& noexcept
{ {
return e.value(); return std::get<E>(v);
} }
constexpr E& error()& noexcept constexpr E& error()& noexcept
{ {
return e.value(); return std::get<E>(v);
} }
constexpr const E&& error() const&& noexcept constexpr const E&& error() const&& noexcept
{ {
return e.value(); return std::get<E>(v);
} }
constexpr E&& error()&& noexcept constexpr E&& error()&& noexcept
{ {
return e.value(); return std::get<E>(v);
} }
template<class U, std::enable_if_t<std::is_convertible_v<U, T> && std::is_copy_constructible_v<T>, bool> = true> template<class U, std::enable_if_t<std::is_convertible_v<U, T> && std::is_copy_constructible_v<T>, bool> = true>
@ -339,34 +477,46 @@ namespace blt
constexpr inline const T* operator->() const noexcept constexpr inline const T* operator->() const noexcept
{ {
return &t.value(); return &std::get<T>(v);
} }
constexpr inline T* operator->() noexcept constexpr inline T* operator->() noexcept
{ {
return &t.value(); return &std::get<T>(v);
} }
constexpr inline const T& operator*() const& noexcept constexpr inline const T& operator*() const& noexcept
{ {
return t.value(); return std::get<T>(v);
} }
constexpr inline T& operator*()& noexcept constexpr inline T& operator*()& noexcept
{ {
return t.value(); return std::get<T>(v);
} }
constexpr inline const T&& operator*() const&& noexcept constexpr inline const T&& operator*() const&& noexcept
{ {
return t.value(); return std::move(std::get<T>(v));
} }
constexpr inline T&& operator*()&& noexcept constexpr inline T&& operator*()&& noexcept
{ {
return std::move(t.value()); return std::move(std::get<T>(v));
} }
}; };
template<typename T, typename E>
class expected<T, E, true> : expected<T, E, false>
{
public:
using expected<T, E, false>::expected;
constexpr expected(const expected& copy): expected<T, E, false>::v(copy ? *copy : copy.error())
{}
expected& operator=(const expected& copy) = default;
};
//#define BLT_LAMBDA(type, var, code) [](const type& var) -> auto { return code; } //#define BLT_LAMBDA(type, var, code) [](const type& var) -> auto { return code; }
//#define BLT_LAMBDA(var, code) [](var) -> auto { return code; } //#define BLT_LAMBDA(var, code) [](var) -> auto { return code; }