Skip to content

Commit 4a768f6

Browse files
authored
Merge pull request #265 from elbeno/product-type-tombstones
✨ Add optional tombstones for product types
2 parents 36ca876 + 4711888 commit 4a768f6

File tree

2 files changed

+44
-7
lines changed

2 files changed

+44
-7
lines changed

include/stdx/optional.hpp

Lines changed: 16 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,17 @@ 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 T, typename... Ts>
44+
struct tombstone_traits<
45+
L<T, Ts...>,
46+
std::enable_if_t<std::is_constructible_v<L<T, Ts...>, T, Ts...> and
47+
(has_tombstone_v<T> and ... and has_tombstone_v<Ts>)>> {
48+
constexpr auto operator()() const {
49+
return L<T, Ts...>{tombstone_traits<T>{}(),
50+
tombstone_traits<Ts>{}()...};
51+
}
52+
};
53+
3854
template <auto V> struct tombstone_value {
3955
constexpr auto operator()() const {
4056
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)