Let me just give a code example.
#include <iostream>
#include <utility>
#include <tuple>
template <typename Service>
struct SubscriberImpl {
virtual void handleService(Service const&) = 0;
};
template <typename...ServiceType>
struct Subscriber : SubscriberImpl<ServiceType>... {
};
struct IntService {};
struct FloatService {};
template <typename StatusUpdatePolicy, typename... ServiceType>
struct StatusUpdater : Subscriber<ServiceType...>
{
StatusUpdater(StatusUpdatePolicy const& statusUpdater)
: m_statusUpdater{statusUpdater}
{}
// wont work
void handleService(IntService const& service) override {
m_statusUpdater.updateService(service);
}
void handleService(FloatService const& service) override {
m_statusUpdater.updateService(service);
}
StatusUpdatePolicy m_statusUpdater;
};
struct DummyPolicy {
void updateService(IntService const& service) {
m_i = 42;
std::cout << m_i << "\n";
}
void updateService(FloatService const& service) {
m_f = 3.14f;
std::cout << m_f << "\n";
}
int m_i;
float m_f;
};
int main() {
StatusUpdater<DummyPolicy, IntService, FloatService> su(DummyPolicy{});
su.handleService(IntService{});
su.handleService(FloatService{});
}
Here Subscriber
has a pure virtual function handleService(ServiceType const)
for each template parameter in pack ServiceType...
. So I have to override each one on StatusUpdater
. Here, I have provided the ones I need by hand for IntService
and FloatService
, knowing I will be only needing those in this minimal example. But I want to be able to provide an override for whatever there is in the pack ServiceType...
. All of them will call updateService
method of the given policy anyways.
Please note that Subscriber
comes from an external library and I cannot modify its definition.
You cannot put such implementations directly into the class, you have to inherit them (similarly to how Subscriber
inherits from multiple SubscriberImpl
instantiations). However, to override them all and still keep your class polymorphically usable as a Subscriber
, you will have to inherit them "sequentially" instead of "in parallel." Additionally, the Curiously recurring template pattern can be used to give all the implementations access to the final overriding object:
template <class Self, class SubscriberClass, class... ServiceTypes>
struct StatusUpdaterOverride;
template <class Self, class SubscriberClass, class ThisType, class... RemainingTypes>
struct StatusUpdaterOverride<Self, SubscriberClass, ThisType, RemainingTypes...> : StatusUpdaterOverride<Self, SubscriberClass, RemainingTypes...>
{
void handleService(ThisType const& service) override
{
static_cast<Self*>(this)->m_statusUpdater.updateService(service);
}
using StatusUpdaterOverride<Self, SubscriberClass, RemainingTypes...>::handleService;
};
template <class Self, class SubscriberClass, class ThisType>
struct StatusUpdaterOverride<Self, SubscriberClass, ThisType> : SubscriberClass
{
void handleService(ThisType const& service) override
{
static_cast<Self*>(this)->m_statusUpdater.updateService(service);
}
};
template <class StatusUpdatePolicy, class... ServiceType>
struct StatusUpdater : StatusUpdaterOverride<StatusUpdater<StatusUpdatePolicy, ServiceType...>, Subscriber<ServiceType...>, ServiceType...>
{
StatusUpdater(StatusUpdatePolicy const& statusUpdater)
: m_statusUpdater{statusUpdater}
{}
StatusUpdatePolicy m_statusUpdater;
};
[Live example]
I can't see a solution to do exactly what you want. However you can achieve the same behavior without needing the virtual
ity at all. I initially thought about a CRTP solution just like @Angew's answer and then came up with another possibility:
You could edit your Subscriber
class like this:
template <typename ServiceType>
class Subscriber {
public:
template <typename Handler>
void handleService(ServiceType const& service, Handler&& hdler) {
// Maybe give `updateService` a broader name that can extend to other service handlers
std::forward<Handler>(hdler).updateService(service);
}
};
With that, your client code becomes:
template <typename StatusUpdatePolicy, typename... ServiceType>
struct StatusUpdater : Subscriber<ServiceType>...
{
StatusUpdater(StatusUpdatePolicy const& statusUpdater)
: m_statusUpdater{statusUpdater}
{}
template <typename ServiceT>
void handleService(ServiceT const& service) override {
Subscriber<ServiceT>::handleService(service, m_statusUpdater);
}
StatusUpdatePolicy m_statusUpdater;
};
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