Is is possible to create a composite constructor without tuples? - c++

I know that I can expand a parameter pack of tuples onto a variadic template of base classes
like this:
#include <tuple>
struct ComponentA {
int foo;
int bar;
ComponentA(std::tuple<int, int> && pack):
foo(std::get<0>(pack)),
bar(std::get<1>(pack))
{}
};
struct ComponentB {
ComponentB(std::tuple<> && pack)
{}
};
template<typename ... Bases>
struct CompositeClass: public Bases... {
template<typename ... Arguments>
CompositeClass(Arguments &&... arguments):
Bases(std::forward<Arguments>(arguments))...
{}
};
int main() {
CompositeClass<ComponentA, ComponentB> composite{
std::make_tuple(100, 100),
std::make_tuple()
};
}
However, I find that syntax cumbersome. Is there a way to avoid the tuples altogether?
Edit:
Something more like this:
struct ComponentA {
int foo;
int bar;
ComponentA(int a, int b):
foo(a),
bar(b)
{}
};
struct ComponentB {
ComponentB()
{}
};
template<typename ... Bases>
struct CompositeClass: public Bases... {
template<typename ... ... Arguments>
CompositeClass(Arguments &&... ... arguments):
Bases(std::forward<Arguments>(arguments)...)...
{}
};
int main() {
CompositeClass<ComponentA, ComponentB> composite{100, 100};
}
Both parameters are passed to ComponentA, none to ComponentB.
Edit 2
So I have got something like this:
template <int ...>
struct SequenceContainer {};
template <int, typename>
struct AppendIntToSequence;
template <int Value, int ... Sequence>
struct AppendIntToSequence<Value, SequenceContainer<Sequence...>> {
typedef SequenceContainer<Sequence..., Value> type;
};
template<int End>
struct MakeSequence:
AppendIntToSequence<End - 1, typename MakeSequence<End - 1>::type> {};
template<>
struct MakeSequence<0> {
typedef SequenceContainer<> type;
};
struct ComponentA {
static const int parameters = 2;
ComponentA(int && a, int && b) {
std::cout << a << b << std::endl;
}
};
struct ComponentB {
static const int parameters = 1;
ComponentB(const char * string) {
std::cout << string << std::endl;
}
};
template <typename Base>
struct TupleConstructor: Base {
template <typename ... Arguments, int ... Sequence>
TupleConstructor(std::tuple<Arguments...> &&, SequenceContainer<Sequence...> const &);
};
template <typename Base>
template <typename ... Arguments, int... Sequence>
TupleConstructor<Base>::TupleConstructor(std::tuple<Arguments...> && arguments, SequenceContainer<Sequence...> const &):
Base(std::forward<Arguments>(std::get<Sequence>(arguments))...)
{}
template <typename ... Components>
struct CompositeClass: public TupleConstructor<Components>... {
template <typename ... Arguments>
CompositeClass(Arguments &&... arguments):
TupleConstructor<Components>(
std::forward<Arguments>(arguments),
typename MakeSequence<std::tuple_size<Arguments>::value>::type{}
)...
{}
};
int main()
{
CompositeClass<ComponentA, ComponentB> composite{
std::tuple<int &&, int &&>(100,100),
std::tuple<const char *>("Hello World!")
};
However, I haven't been able to figure out how to remove the two tuples from the CompositeClass constructor. How can this be done?

It seems, if you cut down a bit on sprinkling periods all over the place you should be OK! Parameter packs aren't anything particular special: they are just expanded into a comma separate list. That is, if you change your code to become that below you should be OK:
template <typename... Bases>
struct CompositeClass: public Bases... {
template <typename... Arguments>
CompositeClass(Arguments&&... arguments):
Bases(std::forward<Arguments>(arguments))...
{}
};
Except for adding a colon in front of the initializer list I have only removed some excess "...". This works OK as long as all your template arguments are actually class types and as long as they happen to be different. Obviously, types which aren't classes can't be used as bases. If You need the same type multiple times as base you need to inherit them indirectly why an auxiliary type which gives them a number. Generating the numbers is a bit tricky the first few times you do it but nothing really magically either.
Expanding on the edited question: You mean, you want to pass lists of arguments to each individual base class constructor? If it is OK to pass in std:tuple<...> as arguments to your CompositeClass this is doable, too. Essentially, you need to transform each std::tuple<...> into a list of arguments. This also requires the generation of said indices. I would at least start off with an auxiliary base which takes a std::tuple<...> as argument and forwards the members as arguments to the base. This would use something similar to the code above for the CompositeClass and the main trick would be in an auxiliary class:
template <int... Numbers> struct Sequence {};
template <int N> struct MakeSequence;
template <typename Base>
struct AuxiliaryBase: Base {
template <typename Tuple, int... Numbers>
AuxiliaryBase(Tuple&&, Sequence<Numbers...> const&);
};
template <typename... Bases>
struct CompositeClass: public AuxiliaryBase<Bases>... {
template <typename... Args>
CompositeClass(Args&&... arguments):
AuxiliaryBase<Bases>(std::forward<Args>(arguments),
typename MakeSequence<std::tuple_size<Args>::size>())...
{}
};
Implementing the constructor of AuxiliaryBase essentially requires that there is a facility creating a sequence of integers from 0 to std::tuple_size<Tuple>::value. This take a bit tweaking but is definitely doable. To make them available an auxiliary parameter is passed in (I'm not sure if this can be avoided; well, probably it can be packaged as a type with the std::tuple<...> if this should be avoided). With this, the base class constructor is fairly straight forward:
template <typename Base>
template <typename Tuple, int... Numbers>
AuxiliaryBase<Base>::AuxiliaryBase(Tuple&& tuple, Sequence<Numbers...> const&):
Base(std::get<Numbers>(tuple)...)
{}
I haven't tested it but something along these lines should actually work. What this version doesn't do is perfect forwarding of the members in the std::tuple<...>: for this you would need a variation of std::forward<>() which takes three arguments which uses the reference qualifications of the outer type to determine which kind of reference needs to be forward for the member. I haven't tried this but it is probably an interesting exercise as well.
I haven't tried to compile this particular example but I have definitely done something like this in the past: if you look the slides for the presentation I gave at the ACCU conference in 2010 you will find all the details on how to do this (including how to create a sequence of integers; a very short of this, actually).

Related

Multiple parameter packs in a single function?

I'm trying to create a function that takes two parameter packs of objects. There are two templated base classes and I'd like to pass instances of derived classes to this function. Consider this example.
template <int N>
struct First {};
template <int N>
struct Second {};
// there are a few of these
struct FirstImpl : First<5> {};
struct SecondImpl : Second<7> {};
template <int... firstInts, int... secondInts>
void function(float f, First<firstInts> &... first, Second<secondInts> &... second) {
// ...
}
What I would like to do is call function like this
FirstImpl firstImpl;
OtherFirstImpl otherFirstImpl;
SecondImpl secondImpl;
OtherSecondImpl otherSecondImpl;
function(9.5f, firstImpl, otherFirstImpl, secondImpl, otherSecondImpl);
but this example won't compile. The compiler seems to be trying to pack everything into the second parameter pack and failing because FirstImpl can't be implicitly converted Second<N>.
How do I get around this?
It's pretty much next to impossible to define something with two variadic parameter packs. Once a variadic parameter pack gets encountered, it likes to consume all remaining parameters, leaving no crumbs for the second pack to feed on.
However, as I mentioned, in many cases you can use tuples, and with deduction guides in C++17, the calling convention is only slightly longer than otherwise.
Tested with gcc 7.3.1, in -std=c++17 mode:
#include <tuple>
template <int N>
struct First {};
template <int N>
struct Second {};
template <int... firstInts, int... secondInts>
void function(std::tuple<First<firstInts>...> a,
std::tuple<Second<secondInts>...> b)
{
}
int main(int, char* [])
{
function( std::tuple{ First<4>{}, First<3>{} },
std::tuple{ Second<1>{}, Second<4>{} });
}
That's the basic idea. In your case, you have subclasses to deal with, so a more sophisticated approach would be necessary, probably with an initial declaration of two tuples being just a generic std::tuple< First...> and std::tuple<Second...>, with some additional template-fu. Probably need to have First and Second declare their own type in a class member declaration, and then have the aforementioned template-fu look for the class member, and figure out which superclass it's dealing with.
But the above is the basic idea of how to designate two sets of parameters, from a single variadic parameter list, and then work with it further...
Let's first code a variable template which determines whether a type derives from First or not:
template <int N>
constexpr std::true_type is_first(First<N> const &) { return {}; }
template <int N>
constexpr std::false_type is_first(Second<N> const &) { return {}; }
template <class T>
constexpr bool is_first_v = decltype( is_first(std::declval<T>()) )::value;
And a struct Split which collects the indices of the First and Second types:
template <class, class, class, std::size_t I = 0> struct Split;
template <
std::size_t... FirstInts,
std::size_t... SecondInts,
std::size_t N
>
struct Split<
std::index_sequence<FirstInts...>,
std::index_sequence<SecondInts...>,
std::tuple<>,
N
> {
using firsts = std::index_sequence<FirstInts...>;
using seconds = std::index_sequence<SecondInts...>;
};
template <
std::size_t... FirstInts,
std::size_t... SecondInts,
std::size_t I,
typename T,
typename... Tail
>
struct Split<
std::index_sequence<FirstInts...>,
std::index_sequence<SecondInts...>,
std::tuple<T, Tail...>,
I
> : std::conditional_t<
is_first_v<T>,
Split<std::index_sequence<FirstInts..., I>,
std::index_sequence<SecondInts...>,
std::tuple<Tail...>,
I + 1
>,
Split<std::index_sequence<FirstInts...>,
std::index_sequence<SecondInts..., I>,
std::tuple<Tail...>,
I + 1
>
> {};
And like I told you in the comments, adding a member value to First and Second (or inheriting from std:integral_constant), this allows us to write the following:
template <std::size_t... FirstIdx, std::size_t... SecondIdx, typename Tuple>
void function_impl(float f, std::index_sequence<FirstIdx...>, std::index_sequence<SecondIdx...>, Tuple const & tup) {
((std::cout << "firstInts: ") << ... << std::get<FirstIdx>(tup).value) << '\n';
((std::cout << "secondInts: ") << ... << std::get<SecondIdx>(tup).value) << '\n';
// your implementation
}
template <class... Args>
void function(float f, Args&&... args) {
using split = Split<std::index_sequence<>,std::index_sequence<>, std::tuple<std::decay_t<Args>...>>;
function_impl(f, typename split::firsts{}, typename split::seconds{}, std::forward_as_tuple(args...));
}
Demo
Why won't you simply pass the class itself as template parameter? Like this:
template <int N>
struct First {};
template <int N>
struct Second {};
// there are a few of these
struct FirstImpl : First<5> {};
struct SecondImpl : Second<7> {};
template <typename FirstSpec, typename SecondSpec>
void function(float f, FirstSpec & first, SecondSpec & second) {
// ...
}
Not exactly what you asked but... you could unify the two list using a variadic template-template int container (Cnt, in the following example) and next detect, for every argument, if is a First or a Second (see the use of std::is_same_v)
The following is a full working example
#include <string>
#include <vector>
#include <iostream>
#include <type_traits>
template <int>
struct First {};
template <int>
struct Second {};
// there are a few of these
struct FirstImpl : First<5> {};
struct SecondImpl : Second<7> {};
template <template <int> class ... Cnt, int... Ints>
void function (float f, Cnt<Ints> & ... args)
{
(std::cout << ... << std::is_same_v<Cnt<Ints>, First<Ints>>);
}
int main()
{
FirstImpl firstImpl;
FirstImpl otherFirstImpl;
SecondImpl secondImpl;
SecondImpl otherSecondImpl;
function(9.5f, firstImpl, otherFirstImpl, secondImpl, otherSecondImpl);
}

Simplify template arguments

I need to create a template class depending on a simple struct like this. It has a method AddNew to insert new element for its tuple member.
struct Type
{
int i;
char c;
};
template<typename ...T>
class A
{
std::tuple<T...> ts;
public:
A(T&&...t):ts(std::move(t)...){}
void AddNew(T t)
{
ts=std::tuple_cat(ts,t);
}
};
int main()
{
A<Type,Type,Type,Type> a({1,'a'},{2,'t'},{3,'c'},{4,'g'});
Type t{5,'x'};
a.AddNew(t);
}
Problem I don't want to write down Type... 4 times like in main. I like something like A<Type,4> when I initialize A with 4 Types or A<Type, 100> for 100 Types.
You probably just want vector:
struct Type
{
int i;
char c;
};
template<typename T>
class A
{
std::vector<T> data;
public:
template <typename ... Ts>
A(Ts&&...ts) : data(std::forward<Ts>(ts)...){}
void AddNew(const T& t)
{
data.push_back(t);
}
};
int main()
{
A<Type> a(Type{1,'a'}, Type{2,'t'}, Type{3,'c'}, Type{4,'g'});
Type t{5,'x'};
a.AddNew(t);
}
you may partially specialize A for some helper 'tag' type:
template<typename T, std::size_t N>
struct repeat{};
template<typename... T>
struct repeat_unpack{ using type = A<T...>; };
template<typename T, int N, typename... V >
struct repeat_unpack<repeat<T,N>,V...>: std::conditional_t<(N>1),
repeat_unpack<repeat<T,N-1>,T,V...>,
repeat_unpack<T,V...>
>{};
template<typename T, int N>
class A<repeat<T,N>>: repeat_unpack<repeat<T,N>>::type {};
// to be used as
A<repeat<Type,3>>
the above uses inheritance to avoid code duplication, so you'll probably need to lift some members (eg. constructors) to the partial specialization as well.
Otherwise, just use an alias
template<class T, std::size_t N>
using A_nth = typename repeat_unpack<repeat<T,N>>::type;
A_nth<Type,3>
Just one more thing, given that you're using tuple internally, it should be easy to simply specialize for std::array and use it instead, being tuple compatible ...
PS. obviously, your AddNew() code makes no sense as is, I'd assume it's just a code snippet typo ...

trouble with constructing from initializer_list

I have a class which shall have many different constructor overloads, one of which is from an initializer_list. Unfortunately, this will be picked automatically when I use list-initialisation as in
class foo
{
template<typename T>
foo(T const&it);
template<typename T>
foo(std::initializer_list<T> list);
};
template<typename other>
foo bar(other const&it)
{
return {it};
}
when the second constructor is called, not the first. This is correct but counter intuitive and hence dangerous. How can I prevent that? I would be happy with a solution where the second constructor specialises for the case that list.size()==1. For example
template<typename T>
foo::foo(std::initializer_list<T> list)
: foo(list.begin(), list.size()) {}
template<typename T> // private constructor
foo::foo(const T*p, size_t n)
{
if(n==1)
foo::foo(p[0]); // this does not what I want
else {
/* ... */
}
}
where I try to explicitly call the first constructor from within another constructor. Is this possible? (the code compiles, but doesn't appear to call the intended constructor or in fact any constructor). The only way I could make this work was by using placement new:
template<typename T> // private constructor
foo::foo(const T*p, size_t n)
{
if(n==1)
::new(this) foo(p[0]); // seems to work
else {
/* ... */
}
}
However, this is at least inelegant if not outright dangerous. Are there better solutions?
Note that trying to replicate the effort of the first constructor instead of calling it is not really a useful solution, as there will be many different first constructors for different type of arguments using SFINAE.
Maybe you can use variadic template instead of initializer_list.
Something like:
#include <type_traits>
// equivalent to std::is_same but for more type.
template <typename... Ts> struct are_same : std::false_type {};
template <typename T> struct are_same<T> : std::true_type {};
template <typename T, typename ...Ts> struct are_same<T, T, Ts...> : are_same<T, Ts...> {};
class foo
{
struct dummy_t {};
public:
template <typename...Ts, typename = typename std::enable_if<(sizeof...(Ts) > 1) && are_same<typename std::decay<Ts>::type...>::value>::type>
foo(Ts&&... args);
template<typename T>
foo(T const&it);
};

C++11 variadic template template parameters

Keeping the old question. See below for resolution.
It is probably something simple, but still. I have the following C++11 code fragment:
#include <vector>
template <typename... Ts>
struct typelist
{
};
template <typename T>
struct EventContainer
{
typedef T Type;
/// TODO. Ring buffer
std::vector<T> container;
void push(const T& t)
{
EventContainer<T>::container.push_back(t);
}
virtual ~EventContainer()
{
}
};
template <template <typename...> class TL>
class EventStorage:
public EventContainer<Ts>...
{
};
class Event1
{
};
class Event2
{
};
typedef typelist<Event1,Event2> Events12;
int main()
{
EventStorage<Events12> ev;
return 0;
}
How can I make EventStorage inherit EventContainer templeted with each of the types in the typelist. I could do it with Loki:: library, but I want to use C++11 with variadic templates.
Thank you.
Resolution1: Fixing EventStorage template template issue. This will make EventStorage, multiple inherit all EventContainer templated with each type of Ts.
template <typename...>
class EventStorage
{
};
template <typename... Ts>
class EventStorage < typelist<Ts...> >:
public EventContainer<Ts>...
{
};
Now I have compile time error, on the following main():
int main()
{
EventStorage<Events12> ev;
Event1 ev1;
ev.push(ev1);
return 0;
}
In function ‘int main()’:
error: request for member ‘push’ is ambiguous
error: candidates are: void EventContainer<T>::push(const T&) [with T = Event2]
error: void EventContainer<T>::push(const T&) [with T = Event1]
Why the compiler is confused? After all I push with specific type.
GCC 4.6.1 here.
Resolution2:
As #Matthieu M. suggested I can present a forwarding method int EventStorage, but at a cost of one extra functin call:
template <typename T>
void push(const T& t)
{
EventContainer<T>::push(t);
}
According to Alexandrescu, the compiler will optimize this forward call as long as parameters are references.
Now the question is officially closed :)
Is there any reason for introducing the typelist in the first place ?
template <typename T> struct Template { void push(T) {} };
template <typename... Args>
class Storage: public Template<Args>...
{
public:
// forwarding...
template <typename T>
void push(T t) {
Template<T>& me = *this;
me.push(t);
}
};
int main() {
Storage< int, char > storage;
}
This works and you can typedef the whole Storage<...> bit.
EDIT: Following on comments regarding the possibility to "combine" types.
There are two solutions:
template <typename...> struct CombineStorage;
template <typename... A, typename... B>
struct CombineStorage<Storage<A...>, Storage<B...>> {
typedef Storage<A..., B...> type;
};
Or simply provide a typelist adapter:
template <typename... Args>
class Storage<typelist<Args...>>: public Storage<Args...> {};
At the moment, you're never even passing a typelist instantiation to the EventStorage, just the typelist template. So currently, there is no type pack to expand.
However, you should be able to unpack the typelist with a specialization and work with type packs otherwise:
template <typename...> class EventStorage;
template <typename Head, typename... Tail> class EventStorage<Head, Tail...>
: public EventContainer<Head>, EventStorage<Tail...>
{
using EventContainer<Head>::push;
using EventStorage<Tail...>::push;
};
// allows you to pass typelists for convenience
template <typename... TL> class EventStorage<typelist<TL...>>
: public EventStorage<TL...>
{
using EventStorage<TL...>::push;
};
The using declarations just pull all the push methods into the same overload set, which seems to work for me.
The alternative would be to add a template method (maybe just to the toplevel typelist specialization) which explicitly forwards to this->EventContainer<T>::push, but it would require an exact type match.

How can I call a set of variadic base class constructors based on tagged argument packs?

I'd like to be able to do this:
template<typename Mix>
struct A {
A(int i) { }
};
template<typename Mix>
struct B {
B() { }
B(const char*) { }
};
template<template<typename> class... Mixins>
struct Mix : Mixins<Mix<Mixins...>>... {
// This works, but forces constructors to take tuples
template<typename... Packs>
Mix(Packs... packs) : Packs::Type(packs.constructorArgs)... { }
};
template<template<typename> class MixinType, typename... Args>
struct ArgPack {
typedef MixinType Type; // pretend this is actually a template alias
tuple<Args...> constructorArgs;
ArgPack(Args... args) : constructorArgs(args...) { }
}
template<typename... Args>
ArgPack<A, Args...> A_(Args... args) {
return ArgPack<A, Args...>(args...);
}
template<typename... Args>
ArgPack<B, Args...> B_(Args... args) {
return ArgPack<B, Args...>(args...);
}
Mix<A, B> m(); // error, A has no default constructor
Mix<A, B> n(A_(1)); // A(int), B()
Mix<A, B> n(A_(1), B_("hello"); // A(int), B(const char*)
How do I fill in /* mysterious code here */ to do what I want, to provide a nice interface for calling some set of constructors of mixins? I have a solution that works by making all non-null constructs actually take a tuple of args, and then overloading figures out which one to call, but I would like to avoid constraining mixin authors by making them write a constructor A(tuple), instead of just A(int, int).
Thanks!
I think I understand what you want. std::pair has a similar feature:
std::pair<T, U> p(std::piecewise_construct
, std::forward_as_tuple(foo, bar)
, std::forward_as_tuple(qux) );
// p.first constructed in-place as if first(foo, bar) were used
// p.second constructed in place as if second(qux) were used
As you can see this has a lot of benefits: exactly one T and U construction each takes place, neither T and U are required to be e.g. MoveConstructible, and this only costs the constructions of two shallow tuples. This also does perfect forwarding. As a warning though, this is considerably harder to implement without inheriting constructors, and I will use that feature to demonstrate a possible implementation of a piecewise-constructor and then attempt to make a variadic version of it.
But first, a neat utility that always come in handy when variadic packs and tuples are involved:
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 Tuple>
constexpr
typename build_indices<
// Normally I'd use RemoveReference+RemoveCv, not Decay
std::tuple_size<typename std::decay<Tuple>::type>::value
>::type
make_indices()
{ return {}; }
So now if we have using tuple_type = std::tuple<int, long, double, double>; then make_indices<tuple_type>() yields a value of type indices<0, 1, 2, 3>.
First, a non-variadic case of piecewise-construction:
template<typename T, typename U>
class pair {
public:
// Front-end
template<typename Ttuple, typename Utuple>
pair(std::piecewise_construct_t, Ttuple&& ttuple, Utuple&& utuple)
// Doesn't do any real work, but prepares the necessary information
: pair(std::piecewise_construct
, std::forward<Ttuple>(ttuple), std::forward<Utuple>(utuple)
, make_indices<Ttuple>(), make_indices<Utuple>() )
{}
private:
T first;
U second;
// Back-end
template<typename Ttuple, typename Utuple, int... Tindices, int... Uindices>
pair(std::piecewise_construct_t
, Ttuple&& ttuple, Utuple&& utuple
, indices<Tindices...>, indices<Uindices...>)
: first(std::get<Tindices>(std::forward<Ttuple>(ttuple))...)
, second(std::get<Uindices>(std::forward<Utuple>(utuple))...)
{}
};
Let's try plugging that with your mixin:
template<template<typename> class... Mixins>
struct Mix: Mixins<Mix<Mixins...>>... {
public:
// Front-end
template<typename... Tuples>
Mix(std::piecewise_construct_t, Tuples&&... tuples)
: Mix(typename build_indices<sizeof...(Tuples)>::type {}
, std::piecewise_construct
, std::forward_as_tuple(std::forward<Tuples>(tuples)...)
, std::make_tuple(make_indices<Tuples>()...) )
{
// Note: GCC rejects sizeof...(Mixins) but that can be 'fixed'
// into e.g. sizeof...(Mixins<int>) even though I have a feeling
// GCC is wrong here
static_assert( sizeof...(Tuples) == sizeof...(Mixins)
, "Put helpful diagnostic here" );
}
private:
// Back-end
template<
typename TupleOfTuples
, typename TupleOfIndices
// Indices for the tuples and their respective indices
, int... Indices
>
Mix(indices<Indices...>, std::piecewise_construct_t
, TupleOfTuples&& tuple, TupleOfIndices const& indices)
: Mixins<Mix<Mixins...>>(construct<Mixins<Mix<Mixins...>>>(
std::get<Indices>(std::forward<TupleOfTuples>(tuple))
, std::get<Indices>(indices) ))...
{}
template<typename T, typename Tuple, int... Indices>
static
T
construct(Tuple&& tuple, indices<Indices...>)
{
using std::get;
return T(get<Indices>(std::forward<Tuple>(tuple))...);
}
};
As you can see I've gone one level higher up with those tuple of tuples and tuple of indices. The reason for that is that I can't express and match a type such as std::tuple<indices<Indices...>...> (what's the relevant pack declared as? int...... Indices?) and even if I did pack expansion isn't designed to deal with multi-level pack expansion too much. You may have guessed it by now but packing it all in a tuple bundled with its indices is my modus operandi when it comes to solving this kind of things... This does have the drawback however that construction is not in place anymore and the Mixins<...> are now required to be MoveConstructible.
I'd recommend adding a default constructor, too (i.e. Mix() = default;) because using Mix<A, B> m(std::piecewise_construct, std::forward_as_tuple(), std::forward_as_tuple()); looks silly. Note that such a defaulted declaration would yield no default constructor if any of the Mixin<...> is not DefaultConstructible.
The code has been tested with a snapshot of GCC 4.7 and works verbatim except for that sizeof...(Mixins) mishap.
I faced a very similar problem in the context of a Policy based design. Basically i have my class inherit behaviour from a set of Policies, and some of them are stateful and need constructor initialization.
The solution i found is to have the policy classes organized in a deep hierarchy, instead of a wide one. This allows to write constructors which take just the elements they need from an argument pack and pass along the remaining pack to initialize the "top" part of the hierarchy.
Here is my code:
/////////////////////////////////////////////
// Generic part //
/////////////////////////////////////////////
template<class Concrete,class Mother,class Policy>
struct PolicyHolder{};
struct DeepHierarchyFinal{};
template<class Concrete,class P,typename... Args>
struct DeepHierarchy: public PolicyHolder<Concrete,DeepHierarchy<Concrete,Args...>,P>,
public P
{
template<typename... ConstructorArgs>
DeepHierarchy(ConstructorArgs... cargs):
PolicyHolder<Concrete,DeepHierarchy<Concrete,Args...>,P>(cargs...){};
};
template<class Concrete,class P>
struct DeepHierarchy<Concrete,P>: public PolicyHolder<Concrete,DeepHierarchyFinal,P>,
public P
{
template<typename... ConstructorArgs>
DeepHierarchy(ConstructorArgs... cargs):
PolicyHolder<Concrete,DeepHierarchyFinal,P>(cargs...){};
};
///////////////////////////////////////////
// Test case //
///////////////////////////////////////////
///////////////////////////////////////////
// Policies //
///////////////////////////////////////////
struct Policy1{};
struct Policy2{};
struct Policy3{};
struct Policy4{};
template<class Concrete,class Mother>
struct PolicyHolder<Concrete,Mother,Policy1> : public Mother
{
int x;
template<typename... Args>
PolicyHolder(int _x,Args... args):Mother(args...),x(_x) {};
};
template<class Concrete,class Mother>
struct PolicyHolder<Concrete,Mother,Policy2> : public Mother
{
template<typename... Args>
PolicyHolder(Args... args):Mother(args...){
cout<<"Policy2 initialized";
// Here is a way to know (at runtime) if a particular
// policy has been selected in the concrete class
if (boost::is_convertible<Concrete,Policy3>::value)
cout<<" together with Policy3\n";
else
cout<<" without Policy3\n";
};
};
template<class Concrete,class Mother>
struct PolicyHolder<Concrete,Mother,Policy3> : public Mother
{
string s;
char c;
template<typename... Args>
PolicyHolder(string _s,char _c,Args... args):Mother(args...), s(_s),c(_c) {};
};
template<class Concrete,class Mother>
struct PolicyHolder<Concrete,Mother,Policy4> : public Mother
{
template<typename... Args>
PolicyHolder(Args... args):Mother(args...) {
// Here is a way to check (at compile time) that 2 incompatible policies
// does not coexist
BOOST_STATIC_ASSERT(( ! boost::is_convertible<Concrete,Policy1>::value));
};
};
//////////////////////////////////////////////
// Concrete class //
//////////////////////////////////////////////
template<class... PoliciesPack>
struct C: public DeepHierarchy<C<PoliciesPack...>,PoliciesPack...>
{
using Policies=DeepHierarchy<C<PoliciesPack...>,PoliciesPack...>;
string s;
template<typename... Args>
C(string _s,Args... args):Policies(args...),s(_s){};
};
BOOST_AUTO_TEST_CASE( testDeepHierarchyConstruction )
{
C<Policy1,Policy2> c0("foo",4);
BOOST_CHECK_EQUAL(c0.x,4);
C<Policy1,Policy2,Policy3> c1("bar",3,"foo",'f');
BOOST_CHECK_EQUAL(c1.c,'f');
C<Policy3,Policy4> c2("rab","oof",'d');
BOOST_CHECK_EQUAL(c2.c,'d');
}
I made a more extensive analisys of this approach at this page.