-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
50a9a2c
commit 3cb7cb2
Showing
7 changed files
with
1,029 additions
and
47 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
_build/ | ||
.vscode/ | ||
.vscode/ | ||
.cache/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
#pragma once | ||
|
||
#include "neo/type_traits.hpp" | ||
#include <neo/concepts.hpp> | ||
|
||
namespace neo { | ||
|
||
template <typename T> | ||
struct tombstone_traits; | ||
|
||
template <typename T> | ||
class optional; | ||
|
||
/** | ||
* @brief Detect whether T is a specialization of neo::optional | ||
*/ | ||
template <typename T> | ||
constexpr bool is_optional_v = false; | ||
|
||
template <typename T> | ||
constexpr bool is_optional_v<optional<T>> = true; | ||
|
||
} // namespace neo | ||
|
||
namespace neo::opt_detail { | ||
|
||
template <typename Traits, typename T> | ||
concept valid_tombstone_traits = requires { | ||
typename Traits::state_type; | ||
requires object_type<typename Traits::state_type>; | ||
} and requires(typename Traits::state_type& mstate, typename Traits::state_type const& cstate) { | ||
{ Traits::has_value(cstate) } noexcept -> simple_boolean; | ||
{ Traits::get(cstate) } noexcept -> convertible_to<add_const_reference_t<T>>; | ||
{ Traits::get(mstate) } noexcept -> convertible_to<add_lvalue_reference_t<T>>; | ||
{ Traits::destroy(mstate) } noexcept; | ||
{ Traits::trivial_copy } -> simple_boolean; | ||
{ Traits::trivial_copy_assign } -> simple_boolean; | ||
{ Traits::trivial_move } -> simple_boolean; | ||
{ Traits::trivial_move_assign } -> simple_boolean; | ||
}; | ||
|
||
template <class_type T> | ||
struct inherit_from : T {}; | ||
|
||
// Hack: A concept that matches a type T that is a in_place_t tag | ||
template <typename T> | ||
concept in_place_t_tag = requires { | ||
// Check for an injected-name matching in_place_t | ||
typename inherit_from<T>::in_place_t; | ||
} | ||
// And that the nested name is the same as the enclosing class | ||
&&weak_same_as<T, typename inherit_from<T>::in_place_t>; | ||
|
||
/// Match a type named "nullopt_t" | ||
template <typename T> | ||
concept nullopt_t_tag = requires { | ||
typename inherit_from<T>::nullopt_t; | ||
requires weak_same_as<typename inherit_from<T>::nullopt_t, T>; | ||
}; | ||
|
||
[[noreturn]] void throw_bad_optional(); | ||
[[noreturn]] void terminate_bad_optional(); | ||
|
||
/** | ||
* This class defines the comparison operators for neo::optional. They are injected | ||
* via ADL from inheritance, to prevent left/right ambiguity during overload selection. | ||
*/ | ||
class adl_operators { | ||
template <typename T, equality_comparable_with<T> U> | ||
friend constexpr bool operator==(const optional<T>& self, const optional<U>& other) noexcept { | ||
if (self.has_value() != other.has_value()) { | ||
return false; | ||
} | ||
return not self.has_value() or *self == *other; | ||
} | ||
|
||
template <typename T> | ||
friend constexpr bool operator==(const optional<T>& self, | ||
opt_detail::nullopt_t_tag auto) noexcept { | ||
return not self.has_value(); | ||
} | ||
|
||
template <typename T, typename U> | ||
requires(not is_optional_v<U>) and equality_comparable_with<T, U> | ||
friend constexpr bool operator==(const optional<T>& self, const U& other) noexcept { | ||
return self.has_value() and *self == other; | ||
} | ||
|
||
/** | ||
* @brief Compare an optional against another optional of a value_type | ||
* that is comparable to the value_type of this optional. | ||
* | ||
* A null optional is always less than an engaged one, two null optionals | ||
* are equivalent, and two engaged optionals are compared by the value | ||
* that they contain. | ||
*/ | ||
template <typename T, three_way_comparable_with<T> U> | ||
friend constexpr std::compare_three_way_result_t<T, U> | ||
operator<=>(const optional<T>& self, const optional<U> other) noexcept { | ||
if (self.has_value() and other.has_value()) { | ||
return std::compare_three_way{}(*self, *other); | ||
} else { | ||
return self.has_value() <=> other.has_value(); | ||
} | ||
} | ||
|
||
/** | ||
* @brief Compare the optional with a null-constant. | ||
* | ||
* The null constant is always less-than any engaged optional, and equivalent | ||
* to a null optional. | ||
*/ | ||
template <typename T> | ||
friend constexpr std::strong_ordering // | ||
operator<=>(const optional<T>& self, opt_detail::nullopt_t_tag auto) noexcept { | ||
if (self.has_value()) { | ||
return std::strong_ordering::greater; | ||
} else { | ||
return std::strong_ordering::equal; | ||
} | ||
} | ||
|
||
/** | ||
* @brief Compare the optional with a value of another type which is comparable with the | ||
* optional's value_type | ||
* | ||
* A null optional is always less-than any other value | ||
*/ | ||
template <typename T, typename U> | ||
requires(not is_optional_v<U>) and three_way_comparable_with<T, U> | ||
friend constexpr std::compare_three_way_result_t<T, U> // | ||
operator<=>(const optional<T>& self, const U& value) noexcept { | ||
if (self.has_value()) { | ||
return std::compare_three_way{}(*self, value); | ||
} else { | ||
return std::strong_ordering::less; | ||
} | ||
} | ||
}; | ||
|
||
} // namespace neo::opt_detail |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
#pragma once | ||
|
||
#include "./fwd.hpp" | ||
|
||
#include <memory> | ||
#include <new> | ||
|
||
namespace neo { | ||
|
||
template <typename T, typename... Args> | ||
requires requires(void* p, Args&&... args) { ::new (p) T(NEO_FWD(args)...); } | ||
constexpr auto construct_at(T* addr, | ||
Args&&... args) // | ||
noexcept(noexcept(::new((void*)0) T(NEO_FWD(args)...))) { | ||
return std::construct_at(addr, NEO_FWD(args)...); | ||
} | ||
|
||
template <typename T> | ||
constexpr void destroy_at(T* addr) { | ||
addr->~T(); | ||
} | ||
|
||
#if defined __GNUC__ || defined _MSC_VER | ||
// GCC, Clang, and MSVC all support constexpr placement-new, so we'll just expand a macro for it | ||
#define NEO_CONSTRUCT_AT(Address, ...) (::std::construct_at(Address __VA_OPT__(, ) __VA_ARGS__)) | ||
#else | ||
#define NEO_CONSTRUCT_AT ::neo::construct_at | ||
#endif | ||
// No compiler lets this "just work" | ||
#define NEO_DESTROY_AT (::neo::destroy_at) | ||
|
||
} // namespace neo |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
#include "./optional.hpp" | ||
|
||
#include "./assert.hpp" | ||
|
||
#include <optional> | ||
|
||
using namespace neo; | ||
|
||
void opt_detail::throw_bad_optional() { throw std::bad_optional_access(); } | ||
void opt_detail::terminate_bad_optional() { | ||
neo_assert(expects, false, "Attempted to dereference a disengaged optional object"); | ||
} |
Oops, something went wrong.