6 votes

Surcharge de plusieurs fonctions virtuelles dans un modèle de classe variadique

Permettez-moi de vous donner un exemple de code.

#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{});
}

Ici Subscriber possède une fonction virtuelle pure handleService(ServiceType const) pour chaque paramètre de modèle dans le paquet ServiceType... . Je dois donc remplacer chacun d'entre eux sur StatusUpdater . Ici, j'ai fourni ceux dont j'ai besoin à la main pour IntService y FloatService sachant que je n'aurai besoin que de ces deux éléments dans cet exemple minimal. Mais je veux être capable de fournir une surcharge pour tout ce qu'il y a dans le pack ServiceType... . Tous appelleront updateService de la politique donnée.

Veuillez noter que Subscriber provient d'une bibliothèque externe et je ne peux pas modifier sa définition.

6voto

Angew Points 53063

Il n'est pas possible d'intégrer ces implémentations directement dans la classe, il faut en hériter (de la même manière que pour la classe Subscriber hérite de plusieurs SubscriberImpl instanciations). Cependant, pour les surcharger tous et continuer à utiliser votre classe de manière polymorphe en tant que Subscriber vous devrez les hériter "séquentiellement" au lieu de les hériter "en parallèle". En outre, la fonction Modèle curieusement récurrent peut être utilisé pour donner à toutes les implémentations l'accès à l'objet de surcharge final :

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;
};

[Exemple vivant].

3voto

Rerito Points 1199

Je ne vois pas de solution pour faire exactement ce que vous voulez. Cependant, vous pouvez obtenir le même comportement sans avoir besoin de l'option virtual de l'entreprise. J'ai d'abord pensé à une solution CRTP, comme dans la réponse de @Angew, puis j'ai envisagé une autre possibilité :

Vous pouvez modifier votre Subscriber classe comme celle-ci :

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);
    }
};

Votre code client devient alors :

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;
};

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X