Skip to content

Commit 186a608

Browse files
committed
✨ Add optional tombstones for product types
Problem: - Product types cannot easily be put into `stdx::optional` without specifying a separate tombstone value. Solution: - If the types inside the product already have tombstone values, use them.
1 parent 36ca876 commit 186a608

File tree

2 files changed

+46
-7
lines changed

2 files changed

+46
-7
lines changed

include/stdx/optional.hpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ template <typename T, typename = void> struct tombstone_traits {
2323
}
2424
};
2525

26+
template <typename T, typename = void> constexpr auto has_tombstone_v = true;
27+
template <typename T>
28+
constexpr auto has_tombstone_v<
29+
T, std::void_t<typename tombstone_traits<T>::unspecialized>> = false;
30+
2631
template <typename T>
2732
struct tombstone_traits<T, std::enable_if_t<std::is_floating_point_v<T>>> {
2833
constexpr auto operator()() const {
@@ -35,6 +40,19 @@ struct tombstone_traits<T, std::enable_if_t<std::is_pointer_v<T>>> {
3540
constexpr auto operator()() const { return nullptr; }
3641
};
3742

43+
template <template <typename...> typename L, typename... Ts>
44+
constexpr auto has_tombstone_v<
45+
L<Ts...>,
46+
std::void_t<std::enable_if_t<std::is_constructible_v<L<Ts...>, Ts...>>>> =
47+
((sizeof...(Ts) > 0) and ... and has_tombstone_v<Ts>);
48+
49+
template <template <typename...> typename L, typename... Ts>
50+
struct tombstone_traits<L<Ts...>, std::enable_if_t<has_tombstone_v<L<Ts...>>>> {
51+
constexpr auto operator()() const {
52+
return L<Ts...>{tombstone_traits<Ts>{}()...};
53+
}
54+
};
55+
3856
template <auto V> struct tombstone_value {
3957
constexpr auto operator()() const {
4058
if constexpr (stdx::is_cx_value_v<decltype(V)>) {

test/optional.cpp

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,14 @@ template <> struct stdx::tombstone_traits<S> {
3434
constexpr auto operator()() const { return S{-1}; }
3535
};
3636

37+
TEST_CASE("tombstone detection", "[optional]") {
38+
STATIC_REQUIRE(stdx::has_tombstone_v<E>);
39+
STATIC_REQUIRE(stdx::has_tombstone_v<S>);
40+
STATIC_REQUIRE(stdx::has_tombstone_v<float>);
41+
STATIC_REQUIRE(stdx::has_tombstone_v<int *>);
42+
STATIC_REQUIRE(not stdx::has_tombstone_v<int>);
43+
}
44+
3745
TEST_CASE("sizeof(optional<T>) == sizeof(T)", "[optional]") {
3846
using O1 = stdx::optional<E>;
3947
STATIC_REQUIRE(sizeof(O1) == sizeof(E));
@@ -428,18 +436,31 @@ TEST_CASE("tombstone with non-structural value", "[optional]") {
428436
}
429437
#endif
430438

431-
#if __cplusplus >= 202002L
432439
namespace {
433440
template <typename T>
434-
using my_optional = stdx::conditional_t<requires {
435-
typename stdx::tombstone_traits<T>::unspecialized;
436-
}, std::optional<T>, stdx::optional<T>>;
441+
using my_optional = stdx::conditional_t<stdx::has_tombstone_v<T>,
442+
stdx::optional<T>, std::optional<T>>;
437443
} // namespace
438444

439445
TEST_CASE("select optional implementation based on whether tombstone traits "
440446
"are present",
441447
"[optional]") {
442-
static_assert(std::is_same_v<my_optional<S>, stdx::optional<S>>);
443-
static_assert(std::is_same_v<my_optional<int>, std::optional<int>>);
448+
STATIC_REQUIRE(std::is_same_v<my_optional<S>, stdx::optional<S>>);
449+
STATIC_REQUIRE(std::is_same_v<my_optional<int>, std::optional<int>>);
450+
}
451+
452+
TEST_CASE("product types have tombstones iff their components do",
453+
"[optional]") {
454+
STATIC_REQUIRE(not stdx::has_tombstone_v<std::tuple<>>);
455+
STATIC_REQUIRE(stdx::has_tombstone_v<std::tuple<E, S>>);
456+
STATIC_REQUIRE(not stdx::has_tombstone_v<std::tuple<int>>);
457+
STATIC_REQUIRE(stdx::has_tombstone_v<std::pair<E, S>>);
458+
}
459+
460+
TEST_CASE("tombstone traits for product types come from components",
461+
"[optional]") {
462+
constexpr auto o = stdx::optional<std::tuple<E, S, float>>{};
463+
STATIC_REQUIRE(not o);
464+
STATIC_REQUIRE(*o == std::tuple{E{0xffu}, S{-1},
465+
std::numeric_limits<float>::infinity()});
444466
}
445-
#endif

0 commit comments

Comments
 (0)