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
4 changes: 2 additions & 2 deletions include/iris/rvariant/rvariant.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ constexpr bool non_wrapped_exactly_once_v = non_wrapped_exactly_once<T, List>::v
template<class T, class Variant>
struct exactly_once_index
{
static_assert(exactly_once_v<T, typename Variant::unwrapped_types>, "`T` or `recursive_wrapper<T>` or `recursive_wrapper_alloca<T, A>` must occur exactly once in Ts...");
static constexpr std::size_t value = find_index_v<T, typename Variant::unwrapped_types>;
static constexpr std::size_t value = find_index_exactly_once_v<T, typename Variant::unwrapped_types>;
static_assert(value != find_npos, "`T` or `recursive_wrapper<T>` or `recursive_wrapper_alloca<T, A>` must occur exactly once in Ts...");
};

template<class T, class Variant>
Expand Down
140 changes: 63 additions & 77 deletions include/iris/type_list.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<Ts...>`: it is
// still a base ([meta.rel]), but no pointer converts into it
template<std::size_t I, class T>
struct indexed_virtual_identity : virtual std::type_identity<T>
{
static void select(std::type_identity<T>*);
};

template<std::size_t... Is, class... Ts>
requires (sizeof...(Ts) > 0)
struct type_bases<indexed_virtual_identity<Is, Ts>...> : indexed_virtual_identity<Is, Ts>...
{
template<template<class...> class TT>
using rebind = TT<Ts...>;

using indexed_virtual_identity<Is, Ts>::select...;
};

template<class T, class BasesT>
concept has_exactly_one_identity_base = requires(std::type_identity<T>* base) {
// If multiple bases exist, overload resolution is ambiguous
BasesT::select(base);
};

// --------------------------------------------------
struct indexed_identity : std::type_identity<T> {};

namespace detail {

template<template<std::size_t, class> class IdentityTT, class Indexes, class... Ts>
template<class Indexes, class... Ts>
struct indexed_identity_bases_impl;

template<template<std::size_t, class> class IdentityTT, std::size_t... Is, class... Ts>
struct indexed_identity_bases_impl<
IdentityTT, std::index_sequence<Is...>, Ts...
>
template<std::size_t... Is, class... Ts>
struct indexed_identity_bases_impl<std::index_sequence<Is...>, Ts...>
{
using type = type_bases<IdentityTT<Is, Ts>...>;
using type = type_bases<indexed_identity<Is, Ts>...>;
};

} // detail

template<class... Ts>
using indexed_virtual_identity_bases = detail::indexed_identity_bases_impl<
indexed_virtual_identity, std::index_sequence_for<Ts...>, Ts...
>::type;
using indexed_identity_bases = detail::indexed_identity_bases_impl<std::index_sequence_for<Ts...>, Ts...>::type;

// --------------------------------------------------

namespace detail {

template<class BasesT, class... Ts>
struct unique_type_list_impl;

template<class BasesT>
struct unique_type_list_impl<BasesT>
// `BasesT` is `identity_bases` of the types seen, and `Accepted` the types accepted
template<class BasesT, class Accepted, class... Ts>
struct unique_type_list_impl
{
using type = BasesT;
using type = Accepted;
};

template<class BasesT, class T, class... Rest>
template<class BasesT, class... Accepted, class T, class... Rest>
requires has_identity_base<T, BasesT>
struct unique_type_list_impl<BasesT, T, Rest...>
: unique_type_list_impl<BasesT, Rest...>
struct unique_type_list_impl<BasesT, type_list<Accepted...>, T, Rest...>
: unique_type_list_impl<BasesT, type_list<Accepted...>, Rest...>
{};

template<class... AcceptedTs, class T, class... Rest>
requires (!has_identity_base<T, identity_bases<AcceptedTs...>>)
struct unique_type_list_impl<identity_bases<AcceptedTs...>, T, Rest...>
: unique_type_list_impl<identity_bases<AcceptedTs..., T>, Rest...>
template<class... Seen, class... Accepted, class T, class... Rest>
requires (!has_identity_base<T, identity_bases<Seen...>>)
struct unique_type_list_impl<identity_bases<Seen...>, type_list<Accepted...>, T, Rest...>
: unique_type_list_impl<identity_bases<Seen..., T>, type_list<Accepted..., T>, Rest...>
{};

} // detail

template<class List = void>
// The types of `List` not in `Excluded`, each once, in the order of their first occurrences. `Excluded`
// has no type more than once.
template<class List = void, class Excluded = type_list<>>
struct unique_type_list;

template<>
Expand All @@ -182,12 +159,10 @@ struct unique_type_list<void>
using type = type_list<>;
};

template<class... Ts>
struct unique_type_list<type_list<Ts...>>
template<class... Ts, class... Excluded>
struct unique_type_list<type_list<Ts...>, type_list<Excluded...>>
{
using type = detail::unique_type_list_impl<
identity_bases<>, Ts...
>::type::template rebind<type_list>;
using type = detail::unique_type_list_impl<identity_bases<Excluded...>, type_list<>, Ts...>::type;
};

// ----------------------------------------------------------
Expand Down Expand Up @@ -298,55 +273,66 @@ inline constexpr std::size_t find_npos = static_cast<std::size_t>(-1);

namespace detail {

template<std::size_t I, class T, class... Ts>
struct find_index_impl
: std::integral_constant<std::size_t, find_npos>
{};

template<std::size_t I, class T, class U, class... Us>
struct find_index_impl<I, T, U, Us...>
: std::conditional_t<
std::is_same_v<T, U>,
std::integral_constant<std::size_t, I>,
find_index_impl<I + 1, T, Us...>
>
{};
template<class T, class... Ts>
inline constexpr std::size_t find_index_impl = [] {
constexpr bool same[]{std::is_same_v<T, Ts>..., false};
for (std::size_t i = 0; i < sizeof...(Ts); ++i) {
if (same[i]) return i;
}
return find_npos;
}();

} // detail

template<class T, class List>
struct find_index;

template<class T, template<class...> class TT, class... Ts>
struct find_index<T, TT<Ts...>> : detail::find_index_impl<0, T, Ts...> {};
struct find_index<T, TT<Ts...>> : std::integral_constant<std::size_t, detail::find_index_impl<T, Ts...>> {};

template<class T, class List>
inline constexpr std::size_t find_index_v = find_index<T, List>::value;


template<class T, class... Ts>
struct is_in : std::bool_constant<
has_identity_base<T, indexed_virtual_identity_bases<Ts...>>
>
namespace detail {

template<class T, std::size_t I>
std::integral_constant<std::size_t, I> index_of_identity(indexed_identity<I, T> 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<I, T>`.
template<class T, class List>
struct find_index_exactly_once;

template<class T, template<class...> class TT, class... Ts>
struct find_index_exactly_once<T, TT<Ts...>> : std::integral_constant<std::size_t, find_npos> {};

template<class T, template<class...> class TT, class... Ts>
requires requires { detail::index_of_identity<T>(static_cast<indexed_identity_bases<Ts...> const*>(nullptr)); }
struct find_index_exactly_once<T, TT<Ts...>>
: decltype(detail::index_of_identity<T>(static_cast<indexed_identity_bases<Ts...> const*>(nullptr)))
{};

template<class T, class List>
inline constexpr std::size_t find_index_exactly_once_v = find_index_exactly_once<T, List>::value;


template<class T, class... Ts>
inline constexpr bool is_in_v = (std::is_same_v<T, Ts> || ...);

template<class T, class... Ts>
inline constexpr bool is_in_v = has_identity_base<T, indexed_virtual_identity_bases<Ts...>>;
struct is_in : std::bool_constant<is_in_v<T, Ts...>> {};

// ----------------------------------------------------------

template<class T, class List>
struct exactly_once;

// `has_identity_base` fails for a type occurring more than once, whose base is ambiguous
template<class T, template<class...> class TT, class... Ts>
struct exactly_once<T, TT<Ts...>> : std::false_type
{
static_assert(sizeof...(Ts) > 0);
};

template<class T, template<class...> class TT, class... Ts>
requires has_exactly_one_identity_base<T, indexed_virtual_identity_bases<Ts...>>
struct exactly_once<T, TT<Ts...>> : std::true_type
struct exactly_once<T, TT<Ts...>> : std::bool_constant<has_identity_base<T, indexed_identity_bases<Ts...>>>
{
static_assert(sizeof...(Ts) > 0);
};
Expand Down
27 changes: 27 additions & 0 deletions test/type_list.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ TEST_CASE("type_list")
STATIC_CHECK(std::same_as<unique_type_list<type_list<A, B, C, A>>::type, type_list<A, B, C>>);
STATIC_CHECK(std::same_as<unique_type_list<type_list<A, B, C, B>>::type, type_list<A, B, C>>);
STATIC_CHECK(std::same_as<unique_type_list<type_list<A, B, C, C>>::type, type_list<A, B, C>>);

STATIC_CHECK(std::same_as<unique_type_list<type_list<>, type_list<A>>::type, type_list<>>);
STATIC_CHECK(std::same_as<unique_type_list<type_list<A>, type_list<A>>::type, type_list<>>);
STATIC_CHECK(std::same_as<unique_type_list<type_list<A, B>, type_list<C>>::type, type_list<A, B>>);
STATIC_CHECK(std::same_as<unique_type_list<type_list<A, B, A, C>, type_list<C>>::type, type_list<A, B>>);
STATIC_CHECK(std::same_as<unique_type_list<type_list<C, B, A, B>, type_list<A, C>>::type, type_list<B>>);
}

TEST_CASE("pack_indexing")
Expand All @@ -79,12 +85,19 @@ TEST_CASE("exactly_once")
{
STATIC_CHECK(iris::exactly_once_v<int, type_list<int, float>>);
STATIC_CHECK(!iris::exactly_once_v<int, type_list<int, int>>);
STATIC_CHECK(!iris::exactly_once_v<int, type_list<float, double>>);
STATIC_CHECK(!iris::exactly_once_v<int, type_list<int, float, int, int>>);
STATIC_CHECK(iris::exactly_once_v<float, type_list<int, float, int>>);
}

TEST_CASE("is_in")
{
STATIC_CHECK(iris::is_in_v<int, int, float>);
STATIC_CHECK(!iris::is_in_v<int, float>);
STATIC_CHECK(!iris::is_in_v<int>);
STATIC_CHECK(iris::is_in_v<int, float, int, int>);
STATIC_CHECK(iris::is_in<int, float, int>::value);
STATIC_CHECK(!iris::is_in<int, float>::value);
}

TEST_CASE("find_index")
Expand All @@ -95,4 +108,18 @@ TEST_CASE("find_index")
STATIC_CHECK(iris::find_index_v<int, type_list<float, double>> == iris::find_npos);

STATIC_CHECK(iris::find_index_v<int, type_list<int, int, double>> == 0);
STATIC_CHECK(iris::find_index_v<float, type_list<int, float, float>> == 1);
STATIC_CHECK(iris::find_index_v<int, type_list<>> == iris::find_npos);
STATIC_CHECK(iris::find_index<double, type_list<int, double>>::value == 1);
}

TEST_CASE("find_index_exactly_once")
{
STATIC_CHECK(iris::find_index_exactly_once_v<int, type_list<int, float, double>> == 0);
STATIC_CHECK(iris::find_index_exactly_once_v<double, type_list<int, float, double>> == 2);
STATIC_CHECK(iris::find_index_exactly_once_v<int, type_list<float, double>> == iris::find_npos);
STATIC_CHECK(iris::find_index_exactly_once_v<int, type_list<>> == iris::find_npos);

STATIC_CHECK(iris::find_index_exactly_once_v<int, type_list<int, float, int>> == iris::find_npos);
STATIC_CHECK(iris::find_index_exactly_once_v<float, type_list<int, float, int>> == 1);
}
Loading