I have two sets of mixin base classes that follow the following pattern
// base class taking one contructor argument
struct OneArgBase
{
const double x;
template<typename T>
OneArgBase(const T & t) :
x(t.x)
{}
};
// base class taking two constructor arguments
struct TwoArgBase
{
const double y;
template<typename T, typename U>
TwoArgBase(const T & t, const U & u) :
y(t.y + u.y)
{}
};
From these base classes i derive two sets of mixin classes
template<typename ... Mixins>
struct OneArgMix : Mixins...
{
template<typename T>
OneArgsMix(const T & t) :
Mixins(t)...
{}
};
template<typename ... Mixins>
struct TwoArgMix : Mixins...
{
template<typename T, typename U>
TwoArgsMix(const T & t, const U & u) :
Mixins(t, u)...
{}
};
The problem I am facing now, is that i want to pass classes following the OneArgBase pattern to TwoArgMix
using Mix = TwoArgMix<TwoArgBase, OneArgBase>;
template<typename ... Mixins>
struct TwoArgMix : Mixins...
{
template<typename T, typename U>
TwoArgsMix(const T & t, const U & u) :
Mixins(t, u)... // if Mixins is TwoArgBase
Mixins(t)... // if Mixins is OneArgBase
{}
};
but have no idea how two write the constructor of TwoArgMix in such a way that if passes only the first template parameter to Mixin base classes that follow the OneArgMix pattern. If possible i would like to avoid writing dummy arguments to the OneArgMix constructor, because these classes are needed for the OneArgMix too.
All problems in programming can be solved by adding another layer of indirection.
We need to conditionally ignore the second argument if it's not constructible. One way to do that is to wrap each mixin in another type that conditionally ignores its second argument:
template <typename M>
struct WrappedMixin : M
{
template <typename T, typename U>
WrappedMixin(T const& t, U const& u)
: WrappedMixin(t, u, std::is_constructible<M, T const&, U const&>{})
{ }
private:
template <typename T, typename U>
WrappedMixin(T const& t, U const& u, std::true_type /* yes, use both */)
: M(t, u)
{ }
template <typename T, typename U>
WrappedMixin(T const& t, U const&, std::false_type /* no, just one */)
: M(t)
{ }
};
And now, our main constructor is easy: we just inherit from the wrapped ones instead:
template<typename ... Mixins>
struct TwoArgMix : WrappedMixin<Mixins>...
{
template<typename T, typename U>
TwoArgsMix(const T & t, const U & u)
: WrappedMixin<Mixins>(t, u)...
{ }
};