Call constructor of mixin base classes based on number of arguments

209 Views Asked by At

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.

3

There are 3 best solutions below

1
On BEST ANSWER

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)...
  { }
};
0
On

With factory, you might do something like:

template <typename> struct Tag{};

template<typename T, typename U>
OneArgBase Create(Tag<OneArgBase>, const T& t, const U&)
{
    return OneArgBase(t);
}

template<typename T, typename U>
TwoArgBase Create(Tag<TwoArgBase>, const T& t, const U& u)
{
    return TwoArgBase(t, u);
}

template<typename ... Mixins>
struct TwoArgsMix : Mixins...
{
  template<typename T, typename U>
  TwoArgsMix(const T & t, const U & u) :
    Mixins(Create(Tag<Mixins>{}, t, u))...
  {}
};

Demo

0
On

You can try to use partially specified template.

Something like:

template<typename twoarg>
struct TwoArgMix<twoarg, OneArgBase>: ...

It will be specific implementation with OneArgBase as second template parameter.