4

I'm trying to forward a series of parameters to two different mixin classes as follows:

template <typename... Checkers>
class Checker : public Checkers... {
public:
    template<typename... Args>
    Checker(Args&&... args) : Checkers(std::forward<Args>(args))... { }
};

template <typename... Handlers>
class Handler : public Handlers... {
public:
    template <typename... Args>
    Handler(Args&&... args) : Handlers(std::forward<Args>(args))... { }
};

template <typename C, typename H>
class Tester : public C, H {
public:
    template <typename... ArgC, typename... ArgH>
    Tester(std::tuple<ArgC...>&& argc, ArgH&&... argh) : C(argc), H(argh...) {
    }
};

Checker and Handler are two different collections of Mixin classes with different requirements for each but shared requirements for all members. I realize I can't do a double variadic construction in Tester (the compiler can't deduce where to split the arguments so passes them all to Handler and none to Checker) so I pass the Checker arguments in a Tuple and the Handler arguments in a variadic list. The problem is, Checker's constructor is responsible for forwarding those parameters to its bases. Handler can do it because Handler's constructor is a variadic list, but Checker gets a tuple but you can't for-each forward a tuple's elements like forward can a variadic parameter list.

Any advice would be greatly appreciated. Thanks.

Additional

A solution would be to either a) unpack argc into Checker's variadic constructor or b) Make Checker's constructor take a tuple and then somehow forward each element of that tuple to each of Checker's mixin bases, Checkers.... I realize std::pair has a trick for forwarding a tuple as a fixed argument list to it's types, such as a 3-parametered std::vector constructor using the std::piecewise_construct type marker to inform it to do the unpack, but I don't see how this can be applied here. I looked at GCC 4.8.1's implementation of std::pair with std::piecewise_construct and couldn't figure it out. I read some of the older, pre-C++11 books on template metaprogramming (Modern C++ Design and C++ Template Metaprogramming, for example) but I'm at a loss here now that there's a standard and I'm trying to avoid Boost and Loki.

Additional

Must conform to at least GCC 4.7.2. The solution I found based on std::pair requires Constructor Inheritance, which didn't become available until GCC 4.8.1 which isn't supported on my build system.

Additional

Although GCC 4.6.3 support would be nice, Delegated Constructors were added in 4.7.2 so I should have access to that language feature.

TimeHorse
  • 479
  • 3
  • 12
  • 1
    May be [this SO question](http://stackoverflow.com/questions/10766112/c11-i-can-go-from-multiple-args-to-tuple-but-can-i-go-from-tuple-to-multiple) can help. – maverik Feb 26 '14 at 22:06
  • I'm not yet sure as the problem is calling a base constructor which is a little limited with calling conventions so my first reading is that won't work but let me mull it over and see if I can twist it to match what I'm looking for. – TimeHorse Feb 26 '14 at 22:36
  • You could emulate the "piecewise" constructor of `std::pair`. – Kerrek SB Feb 26 '14 at 23:50
  • I have seen a number of attempts to do that, but I don't see how it can be made generic to allow an arbitrary number of mixins. Pair has 2 bound template types, but what about the generic case of n bindings? – TimeHorse Feb 27 '14 at 13:00
  • Mainly, I'm trying to see if I can apply http://stackoverflow.com/questions/9730593/how-can-i-call-a-set-of-variadic-base-class-constructors-based-on-tagged-argumen but I'm still not able to make the connection. – TimeHorse Feb 27 '14 at 15:18
  • Have a look at this : http://cpptruths.blogspot.fr/2012/06/perfect-forwarding-of-parameter-groups.html. My edit : I did not see your edit so the solution is pointless there – Davidbrcz Feb 27 '14 at 15:30
  • Thanks @Davidbrcz ; I couldn't access the page anyway. But maybe you could fix the link to let others who aren't quite as constrained know about it. – TimeHorse Feb 27 '14 at 16:01
  • Here is the right link (because I cant edit anymore) : http://cpptruths.blogspot.fr/2012/06/perfect-forwarding-of-parameter-groups.html – Davidbrcz Feb 27 '14 at 16:04
  • @Davidbrcz your solution works for GCC 4.7.2 which is my required version so it works for me, thank you, thank you, thank you. I have one older build system on GCC 4.6.3 which can't handle it but since the requirement is 4.7.2 I'm good so thanks! – TimeHorse Feb 27 '14 at 19:19
  • It is a shame you cant put links as answer =/. But you are welcome =) – Davidbrcz Feb 27 '14 at 19:22

1 Answers1

2

http://cpptruths.blogspot.fr/2012/06/perfect-forwarding-of-parameter-groups.html

Davidbrcz's solution as specified in his blog above was sufficient to solve my conundrum. The solution is rather complex so I'll direct you to his page for it, but the basic idea is to on-the-fly create a numeric index tuple, à la std::maketuple(0, 1, 2, ...) where the tuple contains each of the indices of the various members of the tuple you need to enumerate. Then you just use:

M(std::forward<ArgM>(std::get<IdxM>(argm))...)

For M as either C or H in the example above and ArgM the arguments to M and IdxM the equally sized tuple of indices. Because the lists are the same length, the list gets rolled out into parameters in tandem and the tuple is unpacked.

The limitation is because you want the complicated step of building the index tuple to be hidden as an implementation detail, you need to use Constructor Delegation such that the public constructor takes the 2 tuples which then delegates to the private constructor which takes 2 value tuples and 2 index tuples. GCC 4.7.2 supports Delegated Constructors, but 4.6.3 doesn't.

To get around this, you need to make the 4-parameter constructor (2 tuples of values, 2 tuples of indices) public and then I wrote a macro to fill in the index tuple parameters:

#if __GNUC__ < 4 || __GNUC_MINOR__ <= 6
#define ZEROPARAM , detail::make_indices<>::type()
#define ONEPARAM , detail::make_indices<int>::type()
#define TWOPARAM , detail::make_indices<int, int>::type()
#define THREEPARAM , detail::make_indices<int, int, int>::type()
#define FOURPARAM , detail::make_indices<int, int, int, int>::type()
#define FIVEPARAM , detail::make_indices<int, int, int, int, int>::type()
#define SIXPARAM , detail::make_indices<int, int, int, int, int, int>::type()
#define SEVENPARAM , detail::make_indices<int, int, int, int, int, int, int>::type()
#define EIGHTPARAM , detail::make_indices<int, int, int, int, int, int, int, int>::type()
#define NINEPARAM , detail::make_indices<int, int, int, int, int, int, int, int, int>::type()
#define TENPARAM , detail::make_indices<int, int, int, int, int, int, int, int, int, int>::type()
#else // __GNUC__ < 4 || __GNUC_MINOR__ <= 6
#define ZEROPARAM
#define ONEPARAM
#define TWOPARAM
#define THREEPARAM
#define FOURPARAM
#define FIVEPARAM
#define SIXPARAM
#define SEVENPARAM
#define EIGHTPARAM
#define NINEPARAM
#define TENPARAM
#endif // __GNUC__ < 4 || __GNUC_MINOR__ <= 6

And then add the appropriate macro following the construction of the given Tester, at least while there are still folks on GCC 4.6.3 while I work to get everyone to at least 4.7.2 and preferable 4.8.1. :)

I wish I could give Davidbrcz credit for the solution but at least this may be helpful for folks facing a similar problem to apply his solution in their specific case. The main thing is copy his make_indices template class to do the actual work; the rest is a cake walk!

TimeHorse
  • 479
  • 3
  • 12