diff --git a/include/iris/rvariant/rvariant.hpp b/include/iris/rvariant/rvariant.hpp index b790646..3d8af45 100644 --- a/include/iris/rvariant/rvariant.hpp +++ b/include/iris/rvariant/rvariant.hpp @@ -79,8 +79,8 @@ constexpr bool non_wrapped_exactly_once_v = non_wrapped_exactly_once::v template struct exactly_once_index { - static_assert(exactly_once_v, "`T` or `recursive_wrapper` or `recursive_wrapper_alloca` must occur exactly once in Ts..."); - static constexpr std::size_t value = find_index_v; + static constexpr std::size_t value = find_index_exactly_once_v; + static_assert(value != find_npos, "`T` or `recursive_wrapper` or `recursive_wrapper_alloca` must occur exactly once in Ts..."); }; template diff --git a/include/iris/type_list.hpp b/include/iris/type_list.hpp index 05484e0..02d7b67 100644 --- a/include/iris/type_list.hpp +++ b/include/iris/type_list.hpp @@ -102,78 +102,55 @@ concept has_identity_base = requires(BasesT* bases) { // -------------------------------------------------- +// A type occurring more than once in `Ts...` is an ambiguous base of `indexed_identity_bases`: it is +// still a base ([meta.rel]), but no pointer converts into it template -struct indexed_virtual_identity : virtual std::type_identity -{ - static void select(std::type_identity*); -}; - -template - requires (sizeof...(Ts) > 0) -struct type_bases...> : indexed_virtual_identity... -{ - template class TT> - using rebind = TT; - - using indexed_virtual_identity::select...; -}; - -template -concept has_exactly_one_identity_base = requires(std::type_identity* base) { - // If multiple bases exist, overload resolution is ambiguous - BasesT::select(base); -}; - -// -------------------------------------------------- +struct indexed_identity : std::type_identity {}; namespace detail { -template class IdentityTT, class Indexes, class... Ts> +template struct indexed_identity_bases_impl; -template class IdentityTT, std::size_t... Is, class... Ts> -struct indexed_identity_bases_impl< - IdentityTT, std::index_sequence, Ts... -> +template +struct indexed_identity_bases_impl, Ts...> { - using type = type_bases...>; + using type = type_bases...>; }; } // detail template -using indexed_virtual_identity_bases = detail::indexed_identity_bases_impl< - indexed_virtual_identity, std::index_sequence_for, Ts... ->::type; +using indexed_identity_bases = detail::indexed_identity_bases_impl, Ts...>::type; // -------------------------------------------------- namespace detail { -template -struct unique_type_list_impl; - -template -struct unique_type_list_impl +// `BasesT` is `identity_bases` of the types seen, and `Accepted` the types accepted +template +struct unique_type_list_impl { - using type = BasesT; + using type = Accepted; }; -template +template requires has_identity_base -struct unique_type_list_impl - : unique_type_list_impl +struct unique_type_list_impl, T, Rest...> + : unique_type_list_impl, Rest...> {}; -template - requires (!has_identity_base>) -struct unique_type_list_impl, T, Rest...> - : unique_type_list_impl, Rest...> +template + requires (!has_identity_base>) +struct unique_type_list_impl, type_list, T, Rest...> + : unique_type_list_impl, type_list, Rest...> {}; } // detail -template +// The types of `List` not in `Excluded`, each once, in the order of their first occurrences. `Excluded` +// has no type more than once. +template> struct unique_type_list; template<> @@ -182,12 +159,10 @@ struct unique_type_list using type = type_list<>; }; -template -struct unique_type_list> +template +struct unique_type_list, type_list> { - using type = detail::unique_type_list_impl< - identity_bases<>, Ts... - >::type::template rebind; + using type = detail::unique_type_list_impl, type_list<>, Ts...>::type; }; // ---------------------------------------------------------- @@ -298,19 +273,14 @@ inline constexpr std::size_t find_npos = static_cast(-1); namespace detail { -template -struct find_index_impl - : std::integral_constant -{}; - -template -struct find_index_impl - : std::conditional_t< - std::is_same_v, - std::integral_constant, - find_index_impl - > -{}; +template +inline constexpr std::size_t find_index_impl = [] { + constexpr bool same[]{std::is_same_v..., false}; + for (std::size_t i = 0; i < sizeof...(Ts); ++i) { + if (same[i]) return i; + } + return find_npos; +}(); } // detail @@ -318,35 +288,51 @@ template struct find_index; template class TT, class... Ts> -struct find_index> : detail::find_index_impl<0, T, Ts...> {}; +struct find_index> : std::integral_constant> {}; template inline constexpr std::size_t find_index_v = find_index::value; -template -struct is_in : std::bool_constant< - has_identity_base> -> +namespace detail { + +template +std::integral_constant index_of_identity(indexed_identity const*); // not defined + +} // detail + +// The index of `T` if `T` occurs exactly once in `List`, `find_npos` otherwise. The deduction from a +// base fails unless exactly one base of `indexed_identity_bases` is `indexed_identity`. +template +struct find_index_exactly_once; + +template class TT, class... Ts> +struct find_index_exactly_once> : std::integral_constant {}; + +template class TT, class... Ts> + requires requires { detail::index_of_identity(static_cast const*>(nullptr)); } +struct find_index_exactly_once> + : decltype(detail::index_of_identity(static_cast const*>(nullptr))) {}; +template +inline constexpr std::size_t find_index_exactly_once_v = find_index_exactly_once::value; + + +template +inline constexpr bool is_in_v = (std::is_same_v || ...); + template -inline constexpr bool is_in_v = has_identity_base>; +struct is_in : std::bool_constant> {}; // ---------------------------------------------------------- template struct exactly_once; +// `has_identity_base` fails for a type occurring more than once, whose base is ambiguous template class TT, class... Ts> -struct exactly_once> : std::false_type -{ - static_assert(sizeof...(Ts) > 0); -}; - -template class TT, class... Ts> - requires has_exactly_one_identity_base> -struct exactly_once> : std::true_type +struct exactly_once> : std::bool_constant>> { static_assert(sizeof...(Ts) > 0); }; diff --git a/test/type_list.cpp b/test/type_list.cpp index 9af2ff4..74f7ac8 100644 --- a/test/type_list.cpp +++ b/test/type_list.cpp @@ -53,6 +53,12 @@ TEST_CASE("type_list") STATIC_CHECK(std::same_as>::type, type_list>); STATIC_CHECK(std::same_as>::type, type_list>); STATIC_CHECK(std::same_as>::type, type_list>); + + STATIC_CHECK(std::same_as, type_list>::type, type_list<>>); + STATIC_CHECK(std::same_as, type_list>::type, type_list<>>); + STATIC_CHECK(std::same_as, type_list>::type, type_list>); + STATIC_CHECK(std::same_as, type_list>::type, type_list>); + STATIC_CHECK(std::same_as, type_list>::type, type_list>); } TEST_CASE("pack_indexing") @@ -79,12 +85,19 @@ TEST_CASE("exactly_once") { STATIC_CHECK(iris::exactly_once_v>); STATIC_CHECK(!iris::exactly_once_v>); + STATIC_CHECK(!iris::exactly_once_v>); + STATIC_CHECK(!iris::exactly_once_v>); + STATIC_CHECK(iris::exactly_once_v>); } TEST_CASE("is_in") { STATIC_CHECK(iris::is_in_v); STATIC_CHECK(!iris::is_in_v); + STATIC_CHECK(!iris::is_in_v); + STATIC_CHECK(iris::is_in_v); + STATIC_CHECK(iris::is_in::value); + STATIC_CHECK(!iris::is_in::value); } TEST_CASE("find_index") @@ -95,4 +108,18 @@ TEST_CASE("find_index") STATIC_CHECK(iris::find_index_v> == iris::find_npos); STATIC_CHECK(iris::find_index_v> == 0); + STATIC_CHECK(iris::find_index_v> == 1); + STATIC_CHECK(iris::find_index_v> == iris::find_npos); + STATIC_CHECK(iris::find_index>::value == 1); +} + +TEST_CASE("find_index_exactly_once") +{ + STATIC_CHECK(iris::find_index_exactly_once_v> == 0); + STATIC_CHECK(iris::find_index_exactly_once_v> == 2); + STATIC_CHECK(iris::find_index_exactly_once_v> == iris::find_npos); + STATIC_CHECK(iris::find_index_exactly_once_v> == iris::find_npos); + + STATIC_CHECK(iris::find_index_exactly_once_v> == iris::find_npos); + STATIC_CHECK(iris::find_index_exactly_once_v> == 1); }