Search code examples
c++templatespolicy-based-design

multiple use of policies in policy-based design


i have a class template roundtrip which takes two policies. as long as they are different, everything is fine, but using one policy twice leads to compilation errors.

Example:

#include <iostream>

class walk {
protected:
    void move() {
        std::cout<<"i'm walking."<<std::endl;
    }
};

class car {
protected:
    void move() {
        std::cout<<"i'm driving in a car."<<std::endl;
    }
};

template<typename S, typename T>
class roundtrip : private S, private T {
public:
    void printSchedule(void) {
        std::cout<<"away: ";
        S::move();

        std::cout<<"return: ";
        T::move();
    }
};

int main(void){
    roundtrip<walk,car> LazyTrip;
    LazyTrip.printSchedule();

    roundtrip<car,car> VeryLazyTrip; // ERROR: error: duplicate base type ‘walk’ invalid
    VeryLazyTrip.printSchedule();

    return 0;
}

how can that be resolved? or is there a better design to achieve the same behaviour?

EDIT: i added wrappers to the policies, the user interface does not change. what do you think about this solution, is it a clean design?

template<typename T>
class outbound : private T {
protected:
    void moveOutbound(void) {
        T::move();
    }
};

template<typename T>
class inbound : private T {
protected:
    void moveInbound(void) {
        T::move();
    }
};

template<typename S, typename T>
class roundtrip : private outbound<S>, private inbound<T> {
public:
    void printSchedule(void) {
        std::cout<<"away: ";
        this->moveOutbound();

        std::cout<<"return: ";
        this->moveInbound();
    }
};

Solution

  • Instead of inheriting, you could add data members to roundtrip, but the functions in walk and car are currently protected. If you cannot make the functions in walk and car public, you could:

    • befriend the roundtrip template

      class walk {
      protected:
          void move() {
              std::cout<<"i'm walking."<<std::endl;
          }
          template<class T, class S>
          friend class roundtrip;
      };
      
    • make the member functions accessible to roundtrip via a helper class:

      template<typename S, typename T>
      class roundtrip {
      private:
          struct helperT : private T
          {
          public: // or private + friend roundtrip
              using T::move;
              using T::T;
          } mT;
      
          struct helperS : private S
          {
          public:
              using S::move;
              using S::S;
          } mS;
      
      public:
          void printSchedule(void) {
              std::cout<<"away: ";
              mT.move();
      
              std::cout<<"return: ";
              mS.move();
          }
      };
      

      If the interface is the same for both types, you could use a helper class template instead of two helper classes:

      template<typename S, typename T>
      class roundtrip {
      private:
          template<class U>
          struct helper : private U
          {
          public: // or private + friend roundtrip
              using U::move;
              using U::U;
          };
      
          helper<S> mS;
          helper<T> mT;
      
      public:
          void printSchedule(void) {
              std::cout<<"away: ";
              mT.move();
      
              std::cout<<"return: ";
              mS.move();
          }
      };
      

      (it is also possible to inherit from a helper template specialization, e.g. class roundtrip : helperT<T>, helperS<S>)