Skip to content

Commit

Permalink
value_list_concat_t and type_list_concat_t for concatenating `vta…
Browse files Browse the repository at this point in the history
…g_t` & `tag_t`

Summary: Since these support "any input template(s)" to "any output template", this can also be used to re-wrap one value list template type in another, which something I've had to do before.

Reviewed By: DenisYaroshevskiy

Differential Revision: D62254135

fbshipit-source-id: 18ac9e509f4eaf7968841a08db22a0ee011828d6
  • Loading branch information
Alexey Spiridonov authored and facebook-github-bot committed Oct 5, 2024
1 parent 6d04a53 commit 55d703a
Show file tree
Hide file tree
Showing 2 changed files with 136 additions and 0 deletions.
93 changes: 93 additions & 0 deletions folly/Traits.h
Original file line number Diff line number Diff line change
Expand Up @@ -1173,6 +1173,59 @@ template <typename List>
using type_list_size_t =
decltype(traits_detail::type_list_size_(static_cast<List const*>(nullptr)));

namespace detail {

// The arguments to this "error" type help the user debug bad invocations.
// It is purposely undefined to cause a compile error.
template <typename...>
struct error_list_concat_params_should_be_non_cvref;

// The primary template is only invoked for invalid parameters.
template <template <typename...> class Out, typename... T>
inline constexpr auto type_list_concat_ =
error_list_concat_params_should_be_non_cvref<T...>{};

template <template <typename...> class Out>
inline constexpr type_identity<Out<>> type_list_concat_<Out>;

template <
template <typename...>
class Out,
template <typename...>
class In,
typename... T>
inline constexpr auto type_list_concat_<Out, In<T...>> =
type_identity<Out<T...>>{};

template <
template <typename...>
class Out,
// Allow input lists to come from heterogeneous templates.
template <typename...>
class InA,
typename... A,
template <typename...>
class InB,
typename... B,
typename... Tail>
inline constexpr auto type_list_concat_<Out, InA<A...>, InB<B...>, Tail...> =
// Avoid instantiating the `In*` or `Out` types for the intermediate
// lists, since those types may be invalid, or expensive. Per my tests
// on clang using `tag_t` for the intermediate list is no more expensive
// than using a dedicated incomplete list type.
type_list_concat_<Out, tag_t<A..., B...>, Tail...>;

} // namespace detail

/// type_list_concat_t
///
/// Each `List` is a type list of the form `InK<TypeK...>`, where the
/// templates `InK` are potentially heterogeneous. Concatenates these
/// `List`s into a single type list `Out<Type1..., Type2..., ...>`.
template <template <typename...> class Out, typename... List>
using type_list_concat_t =
typename decltype(detail::type_list_concat_<Out, List...>)::type;

namespace traits_detail {

template <decltype(auto) V>
Expand Down Expand Up @@ -1260,6 +1313,46 @@ inline constexpr value_list_element_type_t<I, List> value_list_element_v =

namespace detail {

// The primary template is only invoked for invalid parameters.
template <template <auto...> class Out, typename... T>
inline constexpr auto value_list_concat_ =
error_list_concat_params_should_be_non_cvref<T...>{};

template <template <auto...> class Out>
inline constexpr type_identity<Out<>> value_list_concat_<Out>;

template <template <auto...> class Out, template <auto...> class In, auto... V>
inline constexpr auto value_list_concat_<Out, In<V...>> =
type_identity<Out<V...>>{};

template <
template <auto...>
class Out,
// Allow input lists to come from heterogeneous templates.
template <auto...>
class InA,
auto... A,
template <auto...>
class InB,
auto... B,
typename... Tail>
inline constexpr auto value_list_concat_<Out, InA<A...>, InB<B...>, Tail...> =
// The use of `vtag_t` is explained in the analogous `type_list_concat_.
value_list_concat_<Out, vtag_t<A..., B...>, Tail...>;

} // namespace detail

/// value_list_concat_t
///
/// Each `List` is a value list of the form `InK<ValK...>`, where the
/// templates `InK` are potentially heterogeneous. Concatenates these
/// `List`s into a single value list `Out<Val1..., Val2..., ...>`.
template <template <auto...> class Out, typename... List>
using value_list_concat_t =
typename decltype(detail::value_list_concat_<Out, List...>)::type;

namespace detail {

template <typename V, typename... T>
constexpr std::size_t type_pack_find_() {
bool eq[] = {std::is_same_v<V, T>..., true};
Expand Down
43 changes: 43 additions & 0 deletions folly/test/TraitsTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,26 @@ TEST(Traits, type_list_size) {
5, (type_list_size_t<tag_t<long long, long, int, short, char>>::value));
}

TEST(Traits, type_list_concat) {
static_assert(std::is_same_v<tag_t<>, type_list_concat_t<tag_t>>);
static_assert(std::is_same_v<tag_t<>, type_list_concat_t<tag_t, tag_t<>>>);
static_assert(
std::is_same_v<tag_t<>, type_list_concat_t<tag_t, tag_t<>, tag_t<>>>);
static_assert(
std::is_same_v<tag_t<int>, type_list_concat_t<tag_t, tag_t<int>>>);
static_assert(std::is_same_v<
tag_t<int, double>,
type_list_concat_t<tag_t, tag_t<int>, tag_t<double>>>);
static_assert( //
std::is_same_v<
tag_t<int, void, double, inspects_tag>,
type_list_concat_t<
tag_t,
std::tuple<int, void, double>,
tag_t<>,
tag_t<inspects_tag>>>);
}

TEST(Traits, value_pack) {
EXPECT_EQ(3, (folly::value_pack_size_v<7u, 8, '9'>));
EXPECT_EQ(3, (folly::value_pack_size_t<7u, 8, '9'>::value));
Expand All @@ -743,6 +763,29 @@ TEST(Traits, value_list) {
EXPECT_EQ(8, (folly::value_list_element_v<1, vtag_t<7u, 8, '9'>>));
}

template <auto...>
struct also_vtag_t {};

TEST(Traits, value_list_concat) {
static_assert(std::is_same_v<vtag_t<>, value_list_concat_t<vtag_t>>);
static_assert(
std::is_same_v<vtag_t<>, value_list_concat_t<vtag_t, vtag_t<>>>);
static_assert(
std::
is_same_v<vtag_t<>, value_list_concat_t<vtag_t, vtag_t<>, vtag_t<>>>);
static_assert(
std::is_same_v<vtag_t<7>, value_list_concat_t<vtag_t, vtag_t<7>>>);
static_assert( //
std::is_same_v<
vtag_t<3, 1, 4, 1, 5, 9>,
value_list_concat_t<
vtag_t,
also_vtag_t<3, 1>,
vtag_t<>,
vtag_t<4>,
vtag_t<1, 5, 9>>>);
}

TEST(Traits, type_pack_find) {
EXPECT_EQ(0, folly::type_pack_find_v<int>);
EXPECT_EQ(0, folly::type_pack_find_t<int>{});
Expand Down

0 comments on commit 55d703a

Please sign in to comment.