Properly align in-memory template, invariant of order of parameters - c++

Take a look at this template.
template < typename T1, typename T2, typename T3 >
struct Alignement {
T1 first;
T2 second;
T3 third;
};
int main() {
Alignement<char, int, double> a1;
Alignement<char, double, int> a2;
assert( sizeof(a1) < sizeof(a2) );
return 0;
}
Obviously assertion holds. Sub-optimal ordering leads to 50% more memory usage in this case.
My question is, what are the ways of combating it and properly ordering types in template structure, other than kindly asking user to take care of it himself (which would leave him with the same problem if he didn't know sizes of his types beforehand)?
My idea is to generate optimal ordering dynamically at compile time with macros or TMP, but I have no proper knowledge of these techniques. Or perhaps an army of partially specialized templates would get the job done?
The key aspect is preserving the AlignedObject.first syntax for client.
For my specific case, I'm looking for solution for exactly 3 parameters (3! possible orderings) but general solution (including variadic-length-templates) would be interesting to see.

For my specific case, I'm looking for solution for exactly 3 parameters (3! possible orderings) but general solution (including variadic-length-templates) would be interesting to see.
I propose a general solution: a variadic type sorter and a variadic Alignement that use it.
Following the Peter's suggestion, the idea is to sort the type putting biggers types first.
I use C++17 because the new template folding permit I use C++11 because the OP must use a C++11 only compliant compiler to make extremely simple a type traits that say if the first type of a list is the bigger one (according sizeof()). I maintain, commented, the original C++17 version
// iftb = is first type bigger ?
// original C++17 version
//
// template <typename T0, typename ... Ts>
// struct iftb
// : public std::integral_constant<bool,((sizeof(Ts) <= sizeof(T0)) && ...)>
// { };
template <typename ...>
struct iftb;
template <typename T0>
struct iftb<T0> : public std::true_type
{ };
template <typename T0, typename T1, typename ... Ts>
struct iftb<T0, T1, Ts...>
: public std::integral_constant<bool,
(sizeof(T1) <= sizeof(T0)) && iftb<T0, Ts...>::value>
{ };
Now a type traits to know if a type container contain a list of ordered types
// ifctb = is first contained type bigger ?
template <typename>
struct ifctb;
template <template <typename ...> class C, typename ... Tc>
struct ifctb<C<Tc...>> : public iftb<Tc...>
{ };
Now the type orderer is simple to write (but not particularly efficient; sorry)
// to = type orderer
template <typename, typename Cd, bool = ifctb<Cd>::value>
struct to;
template <template <typename...> class C, typename ... To,
typename T0, typename ... Tu>
struct to<C<To...>, C<T0, Tu...>, true> : public to<C<To..., T0>, C<Tu...>>
{ };
template <template <typename...> class C, typename ... To,
typename T0, typename ... Tu>
struct to<C<To...>, C<T0, Tu...>, false> : public to<C<To...>, C<Tu..., T0>>
{ };
template <template <typename...> class C, typename ... To, typename T>
struct to<C<To...>, C<T>, true>
{ using type = C<To..., T>; };
Now I propose an indexed wrapper that must be defined through partial specialization to define first, second and third (etc., if you want extend the solution)
template <std::size_t, typename>
struct wrapper;
template <typename T>
struct wrapper<0U, T>
{ T first; };
template <typename T>
struct wrapper<1U, T>
{ T second; };
template <typename T>
struct wrapper<2U, T>
{ T third; };
We need std::index_sequence and std::make_index_sequence that are available only starting from C++14; but the OP must compile this code in a C++11 only compliant compiler so I propose a simple emulation C++11 compliant
// std::index_sequence and std::make_index_sequence simplified emulators
template <std::size_t...>
struct indexSequence
{ using type = indexSequence; };
template <typename, typename>
struct concatSequences;
template <std::size_t... S1, std::size_t... S2>
struct concatSequences<indexSequence<S1...>, indexSequence<S2...>>
: public indexSequence<S1..., ( sizeof...(S1) + S2 )...>
{ };
template <std::size_t N>
struct makeIndexSequenceH
: public concatSequences<
typename makeIndexSequenceH<(N>>1)>::type,
typename makeIndexSequenceH<N-(N>>1)>::type>::type
{ };
template<>
struct makeIndexSequenceH<0> : public indexSequence<>
{ };
template<>
struct makeIndexSequenceH<1> : public indexSequence<0>
{ };
template <std::size_t N>
using makeIndexSequence = typename makeIndexSequenceH<N>::type;
With the help of std::tuple, std::index_sequence and std::make_index_sequence indexSequence and makeIndexSequence (C++11 compliant simplified emulations of std::index_sequence and std::make_index_sequence), I add a couples of helper structs for Alignement
template <typename>
struct AlH2;
template <typename ... Ts>
struct AlH2<std::tuple<Ts...>> : public Ts...
{ };
template <typename...>
struct AlH1;
template <std::size_t ... Is, typename ... Ts>
struct AlH1<indexSequence<Is...>, Ts...>
: public AlH2<typename to<std::tuple<>,
std::tuple<wrapper<Is, Ts>...>>::type>
{ };
Now Alignement can be written as
template <typename ... Ts>
struct Alignement
: public AlH1<makeIndexSequence<sizeof...(Ts)>, Ts...>
{ };
The following is a full (I remember: C++17) C++11 compiling example with some assert()'s to verify the correct ordering.
#include <tuple>
#include <cassert>
#include <iostream>
#include <type_traits>
// std::index_sequence and std::make_index_sequence simplified emulators
template <std::size_t...>
struct indexSequence
{ using type = indexSequence; };
template <typename, typename>
struct concatSequences;
template <std::size_t... S1, std::size_t... S2>
struct concatSequences<indexSequence<S1...>, indexSequence<S2...>>
: public indexSequence<S1..., ( sizeof...(S1) + S2 )...>
{ };
template <std::size_t N>
struct makeIndexSequenceH
: public concatSequences<
typename makeIndexSequenceH<(N>>1)>::type,
typename makeIndexSequenceH<N-(N>>1)>::type>::type
{ };
template<>
struct makeIndexSequenceH<0> : public indexSequence<>
{ };
template<>
struct makeIndexSequenceH<1> : public indexSequence<0>
{ };
template <std::size_t N>
using makeIndexSequence = typename makeIndexSequenceH<N>::type;
// iftb = is first type bigger ?
// original C++17 version
//
// template <typename T0, typename ... Ts>
// struct iftb
// : public std::integral_constant<bool,((sizeof(Ts) <= sizeof(T0)) && ...)>
// { };
template <typename ...>
struct iftb;
template <typename T0>
struct iftb<T0> : public std::true_type
{ };
template <typename T0, typename T1, typename ... Ts>
struct iftb<T0, T1, Ts...>
: public std::integral_constant<bool,
(sizeof(T1) <= sizeof(T0)) && iftb<T0, Ts...>::value>
{ };
// ifctb = is first contained type bigger ?
template <typename>
struct ifctb;
template <template <typename ...> class C, typename ... Tc>
struct ifctb<C<Tc...>>
: public iftb<Tc...>
{ };
// to = type orderer
template <typename, typename Cd, bool = ifctb<Cd>::value>
struct to;
template <template <typename...> class C, typename ... To,
typename T0, typename ... Tu>
struct to<C<To...>, C<T0, Tu...>, true> : public to<C<To..., T0>, C<Tu...>>
{ };
template <template <typename...> class C, typename ... To,
typename T0, typename ... Tu>
struct to<C<To...>, C<T0, Tu...>, false> : public to<C<To...>, C<Tu..., T0>>
{ };
template <template <typename...> class C, typename ... To, typename T>
struct to<C<To...>, C<T>, true>
{ using type = C<To..., T>; };
template <std::size_t, typename>
struct wrapper;
template <typename T>
struct wrapper<0U, T>
{ T first; };
template <typename T>
struct wrapper<1U, T>
{ T second; };
template <typename T>
struct wrapper<2U, T>
{ T third; };
template <typename>
struct AlH2;
template <typename ... Ts>
struct AlH2<std::tuple<Ts...>> : public Ts...
{ };
template <typename...>
struct AlH1;
template <std::size_t ... Is, typename ... Ts>
struct AlH1<indexSequence<Is...>, Ts...>
: public AlH2<typename to<std::tuple<>,
std::tuple<wrapper<Is, Ts>...>>::type>
{ };
template <typename ... Ts>
struct Alignement
: public AlH1<makeIndexSequence<sizeof...(Ts)>, Ts...>
{ };
int main ()
{
Alignement<char, int, long long> a0;
a0.first = '0';
a0.second = 1;
a0.third = 2LL;
assert( (std::size_t)&a0.third < (std::size_t)&a0.first );
assert( (std::size_t)&a0.third < (std::size_t)&a0.second );
assert( (std::size_t)&a0.second < (std::size_t)&a0.first );
}
-- EDIT --
The OP ask
using your solution, if I want to achieve N-argument template class, I need to define N wrapper classes, each containing single field name for n-th argument. Different Alignement<>'s should have different field names == set of N wrappers for each of them. Any good idea for a macro (or template...) to achieve that?
For me, C-style macros are distilled evil (and I don't know they very well), but...
What I propose isn't a full solution; only a draft.
If you define the following set of macros
#define WrpNum(wName, num, fName) \
template <typename T>\
struct wrapper_ ## wName <num, T> \
{ T fName; };
#define Foo_1(wName, tot, fName) \
WrpNum(wName, tot-1U, fName)
#define Foo_2(wName, tot, fName, ...) \
WrpNum(wName, tot-2U, fName) \
Foo_1(wName, tot, __VA_ARGS__)
#define Foo_3(wName, tot, fName, ...) \
WrpNum(wName, tot-3U, fName) \
Foo_2(wName, tot, __VA_ARGS__)
// Foo_4(), Foo_5(), ...
#define Foo(wName, num, ...) \
template <std::size_t, typename> \
struct wrapper_ ## wName; \
Foo_ ## num(wName, num, __VA_ARGS__)
you can define a template indexed struct wrapper_wrp1 with specializations and a first member in wrapper_wrp1<0U, T> specialization, a second member in wrapper_wrp1<1U, T>, etc, calling
Foo(wrp1, 3, first, second, third)
Observe that you need the total number of specializations as second parameter.
Maybe is possible to make better (with recursive variadic macro?) but, frankly, I'm not interested too much in macros.
Given this call
Foo(wrp1, 3, first, second, third)
you could (caution: not tested) modify AlH1 the specific wrapper struct (wrapper_wrp1)
template <std::size_t ... Is, typename ... Ts>
struct AlH1<std::index_sequence<Is...>, Ts...>
: public AlH2<typename to<std::tuple<>,
std::tuple<wrapper_wrp1<Is, Ts>...>>::type>
{ };

#include <iostream>
#include <type_traits>
template <typename... O>
struct SizeOrder;
template <typename T1, typename T2, typename... Rest>
struct SizeOrder<T1, T2, Rest...> {
using type = typename std::conditional<(T1::size::value > T2::size::value || (T1::size::value == T2::size::value && T1::order::value < T2::order::value)), typename SizeOrder<T2, Rest...>::type, int>::type;
};
template <typename T1, typename T2>
struct SizeOrder<T1, T2> {
using type = typename std::conditional<(T1::size::value > T2::size::value || (T1::size::value == T2::size::value && T1::order::value < T2::order::value)), void, int>::type;
};
template <typename... T>
using Order = typename SizeOrder<T...>::type;
template <typename T1, int T2>
struct DeclarationOrder {
using size = typename std::alignment_of<T1>;
using order = typename std::integral_constant<int, T2>;
};
template <typename A, typename B, typename C, typename = void>
struct Alignement;
#define AO DeclarationOrder<A,1>
#define BO DeclarationOrder<B,2>
#define CO DeclarationOrder<C,3>
template <typename A, typename B, typename C>
struct Alignement<A, B, C, Order<AO, BO, CO>> {
A first;
B second;
C third;
};
template <typename A, typename B, typename C>
struct Alignement<A, B, C, Order<AO, CO, BO>> {
A first;
C third;
B second;
};
template <typename A, typename B, typename C>
struct Alignement<A, B, C, Order<BO, AO, CO>> {
B second;
A first;
C third;
};
template <typename A, typename B, typename C>
struct Alignement<A, B, C, Order<BO, CO, AO>> {
B second;
C third;
A first;
};
template <typename A, typename B, typename C>
struct Alignement<A, B, C, Order<CO, AO, BO>> {
C third;
A first;
B second;
};
template <typename A, typename B, typename C>
struct Alignement<A, B, C, Order<CO, BO, AO>> {
C third;
B second;
A first;
};
int main() {
Alignement<char, int, double> t1;
std::cout << sizeof(t1) << std::endl << sizeof(t1.first) << std::endl << sizeof(t1.second) << std::endl << std::endl;
Alignement<char, double, int> t2;
std::cout << sizeof(t2) << std::endl << sizeof(t2.first) << std::endl << sizeof(t2.second) << std::endl << std::endl;
return 0;
}
EDIT: Added a Order<> template to recursively check the size-order of any amount of parameters and extended Aligenment to hold 3 variables.
EDIT2: Template deduction failed when using types with same size, so I changed the SizeOrder template to take a DeclarationOrder template to remove the ambiguity from having 2 possible orders for something like Alignement<int, int, double>
With some testing on godbolt to figure out the macro part we can condense the whole thing to this.
#include <iostream>
#include <type_traits>
template <typename... O>
struct SizeOrder;
template <typename T1, typename T2, typename... Rest>
struct SizeOrder<T1, T2, Rest...> {
using type = typename std::conditional<(T1::size::value > T2::size::value || (T1::size::value == T2::size::value && T1::order::value < T2::order::value)), typename SizeOrder<T2, Rest...>::type, int>::type;
};
template <typename T1, typename T2>
struct SizeOrder<T1, T2> {
using type = typename std::conditional<(T1::size::value > T2::size::value || (T1::size::value == T2::size::value && T1::order::value < T2::order::value)), void, int>::type;
};
template <typename... T>
using Order = typename SizeOrder<T...>::type;
template <typename T1, int T2>
struct DeclarationOrder {
using size = typename std::alignment_of<T1>;
using order = typename std::integral_constant<int, T2>;
};
template <typename A, typename B, typename C, typename = void>
struct Alignement;
#define AO DeclarationOrder<A,1>
#define BO DeclarationOrder<B,2>
#define CO DeclarationOrder<C,3>
#define Aname first
#define Bname second
#define Cname third
#define MAKE_SPECIALIZATION(FIRST, SECOND, THIRD) \
template <typename A, typename B, typename C> \
struct Alignement<A, B, C, Order<FIRST ## O, SECOND ## O, THIRD ## O>> { \
FIRST FIRST ## name; \
SECOND SECOND ## name; \
THIRD THIRD ## name; \
};
MAKE_SPECIALIZATION(A,B,C)
MAKE_SPECIALIZATION(A,C,B)
MAKE_SPECIALIZATION(B,A,C)
MAKE_SPECIALIZATION(B,C,A)
MAKE_SPECIALIZATION(C,A,B)
MAKE_SPECIALIZATION(C,B,A)
int main() {
Alignement<char, int, double> t1;
std::cout << sizeof(t1) << std::endl << sizeof(t1.first) << std::endl << sizeof(t1.second) << std::endl << std::endl;
Alignement<char, double, int> t2;
std::cout << sizeof(t2) << std::endl << sizeof(t2.first) << std::endl << sizeof(t2.second) << std::endl << std::endl;
return 0;
}
To extend it to 4, 5 or 6 variables we need to update struct Alignement to add template D before template = void. Then we #define DO DeclarationOrder<D,4> and #define Dname fourth.
Then we add a D and FOURTH to the MAKE_SPECIALIZATION macro and define all (16?) possible layouts.
Far from squeaky clean, but doable.

For the three-member case (or any other fixed number) you can use a sorting network to efficiently reduce the number of specializations (at best, log^2n swaps AFAIR); in C++11, something like (not tested):
template <typename T,std::size_t> struct MemberSpec: std::alignment_of<T> {};
struct NoMember{};
template<typename, typename = NoMember> struct MemberDecl{};
template<typename T, typename B> struct MemberDecl<MemberSpec<T,0>,B>: B { T first; };
template<typename T, typename B> struct MemberDecl<MemberSpec<T,1>,B>: B { T second; };
template<typename T, typename B> struct MemberDecl<MemberSpec<T,2>,B>: B { T third; };
template<typename M0,typename M1,typename M2>
struct Alignement_01: std::conditional_t<( M0::value < M1::value ),
MemberDecl<M0,MemberDecl<M1,MemberDecl<M2>>>, MemberDecl<M1,MemberDecl<M0,MemberDecl<M2>>> >{};
template<typename M0,typename M1,typename M2>
struct Alignement_02: std::conditional_t<( M0::value < M2::value ),
Alignement_01<M0,M1,M2>, Alignement_01<M2,M1,M0> >{};
template<typename M0,typename M1,typename M2>
struct Alignement_12: std::conditional_t<( M1::value < M2::value ),
Alignement_02<M0,M1,M2>, Alignement_02<M0,M2,M1> >{};
template<typename T0,typename T1,typename T2>
struct Alignement: Alignement_12<MemberSpec<T0,0>,MemberSpec<T1,1>,MemberSpec<T2,2>> {};
in the above, the resulting Alignment<T0,T1,T2> is standard layout since C++14 (and an aggregate since C++17), whenever the Tj are. This means that you'll need to place asserts to check proper field ordering and total size in pre-C++14.
EDIT: I forgot that, even in >=C++14, at most a base class can have non static data members; so, my Alignment<> is almost never standard layout; anyway, any decent compiler should place the fields the expected way, or better if it fits so. This may be acceptable considering that your goal is to help the compiler producing a more optimized layout.
The general case is solved similarly by implementing a sorting algorithm, or generalizing the above to work over some sorting-network abstraction; anyway, you'll still need to specialize something like MemberDecl to get your data member naming right (first,second,third,fourth,... whatever).

Related

A structure that stores its fields by size

I would like to know how can I do the following in C++:
Consider these classes :
C1 < C2 < C3 < ... < Cn, Ci < Cj means sizeof(Ci) < sizeof(Cj)
I want a structure that uses variadic templates as a sequence of Ci's,
OrderBySize<AnySequenceOfCis>, for example : OrderBySize<C1,C2,C3,...,Cn> or
OrderBySize<C2,C1,C3,C4,...,Cn> ... all possible combinations
and gives the following structure as a result :
class result{
Cn elem1;
Cn-1 elem2;
.
.
.
C1 elemn;
}
I read this article, it shows how we can define a Tuple<typename ...T>, however, this is different, much more difficult to implement and very useful.
EDIT :
order_by_size<T1, ..., Tn> would contain a tuple of the ordered combination of T1, ..., Tn
However I don't want the user to know that I am ordering the fields, the user would use it like a tuple. And thus, in order to access the fields, the user will use :
template<typename... Tn>
get<size_t>(const MyStructure<Tn ...>& m) to get the size_t'th element which is has an other index in the new tuple.
Basically this problem reduces to just sorting a list of types based on a given comparator. Once you have that, everything else follows. So this answer is just the sorting part. We'll start with a typelist:
template <typename...>
struct typelist {
using type = typelist;
};
I'm going to assume a bunch of metafunctions which are very short (head, tail, concat, size). I will omit them for brevity.
So let's just jump into writing merge sort:
template <typename TL, typename Cmp = LessSize>
struct sort
{
using left_right = typename split<TL, size<TL>::value/2>::type;
using left = typename sort<head_t<left_right>, Cmp>::type;
using right = typename sort<head_t<tail_t<left_right>>, Cmp>::type;
using type = typename merge<left, right, Cmp>::type;
};
// base case for exactly 1 element
template <typename T, typename Cmp>
struct sort<typelist<T>, Cmp> {
using type = typelist<T>;
};
// potentially add a base case for exactly 2 elements here?
The general structure here should look familiar. We split our typelist, TL, into two equal parts, sort both, and then merge. Of course, this is metaprogramming, so everything is unnecessarily complicated.
Let's start with split. split takes a typelist and a size, and returns a typelist of two typelists: the first having the given size, and the second being the remainder:
template <typename A, typename B, size_t N>
struct split_impl
: std::conditional<
size<A>::value < N,
split_impl<concat_t<A, typelist<head_t<B>>>, tail_t<B>, N>,
typelist<A, B>
>::type
{ };
template <typename TL, size_t N>
struct split
: split_impl<typelist<>, TL, N>
{ };
So that gives us left and right (at least once we apply head_t<> and head_t<tail_t<>>). All that's left is the merge step. I'm using the Boost MPL idea of what a metafunction class is, so LessSize is:
struct LessSize {
template <typename A, typename B>
using apply = std::integral_constant<bool, sizeof(A) < sizeof(B)>;
};
merge just has to walk both typelists and pick the smallest element based on the comparator between the two typelists. First, we'll start with all of our base cases:
template <typename L, typename R, typename Cmp>
struct merge;
// R empty
template <typename... T, typename Cmp>
struct merge<typelist<T...>, typelist<>, Cmp> {
using type = typelist<T...>;
};
// L empty
template <typename... T, typename Cmp>
struct merge<typelist<>, typelist<T...>, Cmp> {
using type = typelist<T...>;
};
And then the recursive step, which is somewhat ugly:
template <typename A, typename... As, typename B, typename... Bs, typename Cmp>
struct merge<typelist<A, As...>, typelist<B, Bs...>, Cmp>
: std::conditional<
Cmp::template apply<A, B>::value,
concat_t<typelist<A>, typename merge<typelist<As...>, typelist<B, Bs...>, Cmp>::type>,
concat_t<typelist<B>, typename merge<typelist<A, As...>, typelist<Bs...>, Cmp>::type>
>::type
{ };
Basically, given two typelists, {A, As...} and {B, Bs...}, we select the smallest based on Cmp, and that's the side we're popping the element off of. If Cmp::apply<A,B>, then we're concatenating A with the result of merging {As...} with {B, Bs...}. And vice versa.
And that's all she wrote:
template <typename T>
struct TD;
int main()
{
using T = sort<typelist<int, double, char, float>, LessSize>::type;
TD<T> r;
}
main.cpp: In function 'int main()':
main.cpp:131:11: error: aggregate 'TD<typelist<char, float, int, double> > r' has incomplete type and cannot be defined
TD<T> r;
^
Once you have the sorted types, making a tuple is straightforward:
template <template <typename...> class C>
struct meta_quote {
template <typename... T>
using apply = C<T...>;
};
template <typename F, typename TL>
struct meta_apply;
template <typename F, typename... T>
struct meta_apply<F, typelist<T...>> {
using type = typename F::template apply<T...>;
};
template <typename... T>
struct my_tuple
: meta_apply<meta_quote<std::tuple>,
typename sort<typelist<T...>>::type
>::type;
{
using base_tuple = meta_apply<...>;
};
Now just add overloads for get<> on my_tuple<T...>:
template <size_t I, typename... T>
auto get(my_tuple<T...>& t) {
using type = std::tuple_element_t<I, std::tuple<T...>>;
return std::get<type>(static_cast<typename my_tuple<T...>::base_type&>(t));
}
This is probably not the most efficient implementation (it uses cyclic permutation of types to determine the one with the largest size), and may contain errors, but the whole idea should be clear. The result is std::tuple with types ordered by descending size. The main function checks if it actually works (and it works on my gcc-4.8.2).
#include <iostream>
#include <tuple>
#include <iomanip>
constexpr std::size_t max (std::size_t x, std::size_t y)
{
return (x < y) ? y : x;
}
template <typename ... Ts>
struct max_size;
template < >
struct max_size < >
{
static constexpr std::size_t result = 0;
};
template <typename T, typename ... Ts>
struct max_size <T, Ts...>
{
static constexpr std::size_t result = max(sizeof(T), max_size<Ts...>::result);
};
template <typename R, typename ... Ts>
struct order_by_size_impl;
template <bool M, typename R, typename ... Ts>
struct order_by_size_helper;
template <typename ... Rs, typename T, typename ... Ts>
struct order_by_size_helper<true, std::tuple<Rs...>, T, Ts...>
: order_by_size_impl<std::tuple<Rs..., T>, Ts...>
{ };
template <typename ... Rs, typename T, typename ... Ts>
struct order_by_size_helper<false, std::tuple<Rs...>, T, Ts...>
: order_by_size_impl<std::tuple<Rs...>, Ts..., T>
{ };
template <typename ... Rs, typename T, typename ... Ts>
struct order_by_size_impl<std::tuple<Rs...>, T, Ts...>
: order_by_size_helper<sizeof(T) >= max_size<Ts...>::result, std::tuple<Rs...>, T, Ts...>
{ };
template <typename ... Rs>
struct order_by_size_impl<std::tuple<Rs...>>
{
typedef std::tuple<Rs...> result;
};
template <typename ... Ts>
struct order_by_size
: order_by_size_impl<std::tuple<>, Ts...>
{ };
struct test
{
std::uint8_t data[128];
};
template <std::size_t I, typename T, typename R>
bool check (R const & r)
{
return std::is_same<typename std::remove_cv<typename std::remove_reference<decltype(std::get<I>(r))>::type>::type, T>::value;
}
int main ( )
{
order_by_size<std::uint8_t, std::uint32_t, std::uint16_t, std::uint64_t, test>::result r;
std::cout << std::boolalpha;
std::cout << check<0, test>(r) << std::endl;
std::cout << check<1, std::uint64_t>(r) << std::endl;
std::cout << check<2, std::uint32_t>(r) << std::endl;
std::cout << check<3, std::uint16_t>(r) << std::endl;
std::cout << check<4, std::uint8_t>(r) << std::endl;
}

Extract template class default parameters

Is there a way to extract a template class' default parameters only knowing the unspecialized template class at compile time?
I know how to extract an instantiated template class' parameters, like this:
// Just an example class for the demonstration
template<class A, class B=void>
struct example {};
// Template parameters storage class
template<class...An>
struct args;
// MPL utility that extracts the template arguments from a template class
template<class C>
struct get_args
{
typedef args<> type;
};
template<template<class...> class C, class...An>
struct get_args< C<An...> >
{
typedef args<An...> type;
};
// And the assertion
static_assert(
std::is_same
< args<int,void>,
get_args< example<int> >::type
>::value,
"Check this out"
);
Now what I would like to know is if decltype or anything else could be used to retrieve the default template parameters only from the unspecialized template class:
// MPL utility that extract template default arguments from a template class
template<template<class...> class C>
struct get_default_args
{
typedef /* what goes here? */ type;
};
// And the assertion
static_assert(
std::is_same
< args<void>,
get_default_args< example >::type
>::value,
"Check this out"
);
For now, I only figured out how to extract the number of parameters of a template class, not their default value:
namespace detail {
template< template<class> class >
boost::mpl::size_t<1> deduce_nb_args();
template< template<class,class> class >
boost::mpl::size_t<2> deduce_nb_args();
template< template<class,class,class> class >
boost::mpl::size_t<3> deduce_nb_args();
/* ... and so on ... */
}
// MPL utility that extract the number of template arguments of a template class
template<template<class...> class C>
struct get_nb_args :
decltype(detail::deduce_nb_args<C>()) {};
// And the assertion
static_assert(
get_nb_args< example >::value == 2,
"Check this out"
);
Edit
It seems that at the end, and once again, MSVC prevents me to perform this operation.
Something like the following makes the compiler crash with a fatal error C1001: An internal error has occurred in the compiler.
template<template<class...> class D> static
boost::boost::mpl::int_<0> mandatory(D<>*)
{ return boost::boost::mpl::int_<0>(); }
template<template<class...> class D> static
boost::mpl::int_<1> mandatory(D<void>*)
{ return boost::mpl::int_<0>(); }
template<template<class...> class D> static
boost::mpl::int_<2> mandatory(D<void,void>*)
{ return boost::mpl::int_<0>(); }
template<template<typename...> class D> static
boost::mpl::int_<-1> mandatory(...)
{ return boost::mpl::int_<-1>(); }
int check()
{
return mandatory<example>(nullptr);
}
Trying next one results in error C2976: 'D' : too few template arguments
template<template<class,class> class D> static
boost::mpl::int_<0> mandatory2(D<>*)
{ return boost::mpl::int_<0>(); }
template<template<class,class> class D> static
boost::mpl::int_<1> mandatory2(D<void>*)
{ return boost::mpl::int_<0>(); }
template<template<class,class> class D> static
boost::mpl::int_<2> mandatory2(D<void,void>*)
{ return boost::mpl::int_<0>(); }
int check2()
{
return mandatory2<example>(nullptr);
}
So to me it seems that no matter the approach, MSVC forbids programmatic instantiation of a template class making use of default parameters.
In turn, it looks impossible to me to use a SFINAE technique to extract:
1. the mandatory number of parameters;
2. the types of default parameters.
Edit 2
Ok, after several tests it seems to be a bug with MSVC occurring when trying to programmatically instantiate a template class only using default arguments.
I filed a bug report here and another one here.
Here is a traits class allowing to check if a class is instantiable using given template parameters that does not make the compiler crash, but do not evaluates to true for fully default instantiable classes.
namespace detail {
typedef std::true_type true_;
typedef std::false_type false_;
template< template<class...> class D, class...An >
true_ instantiable_test(D<An...>*);
template< template<class...> class D, class...An >
false_ instantiable_test(...);
}
template< template<class...> class C, class...An >
struct is_instantiable : decltype(detail::instantiable_test<C,An...>(nullptr)) {};
That being said, it seems impossible with MSVC to retrieve the template type instantiated with default parameters. Typically the following does not compile:
template< template<class...> class T, class...An >
struct get_default_v0
{
typedef T<An...> type;
};
namespace detail {
template< template<class...> class T, class...An >
T<An...> try_instantiate();
} // namespace detail
template< template<class...> class T, class...An >
struct get_default_v1
{
typedef decltype(detail::try_instantiate<T,An...>()) type;
};
// C2976
static_assert(
std::is_same< get_default_v0<example,int> , example<int,void> >::value,
"v0"
);
// C2976
static_assert(
std::is_same< get_default_v1<example,int> , example<int,void> >::value,
"v1"
);
I'd try something like this:
template <typename ...> struct Get2;
template <template <typename...> class Tmpl,
typename A, typename B, typename ...Rest>
struct Get2<Tmpl<A, B, Rest...>>
{
using type = B;
};
template <template <typename...> class Tmpl> struct GetDefault2
{
using type = typename Get2<Tmpl<void>>::type;
};
I realize this is a long answer, but here is a possible approach:
#include <type_traits>
namespace tmpl
{
namespace detail
{
template<template<typename...> class C, typename... T>
struct is_valid_specialization_impl
{
template<template<typename...> class D>
static std::true_type test(D<T...>*);
template<template<typename...> class D>
static std::false_type test(...);
using type = decltype(test<C>(0));
};
} // namespace detail
template<template<typename...> class C, typename... T>
using is_valid_specialization = typename detail::is_valid_specialization_impl<C, T...>::type;
} // namespace tmpl
The following is a partial copy/paste from my github repository, dont worry too much about it, most of the code is to find the minimum/maximum number of template arguments required (in this case we only care about the minimum number):
#if !defined(TEMPLATE_ARGS_MAX_RECURSION)
#define TEMPLATE_ARGS_MAX_RECURSION 30
#endif
namespace tmpl
{
namespace detail
{
enum class specialization_state {
invalid,
valid,
invalid_again
};
template<bool, template<typename...> class C, typename... T>
struct num_arguments_min
: std::integral_constant<int, sizeof...(T)>
{ };
template<template<typename...> class C, typename... T>
struct num_arguments_min<false, C, T...>
: num_arguments_min<is_valid_specialization<C, T..., char>::value, C, T..., char>
{ };
template<specialization_state, template<typename...> class C, typename... T>
struct num_arguments_max;
template<template<typename...> class C, typename... T>
struct num_arguments_max<specialization_state::invalid, C, T...>
: num_arguments_max<
is_valid_specialization<C, T..., char>::value
? specialization_state::valid
: specialization_state::invalid,
C,
T..., char
>
{ };
template<template<typename...> class C, typename... T>
struct num_arguments_max<specialization_state::valid, C, T...>
: std::conditional<
((sizeof...(T) == 0) || (sizeof...(T) == TEMPLATE_ARGS_MAX_RECURSION)),
std::integral_constant<int, -1>,
num_arguments_max<
is_valid_specialization<C, T..., char>::value
? specialization_state::valid
: specialization_state::invalid_again,
C,
T..., char
>
>::type
{ };
template<template<typename...> class C, typename... T>
struct num_arguments_max<specialization_state::invalid_again, C, T...>
: std::integral_constant<int, (sizeof...(T) - 1)>
{ };
} // namespace detail
template<template<typename...> class C>
struct template_traits
{
constexpr static int args_min = detail::num_arguments_min<is_valid_specialization<C>::value, C>::value;
constexpr static int args_max = detail::num_arguments_max<is_valid_specialization<C>::value
? detail::specialization_state::valid
: detail::specialization_state::invalid,
C>::value;
constexpr static bool is_variadic = (args_max < args_min);
template<typename... T>
using specializable_with = is_valid_specialization<C, T...>;
};
} // namespace tmpl
Some helper types specifically for your question:
template<typename... Ts>
struct type_sequence { };
namespace detail
{
template<int N, typename...>
struct skip_n_types;
template<int N, typename H, typename... Tail>
struct skip_n_types<N, H, Tail...>
: skip_n_types<(N - 1), Tail...> { };
template<typename H, typename... Tail>
struct skip_n_types<0, H, Tail...>
{
using type = type_sequence<H, Tail...>;
};
} // namespace detail
template<int N, typename... T>
using skip_n_types = typename detail::skip_n_types<N, T...>::type;
namespace detail
{
template<typename T>
struct get_default_args;
template<template<typename...> class T, typename... A>
struct get_default_args<T<A...> >
{
using type = typename skip_n_types<
tmpl::template_traits<T>::args_min,
A...>::type;
};
} // namespace detail
template<typename T>
using get_default_args = typename detail::get_default_args<T>::type;
To put it all together:
template<typename>
struct dependant { };
template<typename T, typename U = void>
struct example { };
template<typename T, typename U = dependant<T> >
struct dependant_example { };
template<typename T>
void print_type(T)
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
int main(int argc, char** argv)
{
{
using example_type = example<int>;
using default_args = get_default_args<example_type>;
print_type(example_type{});
print_type(default_args{});
}
{
using example_type = dependant_example<int>;
using default_args = get_default_args<example_type>;
print_type(example_type{});
print_type(default_args{});
}
}
Output:
void print_type(T) [T = example<int, void>]
void print_type(T) [T = type_sequence<void>]
void print_type(T) [T = dependant_example<int, dependant<int> >]
void print_type(T) [T = type_sequence<dependant<int> >]

Avoiding creating a variable in this template code

I have a template:
template<typename... Ts> //T1,T2,T3,...
struct foo {
//my struct
};
I want to do static_assert checks on T1,T3,T5,... (the "odd types") and on T2,T4,T6,... (the "even types") separately.
I have found this simple solution:
template<size_t N, typename... Ts>
struct perform_checks {};
template<size_t N, typename T, typename U, typename... Ts>
struct perform_checks<N, T, U, Ts...> : perform_checks<N, Ts...>
{
//check for odd types
static_assert(std::is_default_constructible<T>::value,"failure");
//check for even types
static_assert(std::is_copy_constructible<U>::value,"failure");
};
The N parameter allows it to end. I use it like this:
template<typename... Ts>
struct foo {
perform_checks<0,Ts...> hello;
};
This seems to be working fine. But is it possible to avoid the hello variable? I never use it for any other purpose.
Derive foo from perform_checks<> privately:
template <typename... Ts> struct foo : private perform_checks<Ts...> {
// stuff
};
Oh, and get rid of the N parameter that you don't need:
template <typename... Ts> struct perform_checks {};
template <typename T> struct perform_checks<T> {
template <typename U> struct dependent_name_hack : std::false_type {};
static_assert(dependent_name_hack<T>::value,
"Odd number of parameters not acceptable.");
};
template <typename T, typename U, typename... Ts>
struct perform_checks<T, U, Ts...> : perform_checks<Ts...> {
//check for odd types
static_assert(std::is_default_constructible<T>::value,"failure");
//check for even types
static_assert(std::is_copy_constructible<U>::value,"failure");
};
You can use enable_if1 (and boost::mpl) in more-or-less the following way:
#include <boost/mpl/and.hpp>
template<size_t N, typename... Ts>
struct perform_checks {};
template<size_t N, typename T, typename U, typename... Ts>
struct perform_checks<N, T, U, Ts...> : perform_checks<N, Ts...>
{
typedef boost::mpl::and_<std::is_default_constructible<T>::type,
std::is_copy_constructible<U>::type> type;
};
template < class... Ts,
class = typename std::enable_if<perform_checks<0, Ts...>::type>
struct foo {
//my struct
};
The only purpose of foo in the OP is triggering the check when it's instantiated. That's why you need the variable hello: it's an instantiation of foo.
I would rather follow the approach of traits in <type_traits>. More precisely, I would turn perform_checks into class (or struct) that has public static constexpt bool member called value which is true or false depending on whether the given types pass the test or not. Then I would use a single static_assert to stop compilation if value is false.
My solution, which assumes that the number of template type arguments is even, follows:
#include <type_traits>
template<typename First, typename Second, typename... Others>
struct perform_checks :
std::integral_constant<bool,
perform_checks<First, Second>::value && // Checks First and Second
perform_checks<Others...>::value // Recursively "calls" itself on Others
> {
};
// This specialization finishes the recursion and effectively performs the test
template<typename First, typename Second>
struct perform_checks<First, Second> :
std::integral_constant<bool,
std::is_default_constructible<First>::value && // Checks First
std::is_copy_constructible<Second>::value // Checks Second
> {
};
Here is a simple test:
struct NonDefaultConstructible {
NonDefaultConstructible() = delete;
};
struct NonCopyConstructible {
NonCopyConstructible(const NonCopyConstructible&) = delete;
};
int main() {
static_assert(perform_checks<int, double>::value, "Failure");
static_assert(perform_checks<int, int, double, double>::value, "Failure");
static_assert(!perform_checks<NonDefaultConstructible, int>::value, "Failure");
static_assert(!perform_checks<int, NonCopyConstructible>::value, "Failure");
static_assert(!perform_checks<int, int, double, NonCopyConstructible>::value, "Failure");
}
Notice that no variable was created.

How to create the Cartesian product of a type list?

I'd like to create the cross product of a list of types using variadic templates.
Here's what I have so far:
#include <iostream>
#include <typeinfo>
#include <cxxabi.h>
template<typename...> struct type_list {};
template<typename T1, typename T2> struct type_pair {};
template<typename T, typename... Rest>
struct row
{
typedef type_list<type_pair<T,Rest>...> type;
};
template<typename... T>
struct cross_product
{
typedef type_list<typename row<T,T...>::type...> type;
};
int main()
{
int s;
typedef cross_product<int, float, short>::type result;
std::cout << abi::__cxa_demangle(typeid(result).name(), 0, 0, &s) << std::endl;
return 0;
}
This program outputs:
$ g++ -std=c++0x cross_product.cpp ; ./a.out
type_list<type_list<type_pair<int, int>, type_pair<int, float>, type_pair<int, short> >, type_list<type_pair<float, int>, type_pair<float, float>, type_pair<float, short> >, type_list<type_pair<short, int>, type_pair<short, float>, type_pair<short, short> > >
But I'd like it to output:
type_list<type_pair<int,int>, type_pair<int,float>, type_pair<int,short>, type_pair<float,int>,...>
That is, without the nested type_lists.
Is there a direct way to do this without the row helper, or should the solution "unwrap" the nested type_lists somehow?
A nice clean version I think:
cross_product.cpp:
#include "type_printer.hpp"
#include <iostream>
template<typename ...Ts> struct type_list {};
template<typename T1, typename T2> struct pair {};
// Concatenation
template <typename ... T> struct concat;
template <typename ... Ts, typename ... Us>
struct concat<type_list<Ts...>, type_list<Us...>>
{
typedef type_list<Ts..., Us...> type;
};
// Cross Product
template <typename T, typename U> struct cross_product;
// Partially specialise the empty case for the first type_list.
template <typename ...Us>
struct cross_product<type_list<>, type_list<Us...>> {
typedef type_list<> type;
};
// The general case for two type_lists. Process:
// 1. Expand out the head of the first type_list with the full second type_list.
// 2. Recurse the tail of the first type_list.
// 3. Concatenate the two type_lists.
template <typename T, typename ...Ts, typename ...Us>
struct cross_product<type_list<T, Ts...>, type_list<Us...>> {
typedef typename concat<
type_list<pair<T, Us>...>,
typename cross_product<type_list<Ts...>, type_list<Us...>>::type
>::type type;
};
struct A {};
struct B {};
struct C {};
struct D {};
struct E {};
struct F {};
template <typename T, typename U>
void test()
{
std::cout << print_type<T>() << " \u2a2f " << print_type<U>() << " = "
<< print_type<typename cross_product<T, U>::type>() << std::endl;
}
int main()
{
std::cout << "Cartesian product of type lists\n";
test<type_list<>, type_list<>>();
test<type_list<>, type_list<A>>();
test<type_list<>, type_list<A, B>>();
test<type_list<A, B>, type_list<>>();
test<type_list<A>, type_list<B>>();
test<type_list<A>, type_list<B, C, D>>();
test<type_list<A, B>, type_list<B, C, D>>();
test<type_list<A, B, C>, type_list<D>>();
test<type_list<A, B, C>, type_list<D, E, F>>();
return 0;
}
type_printer.hpp:
#ifndef TYPE_PRINTER_HPP
#define TYPE_PRINTER_HPP
#include "detail/type_printer_detail.hpp"
template <typename T>
std::string print_type()
{
return detail::type_printer<T>()();
}
#endif
detail/type_printer_detail.hpp:
#ifndef DETAIL__TYPE_PRINTER_DETAIL_HPP
#define DETAIL__TYPE_PRINTER_DETAIL_HPP
#include <typeinfo>
#include <cxxabi.h>
#include <string>
template <typename ...Ts> struct type_list;
template <typename T1, typename T2> struct pair;
namespace detail {
// print scalar types
template <typename T>
struct type_printer {
std::string operator()() const {
int s;
return abi::__cxa_demangle(typeid(T).name(), 0, 0, &s);
}
};
// print pair<T, U> types
template <typename T, typename U>
struct type_printer<pair<T, U>> {
std::string operator()() const {
return "(" + type_printer<T>()() + "," + type_printer<U>()() + ")";
}
};
// print type_list<T>
template <>
struct type_printer<type_list<>> {
std::string operator()() const {
return "\u2205";
}
};
template <typename T>
struct type_printer<type_list<T>> {
std::string operator()() const {
return "{" + type_printer<T>()() + "}";
}
std::string operator()(const std::string& sep) const {
return sep + type_printer<T>()();
}
};
template <typename T, typename ...Ts>
struct type_printer<type_list<T, Ts...>> {
std::string operator()() const {
return "{" + type_printer<T>()() + type_printer<type_list<Ts...>>()(std::string(", ")) + "}";
}
std::string operator()(const std::string& sep) const {
return sep + type_printer<T>()() + type_printer<type_list<Ts...>>()(sep);
}
};
}
#endif
Run:
g++ -std=c++0x cross_product.cpp && ./a.out
Output:
Cartesian product of type lists
∅ ⨯ ∅ = ∅
∅ ⨯ {A} = ∅
∅ ⨯ {A, B} = ∅
{A, B} ⨯ ∅ = ∅
{A} ⨯ {B} = {(A,B)}
{A} ⨯ {B, C, D} = {(A,B), (A,C), (A,D)}
{A, B} ⨯ {B, C, D} = {(A,B), (A,C), (A,D), (B,B), (B,C), (B,D)}
{A, B, C} ⨯ {D} = {(A,D), (B,D), (C,D)}
{A, B, C} ⨯ {D, E, F} = {(A,D), (A,E), (A,F), (B,D), (B,E), (B,F), (C,D), (C,E), (C,F)}
(I noticed on Windows using Chrome that the cross product unicode character is not coming out well. Sorry, I don't know how to fix that.)
Somehow my brain is fried: I think I'm using more code than is needed but, at least, it has the desired results (although I didn't fix the memory leak):
#include <iostream>
#include <typeinfo>
#include <cxxabi.h>
template<typename...> struct type_list {};
template<typename T1, typename T2> struct type_pair {};
template<typename T, typename... Rest>
struct row
{
typedef type_list<type_pair<T,Rest>...> type;
};
template <typename... T> struct concat;
template <typename... S, typename... T>
struct concat<type_list<S...>, type_list<T...>>
{
typedef type_list<S..., T...> type;
};
template <typename... T>
struct expand
{
typedef type_list<T...> type;
};
template <> struct expand<> { typedef type_list<> type; };
template <typename... T, typename... L>
struct expand<type_list<T...>, L...>
{
typedef typename concat<typename expand<T...>::type, typename expand<L...>::type>::type type;
};
template<typename... T>
struct cross_product
{
typedef typename expand<type_list<typename row<T,T...>::type...>>::type type;
};
int main()
{
int s;
typedef cross_product<int, float, short>::type result;
std::cout << abi::__cxa_demangle(typeid(result).name(), 0, 0, &s) << std::endl;
return 0;
}
Maybe something like this:
template <typename ...Args> struct typelist { };
template <typename S, typename T> struct typelist_cat;
template <typename ...Ss, typename ...Ts>
struct typelist_cat<typelist<Ss...>, typelist<Ts...>>
{
typedef typelist<Ss..., Ts...> type;
};
template <typename S, typename T> struct product;
template <typename S, typename ...Ss, typename ...Ts>
struct product<typelist<S, Ss...>, typelist<Ts...>>
{
// the cartesian product of {S} and {Ts...}
// is a list of pairs -- here: a typelist of 2-element typelists
typedef typelist<typelist<S, Ts>...> S_cross_Ts;
// the cartesian product of {Ss...} and {Ts...} (computed recursively)
typedef typename product<typelist<Ss...>, typelist<Ts...>>::type
Ss_cross_Ts;
// concatenate both products
typedef typename typelist_cat<S_cross_Ts, Ss_cross_Ts>::type type;
};
// end the recursion
template <typename ...Ts>
struct product<typelist<>, typelist<Ts...>>
{
typedef typelist<> type;
};
Now you should be able to use product<typelist<A,B,C>, typelist<D,E,F>>::type.
C++17
Working Demo
Logic to concatenate type_lists to avoid nested type_list like you are asking for:
// base case: 2 type_lists
template<class... Ts, class... Us>
auto concat(type_list<Ts...>, type_list<Us...>) -> type_list<Ts..., Us...>;
// recursive case: more than 2 type_lists
template<class... Ts, class... Us, class... Rest>
auto concat(type_list<Ts...>, type_list<Us...>, Rest...) -> decltype(concat(type_list<Ts..., Us...>{}, Rest{}...));
Note that these functions don't have (or need) implementations; this is a trick to avoid class template specialization (I learned it from Hana Dusikova's compile time regular expressions)
Then, simplifying your row and cross_product impls as pairs and cross_product_impl, respectively:
template<class T, class... Ts>
using pairs = type_list<type_pair<T, Ts>...>;
template<class... T>
auto cross_product_impl()
{
if constexpr(sizeof...(T) == 0)
return type_list<> {};
if constexpr(sizeof...(T) == 1)
return type_list<type_pair<T, T>...>{};
if constexpr(sizeof...(T) > 1)
return concat(pairs<T, T...>{}...);
}
if constexpr allows us to more easily express the logic, I think.
Finally a type alias for cross_product that gives us what the type would be if we theoretically invoked cross_product_impl:
template<class... T>
using cross_product = decltype(cross_product_impl<T...>());
Usage basically the same as before:
cross_product<int, float, short> result;
So far all solutions have drawbacks, unnecessary dependencies, unnecessary helpers and all are restricted to the Cartesian power of two. The following solution has no such drawbacks and supports:
Any cartesian power including 0.
Returning the empty set if any of the factors is an empty set.
The code is self contained and does not depend on any include files.
The inputs of the function can be of any template type.
The type of the output list can be specified via the first template
parameter.
It was actually to harder to implement (but good as homework) then I thought. I am actually thinking about creating a little generator which allows me an extended template syntax which makes these things really easy.
Simplified the code works as follows: product converts an input list tuple<A...>,tuple<B...>,tuple<C...> into tuple<tuple<A>...>, tuple<B...>, tuple<C...>. This second list is then passed to product_helper which does the recursive Cartesian product computation.
template <typename... T> struct cat2;
template <template<typename...> class R, typename... As, typename... Bs>
struct cat2 <R<As...>, R<Bs...> > {
using type = R <As..., Bs...>;
};
template <typename... Ts> struct product_helper;
template <template<typename...> class R, typename... Ts>
struct product_helper < R<Ts...> > { // stop condition
using type = R< Ts...>;
};
template <template<typename...> class R, typename... Ts>
struct product_helper < R<R<> >, Ts... > { // catches first empty tuple
using type = R<>;
};
template <template<typename...> class R, typename... Ts, typename... Rests>
struct product_helper < R<Ts...>, R<>, Rests... > { // catches any empty tuple except first
using type = R<>;
};
template <template<typename...> class R, typename... X, typename H, typename... Rests>
struct product_helper < R<X...>, R<H>, Rests... > {
using type1 = R <typename cat2<X,R<H> >::type...>;
using type = typename product_helper<type1, Rests...>::type;
};
template <template<typename...> class R, typename... X, template<typename...> class Head, typename T, typename... Ts, typename... Rests>
struct product_helper < R<X...>, Head<T, Ts...>, Rests... > {
using type1 = R <typename cat2<X,R<T> >::type...>;
using type2 = typename product_helper<R<X...> , R<Ts...> >::type;
using type3 = typename cat2<type1,type2>::type;
using type = typename product_helper<type3, Rests...>::type;
};
template <template<typename...> class R, typename... Ts> struct product;
template <template<typename...> class R>
struct product < R > { // no input, R specifies the return type
using type = R<>;
};
template <template<typename...> class R, template<typename...> class Head, typename... Ts, typename... Tail>
struct product <R, Head<Ts...>, Tail... > { // R is the return type, Head<A...> is the first input list
using type = typename product_helper< R<R<Ts>...>, Tail... >::type;
};
Here is a compilable example of how the code can be used.
Here's another solution.
#include <iostream>
#include <typeinfo>
#include <cxxabi.h>
template <typename ...Args> struct typelist { };
template <typename, typename> struct typepair { };
template <typename S, typename T> struct product;
template <typename S, typename T> struct append;
template<typename ...Ss, typename ...Ts>
struct append<typelist<Ss...>, typelist<Ts...>> {
typedef typelist<Ss..., Ts...> type;
};
template<>
struct product<typelist<>, typelist<>> {
typedef typelist<> type;
};
template<typename ...Ts>
struct product<typelist<>, typelist<Ts...>> {
typedef typelist<> type;
};
template<typename ...Ts>
struct product<typelist<Ts...>, typelist<>> {
typedef typelist<> type;
};
template<typename S, typename T, typename ...Ss, typename ...Ts>
struct product<typelist<S, Ss...>, typelist<T, Ts...>> {
typedef typename
append<typelist<typepair<S, T>,
typepair<S, Ts>...,
typepair<Ss, T>...>,
typename product<typelist<Ss...>, typelist<Ts...>>::type>::type type;
};
int main(void)
{
int s;
std::cout << abi::__cxa_demangle(
typeid(product<typelist<int, float>, typelist<short, double>>::type).name(), 0, 0, &s) << "\n";
return 0;
}
Note: This is NOT what the OP asked for... but may be of relevance to others (like me) who stumble upon this question. Here is how it can be done using a Loki::TypeList (i.e. prior C++-11), perhaps of historical interest or for compatability sake.
Also, perhaps it is presumptuous of me to pollute loki's namespace. YMMV.
crossproduct.h
#include "loki/NullType.h"
#include "loki/Typelist.h"
namespace Loki {
namespace TL {
/// a pair of two types
template <typename A_t, typename B_t>
struct TypePair
{
typedef A_t A;
typedef B_t B;
};
/// a template which takes one type and pairs it with all other types
/// in another typelist
template <class T, class TList > struct DistributePair;
/// specialization of Distribute for the nulltype
template < class TList >
struct DistributePair< NullType, TList >
{
typedef NullType type;
};
/// specialization of Distribute where the second parameter is nulltype
template <class T >
struct DistributePair< T, NullType >
{
typedef NullType type;
};
/// specialization of Distribute where the first parameter is a
/// typelist
template <class T, class Head, class Tail >
struct DistributePair< T, Typelist<Head,Tail> >
{
typedef Typelist<
TypePair<T,Head>,
typename DistributePair<T,Tail>::type
> type;
};
/// performs cartesion product of two typelists
template <class TListA, class TListB> struct CrossProduct;
/// specialization of CrossProduct for NullType
template <class TListB>
struct CrossProduct< NullType, TListB >
{
typedef NullType type;
};
/// specialization of CrossProduct for recursion
template <class Head, class Tail, class TListB>
struct CrossProduct< Typelist<Head,Tail>, TListB >
{
typedef typename Append<
typename DistributePair< Head,TListB >::type,
typename CrossProduct< Tail, TListB >::type
>::Result type;
};
} // namespace TL
} // namespace Loki
test.cpp
#include <crossproduct.h>
#include <loki/HierarchyGenerators.h>
#include <iostream>
struct A{};
struct B{};
struct C{};
struct D{};
struct E{};
struct F{};
typedef LOKI_TYPELIST_3(A,B,C) TypeListA_t;
typedef LOKI_TYPELIST_3(D,E,F) TypeListB_t;
typedef typename Loki::TL::CrossProduct< TypeListA_t, TypeListB_t >::type Cross_t;
template <typename T> const char* toString();
template <> const char* toString<A>(){ return "A"; };
template <> const char* toString<B>(){ return "B"; };
template <> const char* toString<C>(){ return "C"; };
template <> const char* toString<D>(){ return "D"; };
template <> const char* toString<E>(){ return "E"; };
template <> const char* toString<F>(){ return "F"; };
template <typename T> struct Printer
{
Printer()
{
std::cout << toString<T>() << ", ";
}
};
template <typename T1, typename T2>
struct Printer< Loki::TL::TypePair<T1,T2> >
{
Printer()
{
std::cout << "(" << toString<T1>() << "," << toString<T2>() << "), ";
}
};
typedef Loki::GenScatterHierarchy< TypeListA_t, Printer > PrinterA_t;
typedef Loki::GenScatterHierarchy< TypeListB_t, Printer > PrinterB_t;
typedef Loki::GenScatterHierarchy< Cross_t, Printer > PrinterCross_t;
int main(int argc, char** argv)
{
std::cout << "\nType list A: \n ";
PrinterA_t a;
std::cout << "\nType list B: \n ";
PrinterB_t b;
std::cout << "\nType list Cross: \n ";
PrinterCross_t cross;
return 0;
}
output
Type list A:
A, B, C,
Type list B:
D, E, F,
Type list Cross:
(A,D), (A,E), (A,F), (B,D), (B,E), (B,F), (C,D), (C,E), (C,F),
With Boost.Mp11, this is a short one-liner (as always):
using input = type_list<int, float, short>;
using result = mp_product<
type_pair,
input, input>;
Demo.
We can generalize this to picking N things, with repetition, from that input. We can't use type_pair anymore to group our elements, so we'll just have a list of type_list of elements. To do that, we basically need to write:
mp_product<type_list, input, input, ..., input>
// ~~~~~~~ N times ~~~~~~~~
Which is also the same as:
mp_product_q<mp_quote<type_list>, input, input, ..., input>
// ~~~~~~~ N times ~~~~~~~~
One way to do that is:
template <int N>
using product = mp_apply<
mp_product_q,
mp_append<
mp_list<mp_quote<type_list>>,
mp_repeat_c<mp_list<input>, N>
>>;
Demo.
Really enjoyed this "homework" assignment :)
Both solutions below create a class full of type_list typedefs, along with member functions that will check to see if a given list of types exist in the class as a type_list.
The first solution creates all possible combinations of types from 1 to N types per type_list (the width parameter defines N). The second solution creates only pairs of types.
First Solution
template<typename... Ts> struct type_list { typedef type_list<Ts...> type; };
template<size_t, typename...> struct xprod_tlist_ {};
template<typename... Ts, typename... Us>
struct xprod_tlist_<1, type_list<Ts...>, Us...> {};
template<size_t width, typename... Ts, typename... Us>
struct xprod_tlist_<width, type_list<Ts...>, Us...>
: type_list<Ts..., Us>...
, xprod_tlist_<width - 1, type_list<Ts..., Us>, Us...>... {};
template<size_t width, typename... Ts> struct xprod_tlist
: type_list<Ts>..., xprod_tlist_<width, type_list<Ts>, Ts...>... {
template<typename... Us> struct exists
: std::is_base_of<type_list<Us...>, xprod_tlist<width, Ts...>> {};
template<typename... Us> struct assert_exists {
static_assert(exists<Us...>::value, "Type not present in list");
};
};
Usage:
typedef xprod_tlist<5, int, char, string, float, double, long> X;
//these pass
X::assert_exists<int, int, int, int, int> assert_test1;
X::assert_exists<double, float, char, int, string> assert_test2;
//these fail
X::assert_exists<char, char, char, char, char, char> assert_test3;
X::assert_exists<int, bool> assert_test4;
//true
auto test1 = X::exists<int, int, int, int, int>::value;
auto test2 = X::exists<double, float, char, int, string>::value;
//false
auto test3 = X::exists<char, char, char, char, char, char>::value;
auto test4 = X::exists<int, bool>::value;
Second Solution
template<class T, class U> struct type_pair { typedef type_pair<T, U> type; };
template<class... Ts> struct type_list {};
template<class...> struct xprod_tlist_ {};
template<class T, class... Ts, class... Us>
struct xprod_tlist_<type_list<T, Ts...>, type_list<Us...>>
: type_pair<T, Us>..., xprod_tlist_<type_list<Ts...>, type_list<Us...>> {};
template<class... Ts>
struct xprod_tlist : xprod_tlist_<type_list<Ts...>, type_list<Ts...>> {
template<class T, class U> struct exists
: std::is_base_of<type_pair<T, U>, xprod_tlist<Ts...>> {};
template<class T, class U> struct assert_exists {
static_assert(exists<T, U>::value, "Type not present in list");
};
};
Usage:
typedef xprod_tlist<int, float, string> X;
//these pass
X::assert_exists<int, int> assert_test1;
X::assert_exists<int, float> assert_test2;
X::assert_exists<int, string> assert_test3;
X::assert_exists<float, int> assert_test4;
X::assert_exists<float, float> assert_test5;
X::assert_exists<float, string> assert_test6;
X::assert_exists<string, int> assert_test7;
X::assert_exists<string, float> assert_test8;
X::assert_exists<string, string> assert_test9;
//this fails
X::assert_exists<int, char> assert_test10;
//true
auto test1 = X::exists<int, int>::value;
auto test2 = X::exists<int, float>::value;
auto test3 = X::exists<int, string>::value;
auto test4 = X::exists<float, int>::value;
auto test5 = X::exists<float, float>::value;
auto test6 = X::exists<float, string>::value;
auto test7 = X::exists<string, int>::value;
auto test8 = X::exists<string, float>::value;
auto test9 = X::exists<string, string>::value;
//false
auto test10 = X::exists<int, char>::value;

Select every even (or odd) argument in template parameter pack

I would like to allow use of the class I'm writing to specify as a template parameters a list of types along with a list of allocators of those types in a manner that types are at odd positions and allocators are at even ones:
template<typename... T>
class MyClass {
// Stuff inside
}
int main() {
MyClass<SomeType1, AllocatorOfSomeType1> c1;
MyClass<SomeType1, AllocatorOfSomeType1,
SomeType2, AllocatorOfSomeType2> c2;
MyClass<SomeType1, AllocatorOfSomeType1,
SomeType2, AllocatorOfSomeType2,
SomeType3, AllocatorOfSomeType3> c3;
// And so on....
}
Internally it would make sense to have a tuple of vectors of types for storage:
std::tuple<std::vector<EveryOddTypeInParameterPack>...> m_storage_;
and a tuple of allocators for usage:
std::tuple<std::vector<EveryEvenTypeInParameterPack>...> m_storage_;
How can I actually declare those tuples in code? In theory I need to somehow select every odd/even type in parameter pack - is that possible?
Though the code got a little lengthy, I suppose the mechanism doesn't have
unnecessary peculiarities.
If I understand the question correctly,
probably the following code will meet the purpose:
// push front for tuple
template< class, class > struct PFT;
template< class A, class... T > struct PFT< A, tuple< T... > > {
typedef tuple< A, T... > type;
};
// for even
template< class... > struct even_tuple;
template< class A, class B > struct even_tuple< A, B > {
typedef tuple< A > type;
};
template< class A, class B, class... T > struct even_tuple< A, B, T... > {
typedef typename PFT< A, typename even_tuple< T... >::type >::type type;
};
// As for odd elements, in the same way as even(please see the test on ideone)
// objective type
template< class > struct storage_type;
template< class... T > struct storage_type< tuple< T... > > {
typedef tuple< vector< T >... > type;
};
template< class... T >
struct MyClass {
typename storage_type< typename even_tuple< T... >::type >::type
m_storage_even_;
typename storage_type< typename odd_tuple< T... >::type >::type
m_storage_odd_;
};
Here is a test on ideone.
Perhaps something like this:
#include <tuple>
// Example receptacle
template <typename ...Args> struct MyContainer;
// Tuple concatenator
template<typename PackR, typename PackL> struct cat;
template<typename ...R, typename ...L>
struct cat<std::tuple<R...>, std::tuple<L...>>
{
typedef std::tuple<R..., L...> type;
};
// Even/Odd extractors
template <typename ...Args> struct GetEven;
template <typename ...Args> struct GetOdd;
template <typename E1, typename O1, typename ...Args>
struct GetEven<E1, O1, Args...>
{
typedef typename cat<std::tuple<E1>, typename GetEven<Args...>::value>::type value;
};
template <typename E1, typename O1>
struct GetEven<E1, O1>
{
typedef std::tuple<E1> value;
};
template <typename E1, typename O1, typename ...Args>
struct GetOdd<E1, O1, Args...>
{
typedef typename cat<std::tuple<O1>, typename GetEven<Args...>::value>::type value;
};
template <typename E1, typename O1>
struct GetOdd<E1, O1>
{
typedef std::tuple<O1> value;
};
// Tuple-to-Receptacle mover
template <typename Pack, template <typename ...T> class Receiver> struct Unpack;
template <typename ...Args, template <typename ...T> class Receiver>
struct Unpack<std::tuple<Args...>, Receiver>
{
typedef Receiver<Args...> type;
};
// Example consumer
template <typename ...Args>
struct Foo
{
typedef typename Unpack<typename GetEven<Args...>::value, MyContainer>::type EvenVector;
typedef typename Unpack<typename GetOdd<Args...>::value, MyContainer>::type OddVector;
EvenVector x;
OddVector y;
};
You still have to define your MyContainer class to do something useful with the variadic parameters, e.g. implement your tuple of vectors... (why not a vector of tuples, though?)
Credits to brunocodutra for the tuple trick.
this is just a try
template<typename... T> class Myclass;
template<typename T1, typename allocT1>
class MyClass <T1, allocT1> {
std::pair<T1, allocT1> myFirstArglist;
//and you have to do a check that allocT1::value_type is same as T1 or not
//or may be alloT1 is an allocator type or not(i'm thinking concepts, may be)
//this idea is inspired from Chris's comment
};
template<typename T1, typename allocT1, typename... T>
class Myclass<T1, allocT1, T...> {
std::pair<T1, allocT1> myFirstArglist;
Myclass<T>; //something like this
};
template<>
class Myclass<> {
//probably you would like some error message here
//when there are no types and containers
};
may be i'm not clear enough, you'd probably like to read
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2080.pdf
Also there is a good post related to design of allocator types... you would like to have a look at:
C++ Design Pattern for allocator type arguments
I know your question was originally tagged "c++11", but I figure it's worth pointing out for posterity that in C++14 you have access to make_index_sequence, and that makes the whole thing pretty simple. For filtering a tuple, I'd start with this outline: https://quuxplusone.github.io/blog/2018/07/23/metafilter/
And then we end up with something like this (Godbolt):
template<bool> struct zero_or_one {
template<class E> using type = std::tuple<E>;
};
template<> struct zero_or_one<false> {
template<class E> using type = std::tuple<>;
};
template<class Tuple, class = std::make_index_sequence<std::tuple_size<Tuple>::value>>
struct just_evens;
template<class... Es, size_t... Is>
struct just_evens<std::tuple<Es...>, std::index_sequence<Is...>> {
using type = decltype(std::tuple_cat(
std::declval<typename zero_or_one<Is % 2 == 0>::template type<Es>>()...
));
};
To get just_odds, you'd switch the condition from Is % 2 == 0 to Is % 2 != 0.
Example usage:
static_assert(std::is_same<
just_evens<std::tuple<char, short, int, long, double>>::type,
std::tuple<char, int, double>
>::value, "");