Please consider the following code for a type that can compose different mixin types. The constructor of the composition type takes a variadic list of tuples that represent the arguments for the constructors of the composed types:
#include <string>
#include <tuple>
#include <utility>
struct MixinBase {
MixinBase() = default;
// Note; want to delete these instead of default them.
MixinBase(const MixinBase&) = default;
MixinBase(MixinBase&&) = default;
};
struct MixinA : public MixinBase {
MixinA(int, const std::string&, const std::string&) {}
};
struct MixinB : public MixinBase {
MixinB(const std::string&, const std::string&) {}
};
template <typename... Mixins>
struct Composition : private Mixins... {
template <typename... Packs>
Composition(Packs&&... packs)
: Mixins(constructMixin<Mixins>(
std::forward<Packs>(packs),
std::make_index_sequence<std::tuple_size_v<Packs>>{}))...
{
}
private:
template <typename Mixin, typename Pack, size_t... Indexes>
Mixin constructMixin(Pack&& arguments, std::index_sequence<Indexes...>) const
{
return Mixin(std::get<Indexes>(std::forward<Pack>(arguments))...);
}
};
int main()
{
std::string a{"a"};
std::string b{"b"};
Composition<MixinA, MixinB>(
std::forward_as_tuple(7, a, b), std::forward_as_tuple(a, b));
return 0;
}
This works perfectly fine, however, I would like to avoid the indirection through constructMixin
, and directly construct every inherited mixin object so that the need for a copy/move constructor on the mixin type can be avoided. Is this possible?
You can define a helper class to support piecewise construction:
template <typename T>
struct Piecewise_construct_wrapper : T {
template <typename Tuple>
Piecewise_construct_wrapper(Tuple&& t) :
Piecewise_construct_wrapper(std::forward<Tuple>(t),
std::make_index_sequence<std::tuple_size_v<Tuple>>{}) {}
template <typename Tuple, size_t... Indexes>
Piecewise_construct_wrapper(Tuple&& t, std::index_sequence<Indexes...>) :
T(std::get<Indexes>(std::forward<Tuple>(t))...) {}
};
Then you can make your Composition
inherit Piecewise_construct_wrapper<Mixins>...
:
template <typename... Mixins>
struct Composition : private Piecewise_construct_wrapper<Mixins>... {
template <typename... Packs>
Composition(Packs&&... packs)
: Piecewise_construct_wrapper<Mixins>(std::forward<Packs>(packs))...
{
}
};
I would like to avoid the indirection through constructMixin, and directly construct every inherited mixin object so that the need for a copy/move constructor on the mixin type can be avoided. Is this possible?
You would need the Mixins to opt-in to allowing piecewise construction directly. Unfortunately that's pretty repetitive, so you could use a macro like:
#define PIECEWISE_CONSTRUCT(Type) \
template <typename Tuple> \
Type(std::piecewise_construct_t, Tuple&& tuple) \
: Type(std::piecewise_construct, \
std::forward<Tuple>(tuple), \
std::make_index_sequence<std::tuple_size_v<Tuple>>()) \
{ } \
template <typename Tuple, size_t... Indexes> \
Type(std::piecewise_construct_t, Tuple&& tuple, \
std::index_sequence<Indexes...>) \
: Type(std::get<Indexes>(std::forward<Tuple>(tuple))...) \
{ }
To be used as:
struct MixinA : public MixinBase {
MixinA(int, const std::string&, const std::string&) {}
PIECEWISE_CONSTRUCT(MixinA)
};
struct MixinB : public MixinBase {
MixinB(const std::string&, const std::string&) {}
PIECEWISE_CONSTRUCT(MixinB)
};
template <typename... Mixins>
struct Composition : private Mixins... {
template <typename... Packs>
Composition(Packs&&... packs)
: Mixins(std::piecewise_construct, std::forward<Packs>(packs))...
{ }
};
Guaranteed copy elision unfortunately can't work in constructing subobjects - so this might be your best bet. I don't think you can do this any more directly without having some kind of nested packs? It's very possible that I'm just not creative enough though and would be very curious if somebody comes up with something better.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With