user3443615
user3443615

Reputation: 165

What is the right way to implement this templated decorator pattern for this situation?

template <typename T> class FooBase {
     void push(T& t) = 0;
     T pop() = 0;
}

template<typename T> class FooDerived1: public FooBase<T> {
}

template<typename T> class FooDerived2: public FooBase<T> {
}
////////////////////////////////
template<typename T, typename Foo> class Bar: public Foo<pair<T, int>> {
       void push(T& t) {
           int value = get_val();
           foo_.push(make_pair(t, v));
       }
       
       pair<T, int> pop() {
           return foo_.pop();     
       }    
       
       Foo<pair<T, int>> foo_;
}

So basically we have different instances of class Foo, "FooDerived1", "FooDerived2" etc

We have a class Bar, which is a decorator, on top of class Foo, which should take in different any children of class Foo.

The problem is that, the decorator would initialize class FooBase's template with a modified typename. Basically change FooBase to FooBase<pair<T, int>>

Inside class Bar, I would use all of Foo's interface, and also a wrapper, around the base class function

What is the right way to do this? Because when I am trying to initialize class Bar, I am confused.

Bar<float, FooDerived1<float>> bar1;
Bar<float, FooDerived2<float>> bar2;

I am not sure how this would work and what is the right way to accomplish, what I am trying to do.

Thank you.

Upvotes: 0

Views: 108

Answers (1)

463035818_is_not_an_ai
463035818_is_not_an_ai

Reputation: 122585

First, Bar needs only a single template argument. T can be deduced from an instantiation FooBase<T> via a type trait, or by using a member alias.

Then you can use SFINAE to check if that argument inherits from FooBase (which adds a second argument again):

#include <type_traits>
#include <utility>

template <typename T> struct FooBase { using type = T; };

template <typename T> struct Foo1 : FooBase<T> {};
   

template <typename T,typename = void> struct Bar;
template <typename T> 
struct Bar< T, 
            std::enable_if_t<
                std::is_base_of_v<
                    FooBase<typename T::type>,T>> 
                > 
       : FooBase<std::pair<typename T::type,int>> {};

int main() {
    Bar< Foo1<int>> c;
}

Though, this would be much simpler if you drop some restrictions. If Bar<T> does not require T to inherit from FooBase and if the caller instantiates it via Bar< sometype::type > then no SFINAE is needed and Bar could be simply template <typename T> struct Bar : FooBase<std::pair<T,int>> {};. Also it is not clear what is the relevance of the derived class for Bar. In your code Bar merely uses T and FooBase. However, I hope the above will help.

Upvotes: 3

Related Questions