I have come across the need to reorder a variadic list of parameters that is supplied to the constructor of a struct. After being reordered based on their types, the parameters will be stored as a tuple. My question is how this can be done so that a modern C++ compiler (e.g. g++-4.7) will not generate unnecessary load or store instructions. That is, when the constructor is invoked with a list of parameters of variable size, it efficiently pushes each parameter into place based on an ordering over the parameters' types.
Here is a concrete example. Assume that the base type of every parameter (without references, rvalue references, pointers, or qualifiers) is either char, int, or float. How can I make it so that all the parameters of base type char appear first, followed by all of those of base type int (which leaves the parameters of base type float last). The relative order in which the parameters were given should not be violated within sublists of homogeneous base type.
Example: foo::foo() is called with arguments float a, char&& b, const float& c, int&& d, char e. The tuple tupe is std::tuple<char, char, int, float, float>, and it is constructed like so: tuple_type{std::move(b), e, std::move(d), a, c}.
Consider the struct defined below, and assume that the metafunction deduce_reordered_tuple_type is already implemented. How would you write the constructor so that it works as intended? If you think that the code for deduce_reodered_tuple_type, would be useful to you, I can provide it; it's a little long.
template <class... Args> struct foo
{
// Assume that the metafunction deduce_reordered_tuple_type is defined.
typedef typename deduce_reordered_tuple_type<Args...>::type tuple_type;
tuple_type t_;
foo(Args&&... args) : t_{reorder_and_forward_parameters<Args>(args)...} {}
};
Edit 1
The technique I describe above does have applications in mathematical frameworks that make heavy use of expression templates, variadic templates, and metaprogramming in order to perform aggressive inlining. Suppose that you wish to define an operator that takes the product of several expressions, each of which may be passed by reference, reference to const, or rvalue reference. (In my case, the expressions are conditional probability tables and the operation is the factor product, but something like matrix multiplication works suitably as well.)
You need access to the data provided by each expression in order to evaluate the product. Consequently, you must move the expressions passed as rvalue references, copy the expressions passed by reference to const, and take the addresses of expressions passed by reference. Using the technique I describe above now poses several benefits.
Other expressions can use uniform syntax to access data elements from this expression, since all of the heavy-lifting metaprogramming work is done beforehand, within the class.
We can save stack space by grouping the pointers together and storing the larger expressions towards the end of the tuple.
Implementing certain types of queries becomes much easier (e.g. check whether any of the pointers stored in the tuple aliases a given pointer).
Thank you very much for your help!
Happy 4th of July everyone! Ok, here you go.
It turns out that g++-4.7 is pretty awesome at iniling and creates identical machine code according to my testing (instructions to reproduce results are below the code).
#include <iostream>
#include <tuple>
#include <typeinfo>
#include <type_traits>
template <class... Args>
struct sequence
{
typedef std::tuple<Args...> tuple_type;
};
template <class U, class V>
struct sequence_cat;
template <class... U, class... V>
struct sequence_cat<sequence<U...>, sequence<V...>>
{
typedef sequence<U..., V...> type;
};
template <class... V>
struct sequence_cat<void, sequence<V...>>
{
typedef sequence<V...> type;
};
template <class... U>
struct sequence_cat<sequence<U...>, void>
{
typedef sequence<U...> type;
};
template <>
struct sequence_cat<void, void>
{
typedef void type;
};
template <class T>
struct undecorate
{
typedef typename std::remove_reference<T>::type noref_type;
typedef typename std::remove_pointer<noref_type>::type noptr_type;
typedef typename std::remove_cv<noptr_type>::type type;
};
template <class T>
struct deduce_storage_type
{
typedef T type;
};
template <class T>
struct deduce_storage_type<T&>
{
typedef T* type;
};
template <class T>
struct deduce_storage_type<const T&>
{
typedef T type;
};
template <class T>
struct deduce_storage_type<T&&>
{
typedef T type;
};
template <class T, class... Args>
struct filter_type;
template <class T, class Arg, class... Args>
struct filter_type<T, Arg, Args...>
{
static constexpr bool pred =
std::is_same<typename undecorate<Arg>::type, T>::value;
typedef typename deduce_storage_type<Arg>::type storage_type;
typedef typename
std::conditional<
pred,
typename sequence_cat<
sequence<storage_type>,
typename filter_type<T, Args...>::type
>::type,
typename filter_type<T, Args...>::type
>::type type;
};
template <class T, class Arg>
struct filter_type<T, Arg>
{
static constexpr bool pred =
std::is_same<typename undecorate<Arg>::type, T>::value;
typedef typename deduce_storage_type<Arg>::type storage_type;
typedef typename
std::conditional<pred, sequence<storage_type>, void>::type
type;
};
template <class... Args>
struct deduce_sequence_type
{
typedef typename filter_type<char, Args...>::type char_sequence;
typedef typename filter_type<int, Args...>::type int_sequence;
typedef typename filter_type<float, Args...>::type float_sequence;
typedef typename
sequence_cat<
char_sequence,
typename sequence_cat<
int_sequence,
float_sequence
>::type
>::type type;
};
template <class T>
struct get_storage_type
{
static T apply(T t) { return t; }
};
template <class T>
struct get_storage_type<T&>
{
static T* apply(T& t) { return &t; }
};
template <class T>
struct get_storage_type<const T&>
{
static T apply(const T& t) { return t; }
};
template <class T>
struct get_storage_type<T&&>
{
static T&& apply(T&& t) { return std::move(t); }
};
template <class T, class Arg>
struct equals_undecorated_type
{
static constexpr bool value =
std::is_same<typename undecorate<Arg>::type, T>::value;
};
template <bool Pred, bool IsNextVoid, class T, class... Args>
struct filter_parameter_impl;
template <class T, class Arg1, class Arg2, class... Args>
struct filter_parameter_impl<false, false, T, Arg1, Arg2, Args...>
{
typedef typename filter_type<T, Arg2, Args...>::type sequence_type;
typedef typename sequence_type::tuple_type tuple_type;
static constexpr bool pred = equals_undecorated_type<T, Arg2>::value;
static constexpr bool is_next_next_void =
std::is_same<typename filter_type<T, Args...>::type, void>::value;
static tuple_type apply(Arg1&&, Arg2&& arg2, Args&&... args)
{
return filter_parameter_impl<
pred, is_next_next_void, T, Arg2, Args...
>::apply(
std::forward<Arg2>(arg2),
std::forward<Args>(args)...
);
}
};
template <class T, class Arg1, class Arg2, class... Args>
struct filter_parameter_impl<false, true, T, Arg1, Arg2, Args...>
{
static void apply(Arg1&&, Arg2&&, Args&&...) {}
};
template <class T, class Arg1, class Arg2, class... Args>
struct filter_parameter_impl<true, false, T, Arg1, Arg2, Args...>
{
typedef typename
filter_type<T, Arg1, Arg2, Args...>::type
sequence_type;
typedef typename sequence_type::tuple_type tuple_type;
static constexpr bool pred = equals_undecorated_type<T, Arg2>::value;
static constexpr bool is_next_next_void =
std::is_same<typename filter_type<T, Args...>::type, void>::value;
static tuple_type apply(Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
return std::tuple_cat(
std::make_tuple(get_storage_type<Arg1>::apply(arg1)),
filter_parameter_impl<
pred, is_next_next_void, T, Arg2, Args...
>::apply(
std::forward<Arg2>(arg2),
std::forward<Args>(args)...
)
);
}
};
template <class T, class Arg1, class Arg2, class... Args>
struct filter_parameter_impl<true, true, T, Arg1, Arg2, Args...>
{
typedef typename filter_type<T, Arg1>::type sequence_type;
typedef typename sequence_type::tuple_type tuple_type;
static tuple_type apply(Arg1&& arg1, Arg2&&, Args&&...)
{
return std::make_tuple(get_storage_type<Arg1>::apply(
std::forward<Arg1>(arg1)
));
}
};
template <class T, class Arg1, class Arg2>
struct filter_parameter_impl<false, false, T, Arg1, Arg2>
{
typedef typename filter_type<T, Arg2>::type sequence_type;
typedef typename sequence_type::tuple_type tuple_type;
static tuple_type apply(Arg1&&, Arg2&& arg2)
{
return std::make_tuple(get_storage_type<Arg2>::apply(
std::forward<Arg2>(arg2)
));
}
};
template <class T, class Arg1, class Arg2>
struct filter_parameter_impl<false, true, T, Arg1, Arg2>
{
static void apply(Arg1&&, Arg2&&) {}
};
template <class T, class Arg1, class Arg2>
struct filter_parameter_impl<true, false, T, Arg1, Arg2>
{
typedef typename filter_type<T, Arg1>::type sequence_type;
typedef typename sequence_type::tuple_type tuple_type;
static tuple_type apply(Arg1&& arg1, Arg2&& arg2)
{
return std::make_tuple(
get_storage_type<Arg1>::apply(std::forward<Arg1>(arg1)),
get_storage_type<Arg2>::apply(std::forward<Arg2>(arg2))
);
}
};
template <class T, class Arg1, class Arg2>
struct filter_parameter_impl<true, true, T, Arg1, Arg2>
{
typedef typename filter_type<T, Arg1, Arg2>::type sequence_type;
typedef typename sequence_type::tuple_type tuple_type;
static tuple_type apply(Arg1&& arg1, Arg2&&)
{
return std::make_tuple(
get_storage_type<Arg1>::apply(std::forward<Arg1>(arg1))
);
}
};
template <class T, class... Args>
struct filter_parameter;
template <class T, class Arg, class... Args>
struct filter_parameter<T, Arg, Args...>
{
typedef typename filter_type<T, Arg, Args...>::type sequence_type;
typedef typename std::conditional<
std::is_same<sequence_type, void>::value,
void,
typename sequence_type::tuple_type
>::type tuple_type;
static constexpr bool pred = equals_undecorated_type<T, Arg>::value;
static constexpr bool is_next_void =
std::is_same<typename filter_type<T, Args...>::type, void>::value;
static tuple_type apply(Arg&& arg, Args&&... args)
{
return filter_parameter_impl<
pred, is_next_void, T, Arg, Args...
>::apply(std::forward<Arg>(arg), std::forward<Args>(args)...);
}
};
template <bool Is1Void, bool Is2Void, bool Is3Void, class... Args>
struct get_tuple_impl;
template <class... Args>
struct get_tuple_impl<false, false, false, Args...>
{
typedef typename deduce_sequence_type<Args...>::type sequence_type;
typedef typename sequence_type::tuple_type tuple_type;
static tuple_type apply(Args&&... args)
{
return std::tuple_cat(
filter_parameter<char, Args...>::apply(
std::forward<Args>(args)...
),
filter_parameter<int, Args...>::apply(
std::forward<Args>(args)...
),
filter_parameter<float, Args...>::apply(
std::forward<Args>(args)...
)
);
}
};
template <class... Args>
struct get_tuple
{
typedef typename deduce_sequence_type<Args...>::type sequence_type;
typedef typename std::conditional<
std::is_same<sequence_type, void>::value,
void,
typename sequence_type::tuple_type
>::type tuple_type;
static constexpr bool is1void =
std::is_same<typename filter_type<char, Args...>::type, void>::value;
static constexpr bool is2void =
std::is_same<typename filter_type<int, Args...>::type, void>::value;
static constexpr bool is3void =
std::is_same<typename filter_type<float, Args...>::type, void>::value;
static tuple_type apply(Args&&... args)
{
return get_tuple_impl<is1void, is2void, is3void, Args...>::
apply(std::forward<Args>(args)...);
}
};
template <class... Args>
struct foo
{
typedef typename deduce_sequence_type<Args...>::type sequence_type;
typedef typename sequence_type::tuple_type tuple_type;
tuple_type t_;
foo(Args&&... args) : t_{} {}
};
int main()
{
char a = 5;
const int b = 6;
float c = 7;
int d = 8;
float e = 9;
char f = 10;
auto x = get_tuple<char&, const int&, float&, int&, float&&, char&>::
apply(a, b, c, d, std::move(e), f);
//std::tuple<char*, char*, int, int*, float*, float> x{&a, &f, b, &d, &c, std::move(f)};
std::cout << typeid(x).name() << std::endl;
return 0;
}
In order to reproduce the results, do the following (assuming you have g++-4.7 installed).
g++ -std=c++11 -Wall -Ofast -march=native variadic_reorder.cpp -S -o with_templates.s
// Comment out the line in main, and comment the line containing auto x, as well as the line below it.
g++ -std=c++11 -Wall -Ofast -march=native variadic_reorder.cpp -S -o without_templates.s
vimdiff with_templates.s without_templates.s
The only differences I noticed were things like the names of labels; otherwise, the generated machine code was identical.
Edit 1
I'm going to accept my own answer until someone else posts something more elegant than what I have.
I don't have time to experiment with this, but if your compiler is generating too many moves when permuting forwarded arguments, I would recommend forwarding through std::forward_as_tuple. The tuple is a data structure with a concrete layout, and constructing it encourages the compiler to put things into memory all at once, in the order you want.
On the other hand, it is less likely to promote arguments to registers and bypass the stack for simple functions. And nothing is really guaranteed as long as the tuple is only used as a pure value (no reference is taken), because under the as-if rule there is no need for its members to have any addresses.
We can save stack space by grouping the pointers together and storing the larger expressions towards the end of the tuple.
Lvalue references are implemented as pointers by the ABI yet you specified to group them as data values. Rvalue references should be treated the same as lvalue references if you want to adhere to the native passing semantics. (I assume you will only be moving class types.) So the sorting problem is slightly more complicated than stated, because you want to sort the parameters into value and pointer categories, then sort those by base type.
As for the sorting algorithm itself, I would try just popping from the input pack and pushing to a set of output tuples, queue-style, and then catenating the output tuples with std::tuple_cat. That will be the simplest to implement, stable, and should hit the compiler's common-case optimizations. Don't implement an algorithm intended to run in-place in memory because TMP doesn't work like that.
As for translating the results of sorting into the function that permutes the parameters into the arguments to forward_as_tuple, I'm not so sure. You'll probably have to deal with indexes.
You want to be very sure the benefit is worth it before committing to all this.
Related
I have a typelist of the form described here:
http://www.drdobbs.com/generic-programmingtypelists-and-applica/184403813
Each type has a duck-typed function (template/compile-time virtual) called get() which returns a simple type like this:
struct Float {
float get() { return 7.0; }
};
struct Int {
int get() { return 7; }
};
typedef typelist<Float, typelist<Int, null_typelist>> types;
I also have a function that takes a variadic number of simple type arguments, like this:
template<typename... Args>
foo(Args... args)
{
}
Now I need a way to call foo given types. I think there's a solution to this, via a tuple, but I'm really far from a solution that works...
I hope you can help me here!
This code converts typelist to tuple and calls foo with simple types.
#include <tuple>
#include <iostream>
template<typename H, typename T>
struct typelist
{
typedef H Head;
typedef T Tail;
};
struct null_typelist {};
template<int... Indices>
struct indices {
using next = indices<Indices..., sizeof...(Indices)>;
};
template<int Size>
struct build_indices {
using type = typename build_indices<Size - 1>::type::next;
};
template<>
struct build_indices<0> {
using type = indices<>;
};
template<typename T>
using Bare = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
template<typename Tuple>
constexpr
typename build_indices<std::tuple_size<Bare<Tuple>>::value>::type
make_indices()
{ return {}; }
template<typename T, typename... Args>
struct tuple_push;
template<typename T, typename... Args>
struct tuple_push<T, std::tuple<Args...>>
{
typedef std::tuple<Args..., T> type;
};
template<typename TL>
struct typelist_to_tuple;
template<typename H, typename T>
struct typelist_to_tuple<typelist<H, T>>
{
typedef typename tuple_push<H, typename typelist_to_tuple<T>::type>::type type;
};
template<typename H>
struct typelist_to_tuple<typelist<H, null_typelist>>
{
typedef std::tuple<H> type;
};
struct Float {
float get() const { return 7.5; }
};
struct Int {
int get() const { return 7; }
};
template<typename... Args>
void foo(const Args&... args)
{
}
template<typename T, typename... Args>
void foo(const T& current, const Args&... args)
{
std::cout << current << std::endl;
foo(args...);
}
template<typename Tuple, int... Indices>
void apply(const Tuple& tuple, indices<Indices...>)
{
foo(std::get<Indices>(tuple).get()...);
}
template<typename Tuple>
void apply(const Tuple& tuple)
{
apply(tuple, make_indices<Tuple>());
}
int main()
{
typedef typelist<Int, typelist<Float, typelist<Int, null_typelist>>> list;
typedef typelist_to_tuple<list>::type tuple;
tuple t = std::make_tuple(Int(), Float(), Int());
apply(t);
}
live example
I think this can be done without using std::tuple. As others have mentioned, C++11 and onwards support typelists based on parameter packs, which are much easier to work with than the C++03 recursive typelists:
template <typename...>
struct tlist {};
Assuming the C++03 typelists are part of an API that you can't change, the first thing is to convert the C++03 typelist to a C++11 typelist. That can be done with some empty function declarations acting as type functions:
// Concatenate two tlist types
template <typename... Ts, typename... Us>
static auto concat_impl(tlist<Ts...>, tlist<Us...>) -> tlist<Ts..., Us...>;
// Base case
static auto to_tlist_impl(null_typelist) -> tlist<>;
// Recursive case
template <typename Head, typename Tail>
static auto to_tlist_impl(typelist<Head, Tail>)
-> decltype(concat_impl(tlist<Head>{}, to_tlist_impl(Tail{})));
// Alias template to make usage easier
template <typename Typelist>
using to_tlist = decltype(to_tlist_impl(Typelist{}));
Next, provide a helper function to take the parameter pack out of a tlist and pass it to the foo function. The original question did not specify how the types should be constructed, so here they are just default constructed as they are passed in to foo.
template <typename... Ts>
void call_foo(tlist<Ts...>) {
foo(Ts{}...);
}
Finally, to call foo with a recursive typelist type just convert it to a tlist with the to_tlist type function:
using list = typelist<Int, typelist<Float, typelist<String, null_typelist>>>;
call_foo(to_tlist<list>{});
Godbolt example: https://godbolt.org/z/j8P55v7Kb
I'm searching for a way to remove (let's say for now all occurences of) a type from a template parameter pack. The end result would be a struct that looked like this :
template<typename T, typename...Ts>
struct RemoveT
{
using type = /* a new type out of Ts that does not contain T */
}
Let's say that the marginal case RemoveT<int, int> would be handled by returning void (not handled in the code that follows). My initial design looks like this:
// --------------------------------------------------------------
// 1. A "way" of typedefing variadic number of types ------------
template<typename...Ts>
struct pack {
using type = Ts;
};
// --------------------------------------------------------------
// --------------------------------------------------------------
template<typename T, typename...Ts> struct RemoveT;
template<typename T, typename T1, typename...Ts>
struct RemoveT {
using type = typename pack<T1, typename RemoveT<T, Ts...>::type>::type;
};
template<typename T, typename T1>
struct RemoveT<T, T1> {
using type = T1;
};
template<typename T, typename...Ts>
struct RemoveT<T, T, Ts...> {
using type = typename RemoveT<Ts...>::type;
};
// --------------------------------------------------------------
Now I can't even begin to test this code because the pack structure is not valid C++
Reiteration
Just in case this is helpfull for an answer, some other thoughs on solving it
One could argue that pack is not even useful at all. We could instead move around the RemoveT structure, creating a new RemoveT that only contains the types needed. The problem then transforms in extracting the types out of the struct
We could create type pairs that mimic the behaviour of typelists and take a more recursive approach on this.
Bottom Line
For variadic types Ts and a type T: Can I create Us out of Ts ommiting T ?
The following provides a non-recursive and direct way to remove T from Ts... and, like Jarod42's solutions, yields a std::tuple<Us...> but without the need to use typename ...::type:
#include <tuple>
#include <type_traits>
template<typename...Ts>
using tuple_cat_t = decltype(std::tuple_cat(std::declval<Ts>()...));
template<typename T, typename...Ts>
using remove_t = tuple_cat_t<
typename std::conditional<
std::is_same<T, Ts>::value,
std::tuple<>,
std::tuple<Ts>
>::type...
>;
int main()
{
static_assert(std::is_same<
remove_t<int, int, char, int, float, int>,
std::tuple<char, float>
>::value, "Oops");
}
Live example
Following may help:
namespace detail
{
template <typename T, typename Tuple, typename Res = std::tuple<>>
struct removeT_helper;
template<typename T, typename Res>
struct removeT_helper<T, std::tuple<>, Res>
{
using type = Res;
};
template<typename T, typename... Ts, typename... TRes>
struct removeT_helper<T, std::tuple<T, Ts...>, std::tuple<TRes...>> :
removeT_helper<T, std::tuple<Ts...>, std::tuple<TRes...>>
{};
template<typename T, typename T1, typename ...Ts, typename... TRes>
struct removeT_helper<T, std::tuple<T1, Ts...>, std::tuple<TRes...>> :
removeT_helper<T, std::tuple<Ts...>, std::tuple<TRes..., T1>>
{};
}
template <typename T, typename...Ts> struct RemoveT
{
using type = typename detail::removeT_helper<T, std::tuple<Ts...>>::type;
};
static_assert(std::is_same<std::tuple<char, float>,
typename RemoveT<int, int, char, int, float, int>::type>::value, "");
First, move all the specific template names out into a list. There might be a way of specifying a template name, and a list of parameters, giving that template with the parameters, but I haven't been able to figure it out:
template <typename...TArgs> struct TypeList
{
typedef std::tuple<TArgs...> tuple_type;
// whatever other types you need
};
Next, define addition:
template<typename T, typename TList> struct AddT;
template<typename T, typename ... TArgs>
struct AddT< T, TypeList<TArgs...> >
{
typedef TypeList<T, TArgs... > type;
};
Then, define removal:
template<typename R, typename ... TArgs> struct RemoveT;
template<typename R>
struct RemoveT<R>
{
typedef TypeList<> type;
};
template<typename R, typename T, typename ...TArgs>
struct RemoveT<R, T, TArgs...>
{
typedef typename std::conditional
< std::is_same<R, T>::value
, typename RemoveT<R, TArgs...>::type
, typename AddT<T, typename RemoveT<R, TArgs...>::type>::type
>::type type;
};
Finally, test:
int result = 0;
result = std::is_same
< std::tuple<long,double>
, RemoveT<int, int, long, int, double, int>::type::tuple_type
>::value;
assert ( result );
Let us suppose that a std::tuple<some_types...> is given. I would like to create a new std::tuple whose types are the ones indexed in [0, sizeof...(some_types) - 2]. For instance, let's suppose that the starting tuple is std::tuple<int, double, bool>. I would like to obtain a sub-tuple defined as std::tuple<int, double>.
I'm quite new to variadic templates. As a first step I tried to write a struct in charge of storing the different types of the original std::tuple with the aim of creating a new tuple of the same kind (as in std::tuple<decltype(old_tuple)> new_tuple).
template<typename... types>
struct type_list;
template<typename T, typename... types>
struct type_list<T, types...> : public type_list<types...> {
typedef T type;
};
template<typename T>
struct type_list<T> {
typedef T type;
};
What I would like to do is something like:
std::tuple<type_list<bool, double, int>::type...> new_tuple // this won't work
And the next step would be of discarding the last element in the parameter pack. How can I access the several type's stored in type_list? and how to discard some of them?
Thanks.
Here is a way to solve your problem directly.
template<unsigned...s> struct seq { typedef seq<s...> type; };
template<unsigned max, unsigned... s> struct make_seq:make_seq<max-1, max-1, s...> {};
template<unsigned...s> struct make_seq<0, s...>:seq<s...> {};
template<unsigned... s, typename Tuple>
auto extract_tuple( seq<s...>, Tuple& tup ) {
return std::make_tuple( std::get<s>(tup)... );
}
You can use this as follows:
std::tuple< int, double, bool > my_tup;
auto short_tup = extract_tuple( make_seq<2>(), my_tup );
auto skip_2nd = extract_tuple( seq<0,2>(), my_tup );
and use decltype if you need the resulting type.
A completely other approach would be to write append_type, which takes a type and a tuple<...>, and adds that type to the end. Then add to type_list:
template<template<typename...>class target>
struct gather {
typedef typename type_list<types...>::template gather<target>::type parent_result;
typedef typename append< parent_result, T >::type type;
};
which gives you a way to accumulate the types of your type_list into an arbitrary parameter pack holding template. But that isn't required for your problem.
This kind of manipulation is fairly easy with an index sequence technique: generate an index sequence with two fewer indices than your tuple, and use that sequence to select fields from the original. Using std::make_index_sequence and return type deduction from C++14:
template <typename... T, std::size_t... I>
auto subtuple_(const std::tuple<T...>& t, std::index_sequence<I...>) {
return std::make_tuple(std::get<I>(t)...);
}
template <int Trim, typename... T>
auto subtuple(const std::tuple<T...>& t) {
return subtuple_(t, std::make_index_sequence<sizeof...(T) - Trim>());
}
In C++11:
#include <cstddef> // for std::size_t
template<typename T, T... I>
struct integer_sequence {
using value_type = T;
static constexpr std::size_t size() noexcept {
return sizeof...(I);
}
};
namespace integer_sequence_detail {
template <typename, typename> struct concat;
template <typename T, T... A, T... B>
struct concat<integer_sequence<T, A...>, integer_sequence<T, B...>> {
typedef integer_sequence<T, A..., B...> type;
};
template <typename T, int First, int Count>
struct build_helper {
using type = typename concat<
typename build_helper<T, First, Count/2>::type,
typename build_helper<T, First + Count/2, Count - Count/2>::type
>::type;
};
template <typename T, int First>
struct build_helper<T, First, 1> {
using type = integer_sequence<T, T(First)>;
};
template <typename T, int First>
struct build_helper<T, First, 0> {
using type = integer_sequence<T>;
};
template <typename T, T N>
using builder = typename build_helper<T, 0, N>::type;
} // namespace integer_sequence_detail
template <typename T, T N>
using make_integer_sequence = integer_sequence_detail::builder<T, N>;
template <std::size_t... I>
using index_sequence = integer_sequence<std::size_t, I...>;
template<size_t N>
using make_index_sequence = make_integer_sequence<size_t, N>;
#include <tuple>
template <typename... T, std::size_t... I>
auto subtuple_(const std::tuple<T...>& t, index_sequence<I...>)
-> decltype(std::make_tuple(std::get<I>(t)...))
{
return std::make_tuple(std::get<I>(t)...);
}
template <int Trim, typename... T>
auto subtuple(const std::tuple<T...>& t)
-> decltype(subtuple_(t, make_index_sequence<sizeof...(T) - Trim>()))
{
return subtuple_(t, make_index_sequence<sizeof...(T) - Trim>());
}
Live at Coliru.
Subrange from tuple with boundary checking, without declaring "helper classes":
template <size_t starting, size_t elems, class tuple, class seq = decltype(std::make_index_sequence<elems>())>
struct sub_range;
template <size_t starting, size_t elems, class ... args, size_t ... indx>
struct sub_range<starting, elems, std::tuple<args...>, std::index_sequence<indx...>>
{
static_assert(elems <= sizeof...(args) - starting, "sub range is out of bounds!");
using tuple = std::tuple<std::tuple_element_t<indx + starting, std::tuple<args...>> ...>;
};
Usage:
struct a0;
...
struct a8;
using range_outer = std::tuple<a0, a1, a2, a3, a4, a5, a6, a7, a8>;
sub_range<2, 3, range_outer>::tuple; //std::tuple<a2, a3, a4>
One way to do it is to recursively pass two tuples to a helper struct that takes the first element of the "source" tuple and adds it to the end of the another one:
#include <iostream>
#include <tuple>
#include <type_traits>
namespace detail {
template<typename...>
struct truncate;
// this specialization does the majority of the work
template<typename... Head, typename T, typename... Tail>
struct truncate< std::tuple<Head...>, std::tuple<T, Tail...> > {
typedef typename
truncate< std::tuple<Head..., T>, std::tuple<Tail...> >::type type;
};
// this one stops the recursion when there's only
// one element left in the source tuple
template<typename... Head, typename T>
struct truncate< std::tuple<Head...>, std::tuple<T> > {
typedef std::tuple<Head...> type;
};
}
template<typename...>
struct tuple_truncate;
template<typename... Args>
struct tuple_truncate<std::tuple<Args...>> {
// initiate the recursion - we start with an empty tuple,
// with the source tuple on the right
typedef typename detail::truncate< std::tuple<>, std::tuple<Args...> >::type type;
};
int main()
{
typedef typename tuple_truncate< std::tuple<bool, double, int> >::type X;
// test
std::cout << std::is_same<X, std::tuple<bool, double>>::value; // 1, yay
}
Live example.
I'm writing a template for expressions parametrised by an arbitrary number of char labels.
Given an argument list, a factory function returns an expression of different types depending on whether there are two arguments of the same types or whether they are unique.
A concrete example: suppose that A is a "labelable" object with its operator() overloaded to produce an ?Expression<...>. Let a, b, ... be declared as labels LabelName<'a'>, LabelName<'b'>, .... Then A(a,b,c,d) would produce a UniqueExpression<'a','b','c','d'>, whereas A(a,c,b,c) would produce a RepeatedExpression<'a','c','b','c'> instead.
To achieve this, I had to define the ?Expression's factory function with auto and decltype. Moreover, the decltype has to cascade to another decltype until the metaprogram finishes recursing through the arguments and the return type is finally decided. As an illustration, I have isolated a fairly minimal code for the factory method.
template <typename... T> struct TypeList { };
template <char C> struct LabelName { };
template <typename... T> class UniqueExpression
{
// Contains implementation details in actual code
};
template <typename... T> class RepeatedExpression
{
// Contains implementation details in actual code
};
class ExpressionFactory {
private:
template <char _C, typename... T, typename... _T>
static UniqueExpression<T...>
_do_build(TypeList<T...>,
TypeList<LabelName<_C>>,
TypeList<>,
TypeList<_T...>)
{
return UniqueExpression<T...> ();
}
template <char _C, typename... T, typename... _T1, typename... _T2, typename... _T3>
static RepeatedExpression<T...>
_do_build(TypeList<T...>,
TypeList<LabelName<_C>, _T1...>,
TypeList<LabelName<_C>, _T2...>,
TypeList<_T3...>)
{
return RepeatedExpression<T...> ();
}
template <char _C1, char _C2, typename... T, typename... _T1, typename... _T2, typename... _T3>
static auto
_do_build(TypeList<T...>,
TypeList<LabelName<_C1>, _T1...>,
TypeList<LabelName<_C2>, _T2...>,
TypeList<_T3...>)
-> decltype(_do_build(TypeList<T...>(),
TypeList<LabelName<_C1>, _T1...>(),
TypeList<_T2...>(),
TypeList<_T3..., LabelName<_C2>>()))
{
return _do_build(TypeList<T...>(),
TypeList<LabelName<_C1>, _T1...>(),
TypeList<_T2...>(),
TypeList<_T3..., LabelName<_C2>>());
}
template <char _C1, char _C2, typename... T, typename... _T1, typename... _T2>
static auto
_do_build(TypeList<T...>,
TypeList<LabelName<_C1>, LabelName<_C2>, _T1...>,
TypeList<>,
TypeList<LabelName<_C2>, _T2...>)
-> decltype(_do_build(TypeList<T...>(),
TypeList<LabelName<_C2>, _T1...>(),
TypeList<_T2...>(),
TypeList<>()))
{
return _do_build(TypeList<T...>(),
TypeList<LabelName<_C2>, _T1...>(),
TypeList<_T2...>(),
TypeList<>());
}
public:
template <char C, typename... T>
static auto
build_expression(LabelName<C>, T...)
-> decltype(_do_build(TypeList<LabelName<C>, T...>(),
TypeList<LabelName<C>, T...>(),
TypeList<T...>(),
TypeList<>()))
{
return _do_build(TypeList<LabelName<C>, T...>(),
TypeList<LabelName<C>, T...>(),
TypeList<T...>(),
TypeList<>());
}
};
The factory could be called in the program like so: (in the actual program there is another class with an overloaded operator() which calls the factory)
int main()
{
LabelName<'a'> a;
LabelName<'b'> b;
...
LabelName<'j'> j;
auto expr = ExpressionFactory::build_expression(a,b,c,d,e,f,g,h,i,j);
// Perhaps do some cool stuff with expr
return 0;
}
The above code works as intended, and is correctly compiled by both GCC and the Intel compiler. Now, I understand that the compiler would take more time to perform recursive template deduction as I crank up the number of labels I use.
On my computer, if build_expression is called with one argument, then GCC 4.7.1 takes around 0.26 second to compile on average. The compile time scales up to around 0.29 second for five arguments, and to 0.62 second for ten arguments. This is all perfectly reasonable.
The story is quite different with the Intel compiler. ICPC 13.0.1 compiles the one-argument code in 0.35 second, and the compile time stays pretty much constant for up to four arguments. At five arguments the compile time goes up to 12 seconds, and at six arguments it shoots up above 9600 seconds (that is, over 2 hours and 40 minutes). Needless to say, I haven't waited long enough to find out how long it takes to compile the seven-argument version.
Two questions immediately come to mind:
Is the Intel compiler particularly known to be slow to compile recursive decltype?
Is there any way to rewrite this code to achieve the same effect in a way that is perhaps friendlier to the compiler?
Here is a stab at it. Instead of doing pairwise comparisons of each of the elements, I sort the list of types, then use a brain-dead unique algorithm to see if there are any duplicates.
I implemented merge sort on types, because it was fun. Probably a naive bubble sort would work better on reasonable number of arguments. Note that a bit of work would allow us to do a merge sort on long lists, and specialize for bubble sorts (or even insertion sorts) on short lists. I'm not up for writing a template quicksort.
This gives me a compile time boolean that says if there are duplicates in the list. I can then use enable_if to pick which overload I'm going to use.
Note that your solution involved n^2 layers of template recursion, at each stage of which the return type requires evaluating the type of a 1 step simpler class, and then the type returned also requires the same! If the Intel compiler memoization fails, you are talking exponential amounts of work.
I augmented a few of your classes with some helpers. I made your LabelNames inherit from std::integral_constant, so I have easy compile time access to their value. This makes the sorting code a tad easier. I also exposed an enum from the repeated and unique return values so I can do simple printf debugging on the result.
Most of this work is writing the merge sort -- is there a standard compile time type sort we could use?
#include <type_traits>
#include <iostream>
template <typename... T> struct TypeList { };
// NOTE THIS CHANGE:
template <char C> struct LabelName:std::integral_constant<char, C> {};
template <typename... T> class UniqueExpression
{
// Contains implementation details in actual code
public:
enum { is_unique = true };
};
template <typename... T> class RepeatedExpression
{
// Contains implementation details in actual code
public:
enum { is_unique = false };
};
// A compile time merge sort for types
// Split takes a TypeList<>, and sticks the even
// index types into Left and odd into Right
template<typename T>
struct Split;
template<>
struct Split<TypeList<>>
{
typedef TypeList<> Left;
typedef TypeList<> Right;
};
template<typename T>
struct Split<TypeList<T>>
{
typedef TypeList<T> Left;
typedef TypeList<> Right;
};
// Prepends First into the TypeList List.
template<typename First, typename List>
struct Prepend;
template<typename First, typename... ListContents>
struct Prepend<First,TypeList<ListContents...>>
{
typedef TypeList<First, ListContents...> type;
};
template<typename First, typename Second, typename... Tail>
struct Split<TypeList<First, Second, Tail...>>
{
typedef typename Prepend< First, typename Split<TypeList<Tail...>>::Left>::type Left;
typedef typename Prepend< Second, typename Split<TypeList<Tail...>>::Right>::type Right;
};
// Merges the sorted TypeList<>s Left and Right to the end of TypeList<> MergeList
template< typename Left, typename Right, typename MergedList=TypeList<> >
struct Merge;
template<typename MergedList>
struct Merge< TypeList<>, TypeList<>, MergedList >
{
typedef MergedList type;
};
template<typename L1, typename... Left, typename... Merged>
struct Merge< TypeList<L1, Left...>, TypeList<>, TypeList<Merged... >>
{
typedef TypeList<Merged..., L1, Left...> type;
};
template<typename R1, typename... Right, typename... Merged>
struct Merge< TypeList<>, TypeList<R1, Right...>, TypeList<Merged...> >
{
typedef TypeList<Merged..., R1, Right...> type;
};
template<typename L1, typename... Left, typename R1, typename... Right, typename... Merged>
struct Merge< TypeList<L1, Left...>, TypeList<R1, Right...>, TypeList<Merged...>>
{
template<bool LeftIsSmaller, typename LeftList, typename RightList, typename MergedList>
struct MergeHelper;
template<typename FirstLeft, typename... LeftTail, typename FirstRight, typename... RightTail, typename... MergedElements>
struct MergeHelper< true, TypeList<FirstLeft, LeftTail...>, TypeList<FirstRight, RightTail...>, TypeList<MergedElements...> >
{
typedef typename Merge< TypeList<LeftTail...>, TypeList< FirstRight, RightTail... >, TypeList< MergedElements..., FirstLeft > >::type type;
};
template<typename FirstLeft, typename... LeftTail, typename FirstRight, typename... RightTail, typename... MergedElements>
struct MergeHelper< false, TypeList<FirstLeft, LeftTail...>, TypeList<FirstRight, RightTail...>, TypeList<MergedElements...> >
{
typedef typename Merge< TypeList<FirstLeft, LeftTail...>, TypeList<RightTail... >, TypeList< MergedElements..., FirstRight > >::type type;
};
typedef typename MergeHelper< (L1::value < R1::value), TypeList<L1, Left...>, TypeList<R1, Right...>, TypeList<Merged...> >::type type;
};
// Takes a TypeList<T...> and sorts it via a merge sort:
template<typename List>
struct MergeSort;
template<>
struct MergeSort<TypeList<>>
{
typedef TypeList<> type;
};
template<typename T>
struct MergeSort<TypeList<T>>
{
typedef TypeList<T> type;
};
template<typename First, typename Second, typename... T>
struct MergeSort<TypeList<First, Second, T...>>
{
typedef Split<TypeList<First, Second, T...>> InitialSplit;
typedef typename MergeSort< typename InitialSplit::Left >::type Left;
typedef typename MergeSort< typename InitialSplit::Right >::type Right;
typedef typename Merge< Left, Right >::type type;
};
// Sorts a TypeList<T..>:
template<typename List>
struct Sort: MergeSort<List> {};
// Checks sorted TypeList<T...> SortedList for adjacent duplicate types
// return value is in value
template<typename SortedList>
struct Unique;
template<> struct Unique< TypeList<> >:std::true_type {};
template<typename T> struct Unique< TypeList<T> >:std::true_type {};
template<typename First, typename Second, typename... Tail>
struct Unique< TypeList< First, Second, Tail... > >
{
enum
{
value = (!std::is_same<First, Second>::value) &&
Unique< TypeList<Second, Tail...> >::value
};
};
// value is true iff there is a repeated type in Types...
template<typename... Types>
struct RepeatedType
{
typedef TypeList<Types...> MyListOfTypes;
typedef typename Sort< MyListOfTypes >::type MyListOfTypesSorted;
enum
{
value = !Unique< MyListOfTypesSorted >::value
};
};
// A struct that creates an rvalue trivial constructed type
// of any type requested.
struct ProduceRequestedType
{
template<typename Result>
operator Result() { return Result(); };
};
struct ExpressionFactory {
template<typename... T>
typename std::enable_if<
!RepeatedType<T...>::value,
UniqueExpression<T...>
>::type
build_expression(T...) const
{
return ProduceRequestedType();
};
template<typename... T>
typename std::enable_if<
RepeatedType<T...>::value,
RepeatedExpression<T...>
>::type
build_expression(T...) const
{
return ProduceRequestedType();
};
};
// Simple testing code for above:
int main()
{
auto foo1 = ExpressionFactory().build_expression( LabelName<'a'>(), LabelName<'b'>(), LabelName<'a'>() );
typedef decltype(foo1) foo1Type;
if (foo1Type::is_unique)
std::cout << "foo1 is unique\n";
else
std::cout << "foo1 is repeated\n";
auto foo2 = ExpressionFactory().build_expression( LabelName<'q'>(), LabelName<'a'>(), LabelName<'b'>(), LabelName<'d'>(), LabelName<'t'>(), LabelName<'z'>() );
typedef decltype(foo2) foo2Type;
if (foo2Type::is_unique)
std::cout << "foo2 is unique\n";
else
std::cout << "foo2 is repeated\n";
}
In addition I'd like to add a critique of your code: Template programming is programming -- your typenames are the equivalent of using "i1" through "i9" for integer variables in a function. Give your typenames meaningful names when doing something non-trivial.
How does this compile on Intel?
template <int I> struct int_ {};
template < typename ... Pack >
struct thingy
{
void call()
{
f(???);
}
};
When instantiated it should end up being:
struct thingy<int,char,double>
{
void call()
{
f(int, int_<1>(), char, int_<2>(), double, int_<3>());
}
}
What you think, can it be done? How?
The only thing I can think of is to have overloads for thingy with N different parameters like so:
template < typename T0 > struct thingy<T0> { ... };
template < typename T0, typename T1 > struct thingy<T0,T1> { ... };
etc...
With a call implementation in each.
Can it be done
Yes, of course.
How ?
In several steps.
You need to be able to create a range of integers
You need to be able to interleave two sequences
Let us start with the range of integers.
template <size_t...>
struct IntegralPack {};
template <size_t A, size_t... N>
IntegralPack<N..., A> push_back(IntegralPack<N...>);
template <size_t A, size_t... N>
IntegralPack<A, N...> push_front(IntegralPack<N...>);
template <size_t L, size_t H>
struct IntegralRangeImpl {
typedef typename IntegralRangeImpl<L+1, H>::type Base;
typedef decltype(push_front<L>((Base()))) type;
};
template <size_t X>
struct IntegralRangeImpl<X, X> {
typedef IntegralPack<> type;
};
template <size_t L, size_t H>
struct IntegralRange {
static_assert(L <= H, "Incorrect range");
typedef typename IntegralRangeImpl<L, H>::type type;
};
The conversion step is easy enough (thankfully):
template <typename...>
struct TypePack {};
template <size_t... N>
TypePack<int_<N>...> to_int(IntegralPack<N...>);
So the next difficulty is the merge.
template <typename... As, typename... Bs>
TypePack<As..., Bs...> cat(TypePack<As...>, TypePack<Bs...>);
template <typename, typename> struct Interleaver;
template <>
struct Interleaver<TypePack<>, TypePack<>> {
typedef TypePack<> type;
};
template <typename A0, typename B0, typename... As, typename... Bs>
struct Interleaver<TypePack<A0, As...>, TypePack<B0, Bs...>> {
typedef typename Interleaver<TypePack<As...>, TypePack<Bs...>>::type Base;
typedef decltype(cat(TypePack<A0, B0>{}, Base{})) type;
};
Putting it altogether:
template <typename... Pack>
struct thingy {
typedef typename IntegralRange<1, sizeof...(Pack) + 1>::type Indexes;
typedef decltype(to_int(Indexes{})) Ints;
typedef typename Interleaver<TypePack<Pack...>, Ints>::type Types;
void call() { this->callImpl(Types{}); }
template <typename... Ts>
void callImpl(TypePack<Ts...>) {
f(Ts{}...);
}
};
Tadaam!
So the way I went about it is a little more specific to what I was actually doing. Turns out some information I thought was beside the point helped me out. I think though that a similar technique could be used for any case.
For one thing...in my case "thingy<>" actually has values in it and is passed to the invoker function. This actually helps a lot.
Also, since the object was to convert the stuff in thingy to serve as induces for another weird thingy, and the ints being passed in were to index the first thingy...the ints end up all being 1 when I do my recursion. So while what I was after was something like (simplified to remove the second tuple):
f(get(my_thingy, idx<1>), get(my_thingy, idx<2>), ...)
It turns out the recursion gets rid of idx<2>...idx:
template < typename Fun, typename H, typename ... T, typename ... Args >
auto call(Fun f, thingy<H,T...> const& t, Args&& ... args)
-> decltype(call(f,static_cast<thingy<T...>const&>(t), get(t,idx<1>), std::forward<Args>(args)...)
{
return call(f, static_cast<thingy<T...>const&>(t), get(t, idx<1>), args...);
}
template < typename Fun, typename ... Args >
auto call(Fun f, thingy<> const&, Args&& ... args)
-> decltype(f(std::forward<Args>(args)...))
{
return f(std::forward<Args>(args)...);
}
I have not been able to fully test thing because the get function fails on me for some reason when using const&...kinda pissing me off. I'm fairly confident that this does the trick though.
If the parameter to idx wasn't always 1 I think that could be forwarded along in a similar fashion.