Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions include/stdx/optional.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ template <typename T, typename = void> struct tombstone_traits {
}
};

template <typename T, typename = void> constexpr auto has_tombstone_v = true;
template <typename T>
constexpr auto has_tombstone_v<
T, std::void_t<typename tombstone_traits<T>::unspecialized>> = false;

template <typename T>
struct tombstone_traits<T, std::enable_if_t<std::is_floating_point_v<T>>> {
constexpr auto operator()() const {
Expand All @@ -35,6 +40,17 @@ struct tombstone_traits<T, std::enable_if_t<std::is_pointer_v<T>>> {
constexpr auto operator()() const { return nullptr; }
};

template <template <typename...> typename L, typename T, typename... Ts>
struct tombstone_traits<
L<T, Ts...>,
std::enable_if_t<std::is_constructible_v<L<T, Ts...>, T, Ts...> and
(has_tombstone_v<T> and ... and has_tombstone_v<Ts>)>> {
constexpr auto operator()() const {
return L<T, Ts...>{tombstone_traits<T>{}(),
tombstone_traits<Ts>{}()...};
}
};

template <auto V> struct tombstone_value {
constexpr auto operator()() const {
if constexpr (stdx::is_cx_value_v<decltype(V)>) {
Expand Down
35 changes: 28 additions & 7 deletions test/optional.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ template <> struct stdx::tombstone_traits<S> {
constexpr auto operator()() const { return S{-1}; }
};

TEST_CASE("tombstone detection", "[optional]") {
STATIC_REQUIRE(stdx::has_tombstone_v<E>);
STATIC_REQUIRE(stdx::has_tombstone_v<S>);
STATIC_REQUIRE(stdx::has_tombstone_v<float>);
STATIC_REQUIRE(stdx::has_tombstone_v<int *>);
STATIC_REQUIRE(not stdx::has_tombstone_v<int>);
}

TEST_CASE("sizeof(optional<T>) == sizeof(T)", "[optional]") {
using O1 = stdx::optional<E>;
STATIC_REQUIRE(sizeof(O1) == sizeof(E));
Expand Down Expand Up @@ -428,18 +436,31 @@ TEST_CASE("tombstone with non-structural value", "[optional]") {
}
#endif

#if __cplusplus >= 202002L
namespace {
template <typename T>
using my_optional = stdx::conditional_t<requires {
typename stdx::tombstone_traits<T>::unspecialized;
}, std::optional<T>, stdx::optional<T>>;
using my_optional = stdx::conditional_t<stdx::has_tombstone_v<T>,
stdx::optional<T>, std::optional<T>>;
} // namespace

TEST_CASE("select optional implementation based on whether tombstone traits "
"are present",
"[optional]") {
static_assert(std::is_same_v<my_optional<S>, stdx::optional<S>>);
static_assert(std::is_same_v<my_optional<int>, std::optional<int>>);
STATIC_REQUIRE(std::is_same_v<my_optional<S>, stdx::optional<S>>);
STATIC_REQUIRE(std::is_same_v<my_optional<int>, std::optional<int>>);
}

TEST_CASE("product types have tombstones iff their components do",
"[optional]") {
STATIC_REQUIRE(not stdx::has_tombstone_v<std::tuple<>>);
STATIC_REQUIRE(stdx::has_tombstone_v<std::tuple<E, S>>);
STATIC_REQUIRE(not stdx::has_tombstone_v<std::tuple<int>>);
STATIC_REQUIRE(stdx::has_tombstone_v<std::pair<E, S>>);
}

TEST_CASE("tombstone traits for product types come from components",
"[optional]") {
constexpr auto o = stdx::optional<std::tuple<E, S, float>>{};
STATIC_REQUIRE(not o);
STATIC_REQUIRE(*o == std::tuple{E{0xffu}, S{-1},
std::numeric_limits<float>::infinity()});
}
#endif