Variadic template unrolling to std::tuple - c++

I have a filter class that takes two template parameters, the number of inputs and the number of outputs.
template<int Ins, int Outs>
class Filter
{
// implementation
};
Sometimes I need to chain multiple filters in series so I was thinking to wrap them in a class
template<int... args>
class Chain
{
};
so that when I use the chain
Chain<5, 10, 25, 15> chain;
it unrolls the args into a tuple to end up with something like this in the Chain class
std::tuple<Filter<5, 10>, Fiter<10, 25>, Filter<25, 15>> filters;
Is something like this possible? I'm fairly new to these concepts and can't wrap my head around it.

We can do this in three lines and no recursion:
template<int... args>
struct Chain
{
// put args... into a constexpr array for indexing
static constexpr int my_args[] = {args...};
// undefined helper function that computes the desired type in the return type
// For Is... = 0, 1, ..., N-2, Filter<my_args[Is], my_args[Is+1]>...
// expands to Filter<my_args[0], my_args[1]>,
// Filter<my_args[1], my_args[2]>, ...,
// Filter<my_args[N-2], my_args[N-1]>
template<size_t... Is>
static std::tuple<Filter<my_args[Is], my_args[Is+1]>...>
helper(std::index_sequence<Is...>);
// and the result
using tuple_type = decltype(helper(std::make_index_sequence<sizeof...(args) - 1>()));
};
Demo.

We can do this with some recursive template magic:
//helper class template which will handle the recursion
template <int... Args>
struct FiltersFor;
//a helper to get the type of concatenating two tuples
template <typename Tuple1, typename Tuple2>
using tuple_cat_t = decltype(std::tuple_cat(std::declval<Tuple1>(),
std::declval<Tuple2>()));
//pop off two ints from the pack, recurse
template <int Ins, int Outs, int... Others>
struct FiltersFor<Ins,Outs,Others...>
{
//the type of concatenating a tuple of Filter<Ins,Outs> with the tuple from recursion
using type = tuple_cat_t<std::tuple<Filter<Ins,Outs>>,
typename FiltersFor<Outs,Others...>::type>;
};
//base case, 1 int left
template <int Dummy>
struct FiltersFor<Dummy>
{
using type = std::tuple<>;
};
//for completeness
template <>
struct FiltersFor<>
{
using type = std::tuple<>;
};
//our front-end struct
template<int... args>
using Chain = typename FiltersFor<args...>::type;
Alternatively, we can get rid of the single int and no int versions and define the primary template like this:
template <int... Args>
struct FiltersFor
{
using type = std::tuple<>;
};
Now we can test this like so:
static_assert(std::is_same<Chain<1,2,3,4>, std::tuple<Filter<1,2>,Filter<2,3>,Filter<3,4>>>::value, "wat");
static_assert(std::is_same<Chain<1,2>, std::tuple<Filter<1,2>>>::value, "wat");
static_assert(std::is_same<Chain<>, std::tuple<>>::value, "wat");
Demo

I already faced a similar problem and ended up with an operator* for class Filter tha accepts a Filter<Ins1, Ins> object and builds a Filter<Ins1, Outs>.
template<int Ins, int Outs>
class Filter
{
template <int Ins1>
Filter<Ins1, Outs> operator*(const Filter<Ins1, Ins> &rhs) const
{
// implementation
}
};
Now the question is: what your filter does? Is composition possible (maybe I am biased from the fact that in my context Filter was a function and in my case operator* was function composition)

Related

Implementation of typed tuple wrapper

How can an implementation look like, that wraps around e.g. a std::tuple as a static list of type/value, plus a type (not contained in tuple) to refer to some kind of owner/visitor.
I want to instantiate like
constexpr auto data = data_of<a>(1, 2.0);
Background
The idea is to use a data_of<T>(...) kind of structure to to pass a list of type/data pairs into a bulk function like this.
template <typename... _Ts>
static constexpr void do_bulk_stuff(_Ts&&... _Vs)
{
// crazy stuff happens here
}
// call it like
do_bulk_stuff
(
data_of<a>(1, 2.0),
data_of<b>(3),
data_of<c>(4.0, 5, 6),
// ...
);
Attempt 1
So far I ended up with a naive (not working) implementation attempt like
template <typename T, typename... Ts>
struct data_of {
using type = T;
using data_t = std::tuple<Ts...>;
data_t data;
constexpr data_of(Ts&&... Vs)
: data(Vs...)
{}
};
Goal
My goal is to achieve a result like this data_of<a> instance pseudo code example
{
// meta
type = a;
data_t = std::tuple<int,double>;
// runtime
data = [1, 2.0];
}
The issue inherent to your attempt is that class template argument deduction is simply not like its function counterpart. There will be no deduction if any of the class template's arguments is explicitly specified. You fail because the trailing pack is always specified (by omission) as empty.
The solution is to shift the burden onto the mechanism that allows you to specify only part of the arguments: function templates.
template <typename T, typename... Ts>
struct data_of_t {
using type = T;
using data_t = std::tuple<Ts...>;
data_t data;
constexpr data_of_t(Ts&&... vs)
: data(std::forward<Ts>(vs)...)
{}
};
template<typename T, typename... Ts>
constexpr auto data_of(Ts&&... vs) {
return data_of_t<T, Ts...>(std::forward<Ts>(vs)...);
}
Now the type of the expression data_of<a>(1, 2.0) has the same "meta" properties you are after.

How can I select a subset of tuple types and create another tuple from contents of the subset?

I have a tuple of structs that are defined using variadic arguments:
#include <tuple>
#include <vector>
template<class Type>
struct Pool {
std::vector<Type> components;
};
template<class... Types>
class Storage {
public:
std::tuple<Pool<Types>...> pools;
template<class SelectedType0, class... SelectedTypes>
std::tuple<std::vector<SelectedTypes>&...> getVectorsFromTuple();
template<class... SelectedTypes>
void iterate();
};
I want a way to select subset of types from the tuple and create a new tuple that is based on contents of it. Essentially, this is what I am trying to achieve:
template<class... Types>
template<class... SelectedTypes>
void Storage<Types...>::iterate() {
std::tuple<std::vector<SelectedTypes>&...> vectors = getVectorsFromTuple<SelectedTypes...>(pools);
// getVectorsFromTuple should get the values in the tuple based on types
// and save reference to each tuple item's structure's `components`
// property in another tuple
}
One idea that I got for it was to "define" first template parameter in another function and then use that function to recursively define other parameters:
template <class... Types>
template<class SelectedType0, class... SelectedTypes>
inline std::tuple<std::vector<SelectedType0>&, std::vector<SelectedTypes>&...> EntityStorageSparseSet<ComponentTypes...>::getVectorsFromPools() {
return std::make_tuple(std::get<PickComponent0>(pools).components, getVectorsFromPools<PickComponents...>());
}
But I don't know how to unroll/flatten the recursed tuples so this function returns something like:
std::tuple<SelectedType0, SelectedType1, ...>
instead of
std::tuple<SelectedType0, std::tuple<SelectedType1, std::tuple<SelectedType2, ...>>>
How can I unroll the tuples or is there a better way to implement what I am trying to achieve?
Following seems to do the job (assuming uniqueness of type in Types):
template <typename... Types>
template <typename... SelectedTypes>
std::tuple<std::vector<SelectedTypes>&...>
EntityStorageSparseSet<Types...>::getVectorsFromPools()
{
return { std::get<SelectedTypes>(pools).components... };
}

Is it possible to have a "generic" template parameter in C++, that can be either a non-type template parameter or a type?

In C++, it's possible to use a type as a template parameter, e.g:
template <typename T>
void MyFn();
It's also possible to use a non-type as a template parameter in some cases, e.g.:
template <int64_t T>
void MyFn2();
My question is whether it's possible to have a "generic" template parameter that can be both? Like:
template <TypenameOrint64_t T>
void MyFn3();
such that both MyFn3<42> and MyFn3<double> would be acceptable.
An example of how I might use this:
template <typename ValType, ValType Head, ValType ...Tail>
struct ListS{
template <typename OutType, template <ValType ArgType> class Fn>
using MapHead = ListS<OutType, Fn<Head>::val, Tail...>;
};
template<int64_t N>
struct SquareS{
static constexpr const int64_t val = N * N;
};
using Sqrd = ListS<int64_t, 3, 4>::MapHead<int64_t, SquareS>;
static_assert(std::is_same<Sqrd, ListS<int64_t, 9, 4>>::value, "Values don't match");
The above is a very rough sketch of a compile-time list of values along with a single compile-time "function" on it. Would it be possible to make something like that also support lists of types, not just lists of non-type template param compatible values, without just duplicating all the code?
Is it possible to have a “generic” template parameter in C++, that can be either a non-type template parameter or a type?
Short answer: no.
Long answer.
No. The best I can imagine to mix types and values is wrap values in types, using std::integral_constant, by example.
So, your desired code, could be written (C++17) almost as follows
#include <utility>
template <typename ...>
struct ListS;
template <typename ValType, ValType Head, ValType ...Tail>
struct ListS<std::integral_constant<ValType, Head>,
std::integral_constant<ValType, Tail>...>
{
template <template <auto> class Fn, typename OutType = ValType>
using MapHead = ListS<std::integral_constant<OutType, Fn<Head>::value>,
std::integral_constant<OutType, Tail>...>;
};
template <auto N>
struct SquareS : public std::integral_constant<decltype(N), N*N>
{ };
int main ()
{
using T1 = ListS<std::integral_constant<long long, 3ll>,
std::integral_constant<long long, 4ll>>;
using T2 = T1::MapHead<SquareS>;
using T3 = ListS<std::integral_constant<long long, 9ll>,
std::integral_constant<long long, 4ll>>;
static_assert( std::is_same_v<T2, T3> );
}
Pre C++17 you can't use auto for the type of the template values so you should make some simple corrections.
You could use function overloading and the auto type deduction that came with C++17
to accomplish something similar.
template<typename myType>
auto myFn3(myType value){
return value;
}
template<auto value> //takes any non-type parameter
auto myFn3(){
return value;
}
int main(){
auto test1_normal = myFn3(3);
auto test1_cast = myFn3<double>(3); //able to perform a cast
auto test1_auto = myFn3<3>();
return 0;
}

How can I get access to this nested template parameter?

Here is my code:
template<
template <typename TSEvent,
typename ...TSEvents> typename V,
typename... Filtered>
constexpr auto filter() {
if constexpr(sizeof...(TSEvents) == 0) {
return type_list<Filtered...>{};
}
if constexpr(is_default_constructible<TSEvent>::value) {
return filter<<TSEvents...>, Filtered...>();
}
return filter<<TSEvents...>, Filtered...>();
}
I however get this error, size...(TSEvents), TSEvents is not declared. Is there anyway for me to access TSEvents in my nested template?
Usually through another level of indirection, and usually a struct that we can specialize.
For example:
namespace detail
{
template<class...>
struct filter_t;
template<template<class, class...> class V, class TSEvent, class... TSEvents, class... Filtered>
struct filter_t<V<TSEvent,TSEvents...>, Filtered...>
{
static constexpr auto filter() {
return sizeof...(TSEvents);
}
};
} // detail
template<class... T>
constexpr auto filter()
{
return detail::filter_t<T...>::filter();
}
template<class T, class...U>
struct type_list{};
int main()
{
std::cout << filter<type_list<int, int, int>, int>();
}
Live Demo
Just to present another option, you could do this with only functions.
#include <iostream>
using namespace std;
template<typename...>
struct type_list{};
template < template <typename...> typename T,typename A,typename... B, typename... Filtered>
constexpr auto filter_impl(T<A,B...>*,type_list<Filtered...>)
{
using filtered_list = std::conditional_t<is_arithmetic<A>::value,
type_list<Filtered...,A>,
type_list<Filtered...>>;
if constexpr (sizeof...(B) == 0)
return filtered_list();
else
return filter_impl( (T<B...>*)0, filtered_list());
}
template <typename T>
constexpr auto filter()
{
return filter_impl( (T*)0,type_list<>());
}
struct not_arethmetic{};
int main() {
auto b = filter< type_list<not_arethmetic,int,bool,not_arethmetic,double> >();
static_assert(std::is_same< decltype(b) , type_list<int,bool,double>>::value);
return 0;
}
Demo
One thing, In your original example your first if expression will mean that the final TSEvent is not checked, as it returns if the varadic TSEvents... is zero size, but there will be one final element to check whether is_default_constructible.
Also, you might find this post useful regarding template template parameter names.
I however get this error, size...(TSEvents), TSEvents is not declared. Is there anyway for me to access TSEvents in my nested template?
Short answer: no.
Long answer: with
template<
template <typename TSEvent,
typename ...TSEvents> typename V,
typename... Filtered>
constexpr auto filter()
you set two template arguments for the filter() function.
The first one, related to the TSEvents variadic list, is a template-template argument that receive one or more types argument.
But your function doesn't receive a type that is based over that template-template (with a fixed TSEvent type and a fixed TSEvents); receive the template-template.
So doesn't make sense the test size...(TSEvents) because, for filter() isn't
fixed the TSEvents list.
To explain this in another way... you can call filter this way
filter<std::tuple, short, int, long>();
Ask for sizeof...(TSEvents) is asking how many types contains std::tuple where std::tuple is only the container of types but without contained types.
If you want to make some sort of actions in your filter() function, you need a type template parameter, not a template-template parameter.
It's simpler with classes (see AndyG's answer) where you can use partial specialization (with functions you can't) or with function when they receive arguments from which you can deduce types.
Suppose your filter() receive an object of type V<SomeTypes...> and an object of type std::tuple<Filtered...>, you can write something as follows (caution: code not tested)
template<
template <typename ...> typename V,
typename TSEvent, typename ... TSEvents, typename... Filtered>
constexpr auto filter (V<TSEvent, TSEvents...> const & v,
std::tuple<Filtered...> const & t) {
/* some code where you can use also TSEvent and TSEvents... */
}
This way TSEvent and TSEvents... are deduced from the v argument.

What is the most compact way to extract the template arguments of a class and iterate over them?

In the little program below, I show the solution I currently use to extract the template arguments of a class and iterate over it via a recursive helper function.
I wonder if there is a more concise way to do it, as I explain in the pseudo-code in the comments below.
template <int...Is> struct Pack {};
template <int I> struct B
{
static void foo() { std::cout << I << "\n"; }
};
// recursive helper function, also used to extract the parameter pack arguments
template <int I, int...Is>
void foo_helper( Pack<I, Is...>&& )
{
B<I>::foo();
foo_helper( Pack<Is...>{} );
}
// terminate recursion
void foo_helper( Pack<>&& ) {}
struct A
{
typedef Pack<1,3,5> ints;
static void foo()
{
// this is what I do
foo_helper(ints{});
// this is what I would like to do, ideally in one single line
// 1) extract the template arguments pack from ints, without creating an helper function for that
// 2) iterate on the template arguments of the pack without a recursive helper
// In pseudocode, something like:
// (B<IterateOver<ArgumentsOf<ints>>>::foo());
}
};
int main()
{
A::foo();
}
If you want to do metaprogramming, start working in types. If you want non-type template parameters, move them over to types asap.
Below, I first take Pack<1,2,3> and convert it to types< std::integral_constant<int, 1>, std::integral_constant<int, 2>, std::integral_constant<int, 3> >. This is a list of types that is in obvious correspondence to your pack of ints.
Then, I introduce a tag type template. This is a type which "carries" another type, but it itself is stateless. You can extract the type from an value of an instance of the template as a bonus.
Third, I write a "for each type" function that takes a lambda and a pack of types, and proceeds to call the lambda once for each of the types, passing in a tag type.
In the body of the lambda, we can extract the passed type by using decltype on the tag variable (or a helper macro).
We chain those together, and from the passed tag type we can extract the integer in the original pack.
The result is you can inject this into your code:
for_each_type( [&](auto tag){
constexpr int i = TAG_TYPE(tag){};
// use i
}, ints_as_types_t<ints>{} );
in the middle of your method, and work on the ints "inline".
If we wanted to only solve your specific problem, we'd do a bit less boilerplate, but I like the genericness.
template<class...>struct types{using type=types;};
template <int...Is> struct Pack {};
template<class pack> struct ints_as_types;
template<class pack>
using ints_as_types_t=typename ints_as_types<pack>::type;
template<class T, template<T...>class pack, T...ts>
struct ints_as_types<pack<ts...>> {
using type=types<std::integral_constant<T,ts>...>;
};
now we can do:
using pack = ints_as_types_t<Pack<1,2,3>>;
and pack is a list of types, not a list of integers.
Now some hana-style metaprogramming: (metaprogramming with values instead of pure types)
template<class T>struct tag_t{using type=T; constexpr tag_t(){};};
template<class T>constexpr tag_t<T> tag={};
template<class Tag>using type_t=typename Tag::type;
#define TAG_TYPE(...) type_t<std::decay_t<decltype(__VA_ARGS__)>>;
template<class F, class...Ts>
void for_each_type(F&& f, types<Ts...>) {
using discard=int[];
(void)discard{ 0, ((
f(tag<Ts>)
),void(),0)...};
}
which lets you iterate over a collection of types.
for_each_type( [&](auto tag){
constexpr int i = TAG_TYPE(tag){};
// use i
}, ints_as_types_t<ints>{} );
gives you a lambda that has a constexpr int i for each of the types in your list.
A bunch of the above work lifts your list of ints into a list of types, because working with only types makes metaprogramming less special-case. You can skip that lifting, and write a for_each_integer that takes a Pack<int...> directly with less code, but it seems less useful to me.
You could add a foo_for_each function to Pack:
template <int...Is> struct Pack {
template <template <int> class T>
static void foo_for_each () {
std::initializer_list<int> { (T<Is>::foo(),0)... } ;
}
};
Then you would just write:
ints::foo_for_each<B>();
This will call B<N>::foo for each N in the pack.
As suggested by Yakk, you could pass in a lambda which gets a tag type as an argument to create a generic Pack::for_each:
template <typename T> struct tag { using type = T; };
template <typename T> using type_t = typename T::type;
template <int...Is> struct Pack {
template <template <int> class T, typename Func>
static void for_each (Func&& func) {
std::initializer_list<int> {
((std::forward<Func>(func)(tag<T<Is>>{})) 0)...
} ;
}
};
Then you could call like this:
auto call_foo = [](auto tag) { type_t<decltype(tag)>::foo(); };
ints::for_each<B>(call_foo);
This is the shortest I can come up with:
#include <iostream>
template<int... Is>
struct Pack;
template <int I> struct B
{
static void foo() { std::cout << I << "\n"; }
};
template<typename PACK> struct unpack;
template<int...Is>
struct unpack<Pack<Is...>>
{
template<template<int> class T>
static void call()
{
using swallow = int[sizeof...(Is)];
(void) swallow{(T<Is>::foo(), 0)...};
}
};
struct A
{
typedef Pack<1,3,5> ints;
static void foo()
{
unpack<ints>::call<B>();
}
};
int main()
{
A::foo();
}
If you want to have the variadic pack for runtime-iteration you could attach a std::arrayto your struct Pack as:
template <int...Is> struct Pack {
std::array<int, sizeof...(Is)> arr = {{Is...}};
};
And then iterate through as:
static void foo() {
for(auto && i : ints{}.arr) std::cout << i << " ";
}
Live Demo
What you wrote here is just plain weird, where did you even find an implementation this
rigid ?
You NEED a helper function, it's just a fact, you could probably work around it
somehow, but I do not see the point of that.
The only solution for that, right now, is to use Clang 3.6, they already implemented
the new syntax, that allows you to write something like this.
// I am pretty sure, this was the syntax, it's called a fold expression
// you can read more about it here:
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4295.html
template<typename ... Type>
auto sum(Type ... argument)
{
return (... + argument);
}
In any other compiler, the way to go about it, is to write two simple functions
template<typename Tail>
auto sum(Tail tail)
{
return tail;
}
template<typename Head, typename ... Tail>
auto sum(Head head, Tail ... tail)
{
return head + sum(tail);
}
This accepts anything that supports + so strings, ints, doubles will work, probably
some more, but you get the gist of it.
Your example would look like this
template<typename Tail>
void print(Tail tail)
{
cout << tail << endl;
}
template<typename Head, typename ... Tail>
void print(Head head, Tail ... tail)
{
cout << head;
print(tail...);
}
Usage:
print(1, 3.14, "something", string{"yeye"}, 52);
or
sum(1, 512, 55, 91);
There are some other ways to use variadic templates, like this guy here describes,
there is way too much of it, for me to put it here, so I'll just link:
http://florianjw.de/en/variadic_templates.html
Iterating over the arguments of a template is a bit harder, because you have to use
some real compiler magic and index_sequence.
I have an example lying around here somewhere, because I have been messing around
with it lately.
template<typename InputTuple, std::size_t ... N>
void tupleIteratorImpl(InputTuple& input, std::index_sequence<N...>)
{
// DO WHATEVER YOU WANT HERE, but the structure is
FUNCTION(/* pass all of the template parameters as arguments */, std::get<N>(input)...);
// and FUNCTION has to have the structure of the examples from point 1.
// but with this, you can already do pretty much anything you imagine
// even at compile time
}
template<typename InputTuple, typename Indices = std::make_index_sequence<std::tuple_size<InputTuple>::value>>
void tupleIterator(InputTuple& input)
{
tupleIteratorImpl(input, Indices());
}
A function for this is already included in c++17, and it is called apply, here's the documentation:
http://en.cppreference.com/w/cpp/experimental/apply with some sample code even.
Hope this answers some of your questions.