The problem that I am facing is that in my application it matters in which order the members of my class are initialized in the constructor. And thus I need to use clumsy syntax to get the behaviour I am aiming for.
This should outline my problem (see below code): I want my class Widget
to include the interface of class WidgetSignals
from the user's point of view. I can't use inheritance because that requires me to initialize the inherited WidgetSignals
before the member elements
of the Widget
which should be used to initialize WidgetSignals
.
template<typename... Elems>
class WidgetSignals
{
WidgetSignals(const std::tuple<Elems...> elems)
: // initialize members using elems
{}
// members ...
};
template<typename... Elems>
class Widget : public WidgetSignals<Elems...>
{
std::tuple<Elems...> elements;
Widget(vec4 quad)
: WidgetSignals<Elems...>(elements) // uh-oh! elements are not initialized yet!
, elements(initalizeElements(quad))
};
Here are my previous solutions to this problem:
Use a mediator helper class:
template<typename... Elems>
class Widget : public WidgetSignals<Elems...>
{
std::tuple<Elems...> elements;
struct Data
{
Data(vec4 quad)
: elems(initializeElements(quad)) // initialize elements here
{}
std::tuple<Elems...> elems;
};
Widget(Data data)
: WidgetSignals<Elems...>(data.elems) // bingo!
, elements(data.elems)
{}
};
Encapsulate WidgetSignals
and expose references to WidgetSignals
' members in Widget:
template<typename... Elems>
class Widget
{
std::tuple<Elems...> elements;
WidgetSignals<Elems...> signals;
Widget(vec4 quad)
: elements(initializeElements(quad))
, signals(elements) // initialized after elements because of order of member declaration
{}
// WidgetSignal member references
const typename WidgetSignals<Elems...>::SignalType& enter = signals.enter;
const typename WidgetSignals<Elems...>::SignalType& leave = signals.leave;
// ... remaining members
};
With both of these solutions I can then use the interface of WidgetSignals
through a Widget
:
Widget widget(vec4(0, 0, 20, 5));
foo(widget.enter);
But both of these solution are rather hacky and messy, so I would really like to have a better syntax for this, something like:
using signals;
or
using signals::enter;
in Widget
. using WidgetSignals<Elems...>::enter;
is actually something that works, but only if Widget
already inherits from WidgetSignals
and that would mean I had to use a mediator helper class again, which I'd like to avoid.
Just stick the elements you want to initialise first into a [private
] base class you inherit from first. For example:
template<typename... Elems>
struct WidgetPbase {
std::tuple<Elems...> elements;
}
template<typename... Elems>
class Widget
: private WidgetPbase<Elems...>
, public WidgetSignals<Elems...>
{
public:
Widget(vec4 quad)
: WidgetPbase<Elems...>{initializeElements(quad)}
, WidgetSignals<Elems...>(elements) {
}
};
As base classes as inherited left-to-right/top-to-bottom this arranges for the elements
member to be initialised at the point it is accessed. The only potential caveat is that virtual
bases are initialised first: if WdigetSignals
is a virtual
base it may be necessary to stick a virtual
in front of WidgetPbase
.