Suppose we have a variadic templated class like
template<class...Ts>
class X{
template<size_t I>
constexpr bool shouldSelect();
std::tuple<TransformedTs...> mResults; // this is want I want eventually
};
where the implementation of shouldSelect is not provided, but what it does is that, given an index i referring to the ith element of the variadic Ts, tells you whether we should select it to the subset.
I want to do a transformation on Ts such that only classes Ts at indexes that results in shouldSelect returning true should be selected. Is there an easy way to do this?
For example, if shouldSelect returns true for I = 1,2,4, and Ts... = short, int, double, T1, T2, then I want to get a TransformedTs... that is made up of int, double, T2. Then I can use this TransformedTs... in the same class.
If you're able to use C++17, this is pretty easy to implement using a combination of if constexpr and expression folding.
Start with some helper types, one with parameters to track the arguments to X::shouldSelect<I>(), and the other with a type to test.
template <typename T, size_t I, typename...Ts>
struct Accumulator {
using Type = std::tuple<Ts...>;
};
template <typename T>
struct Next { };
Then an operator overload either adds the type to the accumulator, or not with if constexpr:
template <typename TAcc, size_t I, typename... Ts, typename TArg>
decltype(auto) operator +(Accumulator<TAcc, I, Ts...>, Next<TArg>) {
if constexpr (TAcc::template shouldSelect<I>()) {
return Accumulator<TAcc, I + 1, Ts..., TArg>{};
} else {
return Accumulator<TAcc, I + 1, Ts...>{};
}
}
Finally, you can put it all together with a fold expression and extract the type with decltype:
template <template <typename... Ts> class T, typename... Ts>
constexpr decltype(auto) FilterImpl(const T<Ts...>&) {
return (Accumulator<T<Ts...>, 0>{} + ... + Next<Ts>{});
}
template<typename T>
using FilterT = typename decltype(FilterImpl(std::declval<T>()))::Type;
Usage:
using Result = FilterT<X<int, double, bool, etc>>;
Demo: https://godbolt.org/z/9h89zG
If you don't have C++17 available to you, it's still possible. You can do the same sort of conditional type transfer using a recursive inheritance chain to iterate though each type in the parameter pack, and std::enable_if to do the conditional copy. Below is the same code, but working in C++11:
// Dummy type for copying parameter packs
template <typename... Ts>
struct Mule {};
/* Filter implementation */
template <typename T, typename Input, typename Output, size_t I, typename = void>
struct FilterImpl;
template <typename T, typename THead, typename... TTail, typename... OutputTs, size_t I>
struct FilterImpl<T, Mule<THead, TTail...>, Mule<OutputTs...>, I, typename std::enable_if<( T::template shouldSelect<I>() )>::type >
: FilterImpl<T, Mule<TTail...>, Mule<OutputTs..., THead>, (I + 1)>
{ };
template <typename T, typename THead, typename... TTail, typename... OutputTs, size_t I>
struct FilterImpl<T, Mule<THead, TTail...>, Mule<OutputTs...>, I, typename std::enable_if<( !T::template shouldSelect<I>() )>::type >
: FilterImpl<T, Mule<TTail...>, Mule<OutputTs...>, (I + 1)>
{ };
template <typename T, typename... OutputTs, size_t I>
struct FilterImpl<T, Mule<>, Mule<OutputTs...>, I>
{
using Type = std::tuple<OutputTs...>;
};
/* Helper types */
template <typename T>
struct Filter;
template <template <typename... Ts> class T, typename... Ts>
struct Filter<T<Ts...>> : FilterImpl<T<Ts...>, Mule<Ts...>, Mule<>, 0>
{ };
template <typename T>
using FilterT = typename Filter<T>::Type;
Demo: https://godbolt.org/z/esso4M
Related
let's say for example I have a function foo() taking a parameter pack Ts. The function however should only accept the parameter pack Ts if there is exactly one type meeting the requirements of std::is_integral. The following code I've written does exactly that as expected.
#include <type_traits>
template <template <typename> typename predicate, typename... Ts>
inline constexpr std::size_t count_if = ((predicate<Ts>::value ? 1 : 0) + ...);
// this method only compiles if exactly one of the given template arguments is of an integral type
template <typename... Ts>
void foo() requires (count_if<std::is_integral, Ts...> == 1) {}
int main()
{
foo<int, double, float>(); //this compiles
//foo<int, long, float>(); //this doesn't, as it should
}
However, let's say the function foo() is being extended by a parameter of template type T like this:
#include <type_traits>
template <template <typename> typename predicate, typename... Ts>
inline constexpr std::size_t count_if = ((predicate<Ts>::value ? 1 : 0) + ...);
// this method only compiles if one of the given template arguments is of an integral type
template <typename T, typename... Ts>
void foo(T) requires (count_if<std::is_integral, Ts...> == 1) {}
int main()
{
foo<double, int, double, float>(double{}); //this compiles
foo<int, int, double, float>(int{}); //this compiles
//foo<double, int, long, float>(double{}); //this doesn't, as it should
}
And now to my question: Is it possible, to make the predicate I'm giving to my count_if function dependent of the template parameter T of my function foo()?
For example, so I could check following:
#include <type_traits>
template <template <typename> typename predicate, typename... Ts>
inline constexpr std::size_t count_if = ((predicate<Ts>::value ? 1 : 0) + ...);
template <typename T, typename... Ts>
void foo(T) requires (count_if<std::is_same<T, ?????> , Ts...> == 1) {}
int main()
{
foo<double, int, double, float>(double{}); //this should compile, as there is exactly one type in parameter pack of type double
foo<int, int, double, float>(int{}); //this should compile, as there is exactly one type in parameter pack of type int
//foo<int, double, float>(); //this shouldn't compile, as there is no int in parameter pack
//foo<double, double, double>(); //this shouldn't compile, as there are two doubles in paramter pack
}
Is this somehow possible? I tried to check it using static_assert() inside the function body, but I can not define a template struct inside it, which I would need so I could write my own predicate like this for example:
template <typename T, typename... Ts>
void foo(T)
{
template <typename U>
struct custom_predicate
{
static constexpr bool value = std::is_same<T, U>::value;
};
static_assert(count_if<custom_predicate, Ts...> == 1);
}
But like I said this isn't possible because I can't define template structs inside a function body.
Does someone have an idea?
It seems to me you're looking for
template <template <typename, typename> typename predicate, typename T, typename... Ts>
inline constexpr std::size_t count_if = ((predicate<T, Ts>::value ? 1 : 0) + ...);
template <typename T, typename... Ts>
void foo(T) requires (count_if<std::is_same, T, Ts...> == 1) {}
But, this way, you have to change the count_if requirements.
If you want (as you tried in the static_assert() example) a way to generate a specific is_same fixing the first type, you can write something similar your custom_predicate but outside the functions.
For example
template <template <typename> typename predicate, typename... Ts>
inline constexpr std::size_t count_if = ((predicate<Ts>::value ? 1 : 0) + ...);
template <typename T>
struct my_predicate
{
template <typename U>
using my_is_same = std::is_same<T, U>;
};
template <typename T, typename... Ts>
void foo(T) requires (count_if<my_predicate<T>::template my_is_same, Ts...> == 1)
{ /* ... */ }
Or maybe, to be more flexible (and passing the same std::is_same as parameter)
template <template <typename> typename predicate, typename... Ts>
inline constexpr std::size_t count_if = ((predicate<Ts>::value ? 1 : 0) + ...);
template <template <typename...> class C, typename ... Ts>
struct my_predicate
{
template <typename ... Us>
using my_is_same = C<Ts..., Us...>;
};
template <typename T, typename... Ts>
void foo(T)
requires (count_if<my_predicate<std::is_same, T>::template my_is_same, Ts...> == 1)
{ }
Is there a utility in the standard library to get the index of a given type in std::variant? Or should I make one for myself? That is, I want to get the index of B in std::variant<A, B, C> and have that return 1.
There is std::variant_alternative for the opposite operation. Of course, there could be many same types on std::variant's list, so this operation is not a bijection, but it isn't a problem for me (I can have first occurrence of type on list, or unique types on std::variant list).
Update a few years later: My answer here may be a cool answer, but this is the correct one. That is how I would solve this problem today.
We could take advantage of the fact that index() almost already does the right thing.
We can't arbitrarily create instances of various types - we wouldn't know how to do it, and arbitrary types might not be literal types. But we can create instances of specific types that we know about:
template <typename> struct tag { }; // <== this one IS literal
template <typename T, typename V>
struct get_index;
template <typename T, typename... Ts>
struct get_index<T, std::variant<Ts...>>
: std::integral_constant<size_t, std::variant<tag<Ts>...>(tag<T>()).index()>
{ };
That is, to find the index of B in variant<A, B, C> we construct a variant<tag<A>, tag<B>, tag<C>> with a tag<B> and find its index.
This only works with distinct types.
I found this answer for tuple and slightly modificated it:
template<typename VariantType, typename T, std::size_t index = 0>
constexpr std::size_t variant_index() {
static_assert(std::variant_size_v<VariantType> > index, "Type not found in variant");
if constexpr (index == std::variant_size_v<VariantType>) {
return index;
} else if constexpr (std::is_same_v<std::variant_alternative_t<index, VariantType>, T>) {
return index;
} else {
return variant_index<VariantType, T, index + 1>();
}
}
It works for me, but now I'm curious how to do it in old way without constexpr if, as a structure.
You can also do this with a fold expression:
template <typename T, typename... Ts>
constexpr size_t get_index(std::variant<Ts...> const&) {
size_t r = 0;
auto test = [&](bool b){
if (!b) ++r;
return b;
};
(test(std::is_same_v<T,Ts>) || ...);
return r;
}
The fold expression stops the first time we match a type, at which point we stop incrementing r. This works even with duplicate types. If a type is not found, the size is returned. This could be easily changed to not return in this case if that's preferable, since missing return in a constexpr function is ill-formed.
If you dont want to take an instance of variant, the argument here could instead be a tag<variant<Ts...>>.
With Boost.Mp11 this is a short, one-liner:
template<typename Variant, typename T>
constexpr size_t IndexInVariant = mp_find<Variant, T>::value;
Full example:
#include <variant>
#include <boost/mp11/algorithm.hpp>
using namespace boost::mp11;
template<typename Variant, typename T>
constexpr size_t IndexInVariant = mp_find<Variant, T>::value;
int main()
{
using V = std::variant<int,double, char, double>;
static_assert(IndexInVariant<V, int> == 0);
// for duplicates first idx is returned
static_assert(IndexInVariant<V, double> == 1);
static_assert(IndexInVariant<V, char> == 2);
// not found returns ".end()"/ or size of variant
static_assert(IndexInVariant<V, float> == 4);
// beware that const and volatile and ref are not stripped
static_assert(IndexInVariant<V, int&> == 4);
static_assert(IndexInVariant<V, const int> == 4);
static_assert(IndexInVariant<V, volatile int> == 4);
}
One fun way to do this is to take your variant<Ts...> and turn it into a custom class hierarchy that all implement a particular static member function with a different result that you can query.
In other words, given variant<A, B, C>, create a hierarchy that looks like:
struct base_A {
static integral_constant<int, 0> get(tag<A>);
};
struct base_B {
static integral_constant<int, 1> get(tag<B>);
};
struct base_C {
static integral_constant<int, 2> get(tag<C>);
};
struct getter : base_A, base_B, base_C {
using base_A::get, base_B::get, base_C::get;
};
And then, decltype(getter::get(tag<T>())) is the index (or doesn't compile). Hopefully that makes sense.
In real code, the above becomes:
template <typename T> struct tag { };
template <std::size_t I, typename T>
struct base {
static std::integral_constant<size_t, I> get(tag<T>);
};
template <typename S, typename... Ts>
struct getter_impl;
template <std::size_t... Is, typename... Ts>
struct getter_impl<std::index_sequence<Is...>, Ts...>
: base<Is, Ts>...
{
using base<Is, Ts>::get...;
};
template <typename... Ts>
struct getter : getter_impl<std::index_sequence_for<Ts...>, Ts...>
{ };
And once you establish a getter, actually using it is much more straightforward:
template <typename T, typename V>
struct get_index;
template <typename T, typename... Ts>
struct get_index<T, std::variant<Ts...>>
: decltype(getter<Ts...>::get(tag<T>()))
{ };
That only works in the case where the types are distinct. If you need it to work with independent types, then the best you can do is probably a linear search?
template <typename T, typename>
struct get_index;
template <size_t I, typename... Ts>
struct get_index_impl
{ };
template <size_t I, typename T, typename... Ts>
struct get_index_impl<I, T, T, Ts...>
: std::integral_constant<size_t, I>
{ };
template <size_t I, typename T, typename U, typename... Ts>
struct get_index_impl<I, T, U, Ts...>
: get_index_impl<I+1, T, Ts...>
{ };
template <typename T, typename... Ts>
struct get_index<T, std::variant<Ts...>>
: get_index_impl<0, T, Ts...>
{ };
My two cents solutions:
template <typename T, typename... Ts>
constexpr std::size_t variant_index_impl(std::variant<Ts...>**)
{
std::size_t i = 0; ((!std::is_same_v<T, Ts> && ++i) && ...); return i;
}
template <typename T, typename V>
constexpr std::size_t variant_index_v = variant_index_impl<T>(static_cast<V**>(nullptr));
template <typename T, typename V, std::size_t... Is>
constexpr std::size_t variant_index_impl(std::index_sequence<Is...>)
{
return ((std::is_same_v<T, std::variant_alternative_t<Is, V>> * Is) + ...);
}
template <typename T, typename V>
constexpr std::size_t variant_index_v = variant_index_impl<T, V>(std::make_index_sequence<std::variant_size_v<V>>{});
If you wish a hard error on lookups of not containing type or duplicate type - here are static asserts:
constexpr auto occurrences = (std::is_same_v<T, Ts> + ...);
static_assert(occurrences != 0, "The variant cannot have the type");
static_assert(occurrences <= 1, "The variant has duplicates of the type");
Another take on it:
#include <type_traits>
namespace detail {
struct count_index {
std::size_t value = 0;
bool found = false;
template <typename T, typename U>
constexpr count_index operator+(const std::is_same<T, U> &rhs)
{
if (found)
return *this;
return { value + !rhs, rhs};
}
};
}
template <typename Seq, typename T>
struct index_of;
template <template <typename...> typename Seq, typename... Ts, typename T>
struct index_of<Seq<Ts...>, T>: std::integral_constant<std::size_t, (detail::count_index{} + ... + std::is_same<T, Ts>{}).value> {
static_assert(index_of::value < sizeof...(Ts), "Sequence doesn't contain the type");
};
And then:
#include <variant>
struct A{};
struct B{};
struct C{};
using V = std::variant<A, B, C>;
static_assert(index_of<V, B>::value == 1);
Or:
static_assert(index_of<std::tuple<int, float, bool>, float>::value == 1);
See on godbolt: https://godbolt.org/z/7ob6veWGr
I have a function foo that calls a function bar with a subset of types passed into foo's variadic template. For example:
template <typename... T>
void foo() {
// ...
template <size_t start_idx, typename... T>
using param_pack = /*Parameter pack with T[start_idx]...T[N]*/
auto b = bar<param_pack<2, T...>>();
// ...
}
Is there a way to extract a "sub-parameter pack". In the above case
if T = [int float char double] then param_pack<2, T...> = [char double]
[EDIT]
My goal is to be able to use something like this to match event handlers. For example
struct ev {};
template <typename... T>
struct event : ev {
std::tuple<T...> data_;
event(T&&... d) : data_(std::make_tuple(std::forward<T>(d)...)) {}
};
template <typename... Functor>
struct handler {
std::tuple<Functor...> funcs_;
handler(Functor&&... f) : funcs_(std::make_tuple(std::forward<Functor>(f)...)) {}
void handle_message(ev* e) {
auto ptrs = std::make_tuple(
dynamic_cast<event<param_pack<1, typename function_traits<F>::args>>*>(e)...
);
match(ptrs);
}
};
Here function_traits::args get a parameter pack for the function arguments and match iterates over the the tuple funcs_ checking if the dynamic_cast was successful and executing the first successful function. I already have these implemented.
The handlers are something like
[] (handler* self, <ARGS>) -> void {
// ...
}
I am essentially trying to get rid of the self argument.
Set aside the fact that it lacks a check on the index N for simplicity, here is a possible solution based on a function declaration (no definition required) and an using declaration:
template<std::size_t N, typename... T, std::size_t... I>
std::tuple<std::tuple_element_t<N+I, std::tuple<T...>>...>
sub(std::index_sequence<I...>);
template<std::size_t N, typename... T>
using subpack = decltype(sub<N, T...>(std::make_index_sequence<sizeof...(T) - N>{}));
The good part of this approach is that you have not to introduce a new type designed around a tuple, then specialize it somehow iteratively.
It follows a minimal, working example that uses the code above:
#include<functional>
#include<tuple>
#include<cstddef>
#include<type_traits>
template<std::size_t N, typename... T, std::size_t... I>
std::tuple<std::tuple_element_t<N+I, std::tuple<T...>>...>
sub(std::index_sequence<I...>);
template<std::size_t N, typename... T>
using subpack = decltype(sub<N, T...>(std::make_index_sequence<sizeof...(T) - N>{}));
int main() {
static_assert(std::is_same<subpack<2, int, float, char, double>, std::tuple<char, double>>::value, "!");
}
See a full example up and running on wandbox.
The extended version that includes a check on the index N would look like this:
template<std::size_t N, typename... T, std::size_t... I>
std::enable_if_t<(N < sizeof...(T)), std::tuple<std::tuple_element_t<N+I, std::tuple<T...>>...>>
sub(std::index_sequence<I...>);
That is the type you can see in the first example once wrapped in a std::enable_if_t, nothing more. Again, declaration is enough, no definition required.
EDIT
If you want to use your own class template instead of an std::tuple, you can easily modify the code to do that:
#include<functional>
#include<tuple>
#include<cstddef>
#include<type_traits>
template<typename...>
struct bar {};
template<template<typename...> class C, std::size_t N, typename... T, std::size_t... I>
std::enable_if_t<(N < sizeof...(T)), C<std::tuple_element_t<N+I, std::tuple<T...>>...>>
sub(std::index_sequence<I...>);
template<template<typename...> class C, std::size_t N, typename... T>
using subpack = decltype(sub<C, N, T...>(std::make_index_sequence<sizeof...(T) - N>{}));
int main() {
static_assert(std::is_same<subpack<bar, 2, int, float, char, double>, bar<char, double>>::value, "!");
}
EDIT
According to the code added to the question, the solution above is still valid. You should just define your event class as it follows:
struct ev {};
template <typename>
struct event;
template <typename... T>
struct event<std::tuple<T...>>: ev {
// ...
};
This way, when you do this:
event<param_pack<1, typename function_traits<F>::args>>
You still get a tuple out of param_pack (that is the subpack using declaration in my example), but it matches the template partial specialization of event and the parameter pack is at your disposal as T....
This is the best you can do, for you cannot put a parameter pack in an using declaration. Anyway it just works, so probably it can solve your issue.
You may do something like:
template <std::size_t N, typename ... Ts> struct drop;
template <typename ... Ts>
struct drop<0, Ts...>
{
using type = std::tuple<Ts...>;
};
template <std::size_t N, typename T, typename ... Ts>
struct drop<N, T, Ts...>
{
using type = typename drop<N - 1, Ts...>;
};
// Specialization to avoid the ambiguity
template <typename T, typename... Ts>
struct drop<0, T, Ts...>
{
using type = std::tuple<T, Ts...>;
};
Here is a quick but not particularly reusable solution.
template <typename Pack, std::size_t N, std::size_t... Is>
void invoke_bar_impl(std::index_sequence<Is...>) {
bar<std::tuple_element_t<N + Is, Pack>...>();
}
template <std::size_t N, typename... Ts>
void invoke_bar() {
auto indices = std::make_index_sequence<sizeof...(Ts) - N>();
invoke_bar_impl<std::tuple<Ts...>, N>(indices);
}
One thing that really annoys me about C++ is that an empty struct/class takes up space.
So, I have this idea that std::tuple (or some variant, since it's (and the compiler's) implementation is highly implementation dependent) might be able to save the day, which it sort of does, but there are issues due to packing and alignment. Because of how compilers will align the items in the struct, having a empty next to a non-empty next to an empty next to a non-empty will be larger than 2 empties next to 2 non-empties.
Because of this, I need a way to reorder the types based on some criteria. Sorting the entire list based on size isn't necessary (and may in some cases be detrimental) so I need some generic way to reorder the tuple's type list but still access it as if the type list was in the original order.
I looked around a bit and haven't found anything like this and I'm at a loss. Ideas on how to accomplish this?
Example
struct A{};
struct B{};
// Need to be reordered based on some criteria.
std::tuple<A, int, B, float> x;
// In this case move all of the empty objects together like:
// std::tuple<A, B, int, float> x;
// but still have get<1>(x) return the `int` and get<2>(x) return `B`.
static_assert(std::is_same<decltype(get<0>()), A>::value, "0 should be type A");
static_assert(std::is_same<decltype(get<1>()), int>::value, "1 should be type int");
static_assert(std::is_same<decltype(get<2>()), B>::value, "2 should be type float");
static_assert(std::is_same<decltype(get<3>()), float>::value, "3 should be type B");
The reason this cannot be done by hand is that this could be part of a template and the elements in tuple may be empty or not, based on the parameters:
template <typename A, typename B, typename C, typename D>
class X
{
// Need to have this auto arranged given some criteria
// like size or move all of the empties together.
tuple<A, B, C, D> x;
public:
template<int i>
auto get() -> typename std::tuple_element<i, decltype(x)>
{
return get<i>(x);
}
};
// What are these types? Who knows. This could be buried in some
// template library somewhere.
X<T1, T2, T3, T4> x;
Building on what Barry did.
So from here I'd need a mapping meta-function to use the original
indices, how would I do that?
First, some helpers to facilitate index mapping. And because I'm lazy, I modified typelist slightly.
template <typename... Args>
struct typelist {
static constexpr std::size_t size = sizeof...(Args);
};
template<class T, std::size_t OldIndex, std::size_t NewIndex>
struct index_map_leaf {
using type = T;
static constexpr std::size_t old_index = OldIndex;
static constexpr std::size_t new_index = NewIndex;
};
template<class... Leaves>
struct index_map : Leaves... {};
Given a properly built index_map, converting from old index to new index is simple, leveraging template argument deduction and overload resolution:
template<std::size_t OldIndex, std::size_t NewIndex, class T>
index_map_leaf<T, OldIndex, NewIndex>
do_convert_index(index_map_leaf<T, OldIndex, NewIndex>);
template<std::size_t OldIndex, class IndexMap>
using converted_index_t = decltype(do_convert_index<OldIndex>(IndexMap()));
converted_index_t<OldIndex, IndexMap>::new_index is, well, the new index.
To build the index map, we do it in in three steps. We start by transforming the types into type-index pairs.
template<class... Ts, std::size_t... Is>
typelist<index_map_leaf<Ts, Is, 0>...>
do_build_old_indices(typelist<Ts...>, std::index_sequence<Is...>);
template<class TL>
using build_old_indices =
decltype(do_build_old_indices(TL(), std::make_index_sequence<TL::size>()));
Next, we partition the pairs. We need a metafunction that applies another metafunction to its arguments' nested typedef type rather than the arguments themselves.
// Given a metafunction, returns a metafunction that applies the metafunction to
// its arguments' nested typedef type.
template<class F>
struct project_type {
template<class... Args>
using apply = typename F::template apply<typename Args::type...>;
};
Given this, partitioning a typelist of index_map_leafs is simply partition_t<LeafList, project_type<F>>.
Finally, we transform the partitioned list, adding the new indices.
template<class... Ts, std::size_t... Is, std::size_t...Js>
typelist<index_map_leaf<Ts, Is, Js>...>
do_build_new_indices(typelist<index_map_leaf<Ts, Is, 0>...>,
std::index_sequence<Js...>);
template<class TL>
using build_new_indices =
decltype(do_build_new_indices(TL(), std::make_index_sequence<TL::size>()));
Bringing it all together,
template<class TL, class F>
using make_index_map =
apply_t<quote<index_map>, build_new_indices<partition_t<build_old_indices<TL>,
project_type<F>>>>;
With a little utility to convert the arguments of an arbitrary template to a type list:
template<template<class...> class T, class... Args>
typelist<Args...> do_as_typelist(typelist<T<Args...>>);
template<class T>
using as_typelist = decltype(do_as_typelist(typelist<T>()));
We can do the partitioning only once, by constructing the reordered tuple type directly from the index_map.
template<class Tuple, class F>
struct tuple_partitioner {
using map_type = make_index_map<as_typelist<Tuple>, F>;
using reordered_tuple_type = apply_t<project_type<quote<std::tuple>>,
as_typelist<map_type>>;
template<std::size_t OldIndex>
using new_index_for =
std::integral_constant<std::size_t,
converted_index_t<OldIndex, map_type>::new_index>;
};
For example, given
using original_tuple = std::tuple<int, double, long, float, short>;
using f = quote<std::is_integral>;
using partitioner = tuple_partitioner<original_tuple, f>;
The following assertions hold:
static_assert(partitioner::new_index_for<0>() == 0, "!");
static_assert(partitioner::new_index_for<1>() == 3, "!");
static_assert(partitioner::new_index_for<2>() == 1, "!");
static_assert(partitioner::new_index_for<3>() == 4, "!");
static_assert(partitioner::new_index_for<4>() == 2, "!");
static_assert(std::is_same<partitioner::reordered_tuple_type,
std::tuple<int, long, short, double, float>>{}, "!");
Demo.
P.S. Here's my version of filter:
template<typename A, typename F>
using filter_one = std::conditional_t<F::template apply<A>::value,
typelist<A>, typelist<>>;
template<typename F, typename... Args>
concat_t<filter_one<Args, F>...> do_filter(typelist<Args...>);
template <typename TL, typename F>
using filter_t = decltype(do_filter<F>(TL()));
First, let's start with the basics. We need a way to turn a template template (std::tuple) into a metafunction class:
template <template <typename...> class Cls>
struct quote {
template <typename... Args>
using apply = Cls<Args...>;
};
And a typelist:
template <typename... Args>
struct typelist { };
And something to go between them:
template <typename F, typename TL>
struct apply;
template <typename F, typename... Args>
struct apply<F, typelist<Args...>> {
using type = typename F::template apply<Args...>;
};
template <typename F, typename TL>
using apply_t = typename apply<F, TL>::type;
So that given some typelist, we can just have:
using my_tuple = apply_t<quote<std::tuple>, some_typelist>;
Now, we just need a partitioner on some criteria:
template <typename TL, typename F>
struct partition {
using type = concat_t<filter_t<TL, F>,
filter_t<TL, not_t<F>>
>;
};
Where concat:
template <typename... Args>
struct concat;
template <typename... Args>
using concat_t = typename concat<Args...>::type;
template <typename... A1, typename... A2, typename... Args>
struct concat<typelist<A1...>, typelist<A2...>, Args...> {
using type = concat_t<typelist<A1..., A2...>, Args...>;
};
template <typename TL>
struct concat<TL> {
using type = TL;
};
filter:
template <typename TL, typename F>
struct filter;
template <typename TL, typename F>
using filter_t = typename filter<TL, F>::type;
template <typename F>
struct filter<typelist<>, F> {
using type = typelist<>;
};
template <typename A, typename... Args, typename F>
struct filter<typelist<A, Args...>, F> {
using type = concat_t<
std::conditional_t<F::template apply<A>::value,
typelist<A>,
typelist<>>,
filter_t<typelist<Args...>, F>
>;
};
And not_:
template <typename F>
struct not_ {
template <typename Arg>
using apply = std::conditional_t<F::template apply<Args>::value,
std::false_type,
std::true_type>;
};
Which, given some_typelist of types that you want to put in your tuple becomes:
using my_tuple = apply_t<
quote<std::tuple>,
partition_t<
some_typelist,
some_criteria_metafunc_class
>>;
I'm reading through Eric Niebler's post on his tiny metaprogramming library. In trying to implement the pieces that he omits / lists as challenges, I am left with the following implementation of transform:
template <typename F, typename... As>
using meta_apply = typename F::template apply<As...>;
template <typename... >
struct typelist_transform;
// unary
template <typename... T, typename F>
struct typelist_transform<typelist<T...>, F>
{
using type = typelist<meta_apply<F,T>...>;
};
// binary
template <typename... T, typename... U, typename F>
struct typelist_transform<typelist<T...>, typelist<U...>, F>
{
using type = typelist<meta_apply<F,T,U>...>;
};
This works, but seems very unsatisfactory to me - I need a partial specialization of typelist_transform for every number of "arguments" into F. Is there a better way to implement this metafunction?
With a slightly modified interface (taking the metafunction first, rather than last, and adding a couple members to typelist to get its size and the N-th member rather than using standalone metafunctions):
template <typename... Ts>
struct typelist {
template<size_t N>
using get = std::tuple_element_t<N, std::tuple<Ts...>>;
static constexpr size_t size = sizeof...(Ts);
};
template<class, class, class...>
struct typelist_transform_helper;
template<size_t... Ints, class F, class...Lists>
struct typelist_transform_helper<std::index_sequence<Ints...>, F, Lists...>{
template <class MF, size_t N, typename... Ls>
using apply_to_Nth = meta_apply<MF, typename Ls::template get<N>...>;
using type = typelist<apply_to_Nth<F, Ints, Lists...>...>;
};
template<class F, class L1, class... Lists>
struct typelist_transform : typelist_transform_helper<std::make_index_sequence<L1::size>, F, L1, Lists...> {
// static assert on size of all lists being equal if wanted
};
Demo.
One problem with the above is that tuple_element usually requires N recursive template instantiations, making the total number of template instantiations quadratic in the length of the list. Given our use case (we access every single index), we can do better:
template<class L>
struct typelist_indexer {
template<size_t N, class T> struct helper_base { using type = T; };
template<class S, class> struct helper;
template<size_t... Ns, class... Ts>
struct helper<std::index_sequence<Ns...>, typelist<Ts...>> : helper_base<Ns, Ts>... {};
template<size_t N, class T>
static helper_base<N, T> do_get(helper_base<N, T>);
using helper_type = helper<std::make_index_sequence<L::size>, L>;
template<size_t N>
using get = typename decltype(do_get<N>(helper_type()))::type;
};
and then
template<size_t... Ints, class F, class...Lists>
struct typelist_transform_helper<std::index_sequence<Ints...>, F, Lists...>{
template <class MF, size_t N, typename... Ls>
using apply_to_Nth = meta_apply<MF, typename typelist_indexer<Ls>::template get<N>...>;
using type = typelist<apply_to_Nth<F, Ints, Lists...>...>;
};
Now the number of template instantiations is linear in the size of the list, and in my tests this result in a significant compile time improvement. Demo.