Files
Cpp-in-Embedded-Systems/Chapter18/cib/libs/stdx/optional.hpp
Amar Mahmutbegovic 8634accda5 add Chapter18
2025-02-06 00:19:59 +01:00

263 lines
9.0 KiB
C++

#pragma once
#include <stdx/functional.hpp>
#include <stdx/type_traits.hpp>
#include <stdx/utility.hpp>
#include <limits>
#include <memory>
#include <optional>
#include <type_traits>
#include <utility>
// NOLINTBEGIN(modernize-use-constraints)
namespace stdx {
inline namespace v1 {
template <typename T, typename = void> struct tombstone_traits {
static_assert(
stdx::always_false_v<T>,
"To use stdx::optional you must specialize stdx::tombstone_traits");
};
template <typename T>
struct tombstone_traits<T, std::enable_if_t<std::is_floating_point_v<T>>> {
constexpr auto operator()() const {
return std::numeric_limits<T>::infinity();
}
};
template <typename T>
struct tombstone_traits<T, std::enable_if_t<std::is_pointer_v<T>>> {
constexpr auto operator()() const { return nullptr; }
};
template <auto V> struct tombstone_value {
constexpr auto operator()() const {
if constexpr (stdx::is_cx_value_v<decltype(V)>) {
return V();
} else {
return V;
}
}
};
template <typename T, typename TS = tombstone_traits<T>> class optional {
static_assert(not std::is_integral_v<T> or
not stdx::is_specialization_of_v<TS, tombstone_traits>,
"Don't define tombstone traits for plain integral types");
constexpr static inline auto traits = TS{};
T val{traits()};
public:
using value_type = T;
constexpr optional() = default;
constexpr explicit optional(std::nullopt_t) {}
template <typename... Args>
constexpr explicit optional(std::in_place_t, Args &&...args)
: val{std::forward<Args>(args)...} {}
template <
typename U = T,
typename = std::enable_if_t<
std::is_constructible_v<T, U &&> and
not std::is_same_v<stdx::remove_cvref_t<U>, std::in_place_t> and
not std::is_same_v<stdx::remove_cvref_t<U>, optional>>>
constexpr explicit optional(U &&u) : val{std::forward<U>(u)} {}
constexpr auto operator=(std::nullopt_t) -> optional & {
reset();
return *this;
}
template <
typename U = T,
typename = std::enable_if_t<
std::is_constructible_v<T, U> and std::is_assignable_v<T &, U> and
not std::is_same_v<stdx::remove_cvref_t<U>, optional> and
(std::is_scalar_v<T> or not std::is_same_v<std::decay_t<U>, T>)>>
constexpr auto operator=(U &&u) -> optional & {
val = std::forward<U>(u);
return *this;
}
[[nodiscard]] constexpr auto has_value() const noexcept -> bool {
return not(val == traits());
}
constexpr explicit operator bool() const noexcept { return has_value(); }
[[nodiscard]] constexpr auto value() & LIFETIMEBOUND -> value_type & {
return val;
}
[[nodiscard]] constexpr auto value() const
& LIFETIMEBOUND -> value_type const & {
return val;
}
[[nodiscard]] constexpr auto value() && LIFETIMEBOUND -> value_type && {
return std::move(val);
}
[[nodiscard]] constexpr auto value() const
&& LIFETIMEBOUND -> value_type const && {
return std::move(val);
}
[[nodiscard]] constexpr auto
operator->() const LIFETIMEBOUND->value_type const * {
return std::addressof(val);
}
[[nodiscard]] constexpr auto operator->() LIFETIMEBOUND->value_type * {
return std::addressof(val);
}
[[nodiscard]] constexpr auto operator*() const
& LIFETIMEBOUND->decltype(auto) {
return value();
}
[[nodiscard]] constexpr auto operator*() & LIFETIMEBOUND->decltype(auto) {
return value();
}
[[nodiscard]] constexpr auto operator*() const
&& LIFETIMEBOUND->decltype(auto) {
return std::move(*this).value();
}
[[nodiscard]] constexpr auto operator*() && LIFETIMEBOUND->decltype(auto) {
return std::move(*this).value();
}
template <typename U>
[[nodiscard]] constexpr auto
value_or(U &&default_value) const & -> value_type {
return has_value() ? val : T{std::forward<U>(default_value)};
}
template <typename U>
[[nodiscard]] constexpr auto value_or(U &&default_value) && -> value_type {
return has_value() ? std::move(val) : T{std::forward<U>(default_value)};
}
template <typename... Args>
constexpr auto emplace(Args &&...args) LIFETIMEBOUND -> value_type & {
val.~value_type();
new (std::addressof(val)) value_type(std::forward<Args>(args)...);
return value();
}
constexpr auto reset() {
val.~value_type();
new (std::addressof(val)) value_type(traits());
}
template <typename F> constexpr auto transform(F &&f) & {
using func_t = stdx::remove_cvref_t<F>;
using U = std::invoke_result_t<func_t, value_type &>;
return *this ? optional<U>{with_result_of{
[&] { return std::forward<F>(f)(val); }}}
: optional<U>{};
}
template <typename F> constexpr auto transform(F &&f) const & {
using func_t = stdx::remove_cvref_t<F>;
using U = std::invoke_result_t<func_t, value_type const &>;
return *this ? optional<U>{with_result_of{
[&] { return std::forward<F>(f)(val); }}}
: optional<U>{};
}
template <typename F> constexpr auto transform(F &&f) && {
using func_t = stdx::remove_cvref_t<F>;
using U = std::invoke_result_t<func_t, value_type &&>;
return *this ? optional<U>{with_result_of{
[&] { return std::forward<F>(f)(std::move(val)); }}}
: optional<U>{};
}
template <typename F> constexpr auto transform(F &&f) const && {
using func_t = stdx::remove_cvref_t<F>;
using U = std::invoke_result_t<func_t, value_type const &&>;
return *this ? optional<U>{with_result_of{
[&] { return std::forward<F>(f)(std::move(val)); }}}
: optional<U>{};
}
template <typename F> constexpr auto or_else(F &&f) const & -> optional {
return *this ? *this : std::forward<F>(f)();
}
template <typename F> constexpr auto or_else(F &&f) && -> optional {
return *this ? std::move(*this) : std::forward<F>(f)();
}
template <typename F> constexpr auto and_then(F &&f) & {
using func_t = stdx::remove_cvref_t<F>;
using U = std::invoke_result_t<func_t, value_type &>;
return *this ? std::forward<F>(f)(val) : U{};
}
template <typename F> constexpr auto and_then(F &&f) const & {
using func_t = stdx::remove_cvref_t<F>;
using U = std::invoke_result_t<func_t, value_type const &>;
return *this ? std::forward<F>(f)(val) : U{};
}
template <typename F> constexpr auto and_then(F &&f) && {
using func_t = stdx::remove_cvref_t<F>;
using U = std::invoke_result_t<func_t, value_type &&>;
return *this ? std::forward<F>(f)(std::move(val)) : U{};
}
template <typename F> constexpr auto and_then(F &&f) const && {
using func_t = stdx::remove_cvref_t<F>;
using U = std::invoke_result_t<func_t, value_type &&>;
return *this ? std::forward<F>(f)(std::move(val)) : U{};
}
private:
[[nodiscard]] friend constexpr auto
operator==(optional const &lhs, optional const &rhs) -> bool {
return lhs.val == rhs.val;
}
#if __cpp_impl_three_way_comparison < 201907L
[[nodiscard]] friend constexpr auto
operator!=(optional const &lhs, optional const &rhs) -> bool {
return not(lhs == rhs);
}
#endif
[[nodiscard]] friend constexpr auto operator<(optional const &lhs,
optional const &rhs) -> bool {
return lhs.has_value() and rhs.has_value()
? lhs.val < rhs.val
: not lhs.has_value() and rhs.has_value();
}
[[nodiscard]] friend constexpr auto
operator<=(optional const &lhs, optional const &rhs) -> bool {
return not(rhs < lhs);
}
[[nodiscard]] friend constexpr auto operator>(optional const &lhs,
optional const &rhs) -> bool {
return rhs < lhs;
}
[[nodiscard]] friend constexpr auto
operator>=(optional const &lhs, optional const &rhs) -> bool {
return not(lhs < rhs);
}
};
template <typename T> optional(T) -> optional<T>;
template <typename F, typename... Ts,
typename = std::enable_if_t<
(... and stdx::is_specialization_of_v<stdx::remove_cvref_t<Ts>,
optional>)>>
constexpr auto transform(F &&f, Ts &&...ts) {
using func_t = stdx::remove_cvref_t<F>;
using R = std::invoke_result_t<
func_t,
forward_like_t<Ts, typename stdx::remove_cvref_t<Ts>::value_type>...>;
if ((... and ts.has_value())) {
return optional<R>{with_result_of{[&] {
return std::forward<F>(f)(std::forward<Ts>(ts).value()...);
}}};
}
return optional<R>{};
}
} // namespace v1
} // namespace stdx
// NOLINTEND(modernize-use-constraints)