tags:

views:

82

answers:

3

A while back I learned about the Curiously Recurring Template Pattern (http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern), and it reminded me of a technique I had used to implement an event queue cache.

The basic idea is that we take advantage of a Base class pointer to store a container of homogeneous pointer types. However because the Derived class is a template class, which stores an item of type T, what we are really storing is a list of heterogeneous types.

I was curious if anyone has seen this technique, which is perhaps interesting, and if so if anyone has named it? Anyone care to critique it? Is there a better way to achieve my end here?

Thanks.

#include <iostream>
#include <algorithm>
#include <functional>
#include <list>
#include <string>

class Base
{
   public:
      Base(){}
      virtual ~Base(){}

      virtual void operator()() = 0;
};


template<typename C, typename T>
class Derived : public Base
{
   public:

      Derived(C* c, T item) : consumer_(c), item_(item) {}

      virtual void operator()()
      {
         consumer_->consume(item_);
      }

      C* consumer_;
      T item_;
};

class Consumer
{
      bool postpone_;
      std::list<Base*> cache_;


   public:
      Consumer() : postpone_(true)
      {
      }

      void pause()
      {
         postpone_ = true;
      }

      void resume()
      {
         postpone_ = false;

         const std::list<Base*>::iterator end = cache_.end();
         for ( std::list<Base*>::iterator iter = cache_.begin();
               iter != end;
               ++iter )
         {
            Base* bPtr = *iter;
            bPtr->operator()();
            delete bPtr;
         }
         cache_.clear();
      }

      void consume(int i) 
      {
         if ( postpone_ )
         {
            std::cerr << "Postpone int.\n";
            cache_.push_back(new Derived<Consumer, int>(this, i));
         }
         else
         {
            std::cerr << "Got int.\n";
         }
      }

      void consume(double d)
      {
         if ( postpone_ )
         {
            std::cerr << "Postpone double.\n";
            cache_.push_back(new Derived<Consumer, double>(this, d));
         }
         else
         {
            std::cerr << "Got double.\n";
         }
      }
      void consume(char c)
      {
         if ( postpone_ )
         {
            std::cerr << "Postpone char.\n";
            cache_.push_back(new Derived<Consumer, char>(this, c));
         }
         else
         {
            std::cerr << "Got char.\n";
         }
      }
};
static Consumer consumer;



void destroy(Base* object)
{
   delete object;
}


int main()
{
   // Consumer is registered with something that sends events out to lots
   // of different consumer types (think observer pattern). Also in the non-toy
   // version consumer isn't being passed PODs, but various Event types.
   consumer.consume(0);
   consumer.consume(0.1f);
   consumer.consume('x');

   consumer.resume();
}

The output is:

Postpone int.
Postpone double.
Postpone char.
Got int.
Got double.
Got char.
+1  A: 

anyone has named it?

I think it is an adapter pattern implemented without using inheritance from T.

Anyone care to critique it?

YOu could have used short template function instead of this class. Or you could use template function that returns template class. Template function can automatically guess required types - sou you could omit <> and do less typing.

SigTerm
+3  A: 

What you are using is plain polymorphism, as Stephen points out in his comment. While you store different objects internally in the container, you are limited to using the interface defined in Base. That is, of course, unless you intend to add type checking and downcasts to actually retrieve the values. There is just a limited amount of things that you can do with unrelated objects.

Depending on what you are actually wanting to achieve you might consider using other solutions like boost::any/boost::variant if what you want is to actually store unrelated types (in the few cases where this makes sense --cells in a spreadsheet, for example).

David Rodríguez - dribeas
A big field for variant types are language bridges: COM, NPAPI, many scripting language interfaces, ...
Georg Fritzsche
+1  A: 

Nice.

You're utilizing compiler's power to generate templated series of derived classes and it's actually cool that you can mix plain derived classes (written by yourself) with template-specialized derived classes and with compiler-generated ones (built as result of template instantiation).

class Base { ... };

template <typename Y> class Derived1 : public Base { ... };

template <specialization>
class Derived1 : public Base { ... };

class Derived2 : public Base { ... };

This could be useful, but it doesn't somehow extend the polymorphism term, because you're still limited to the Base class interface.

Also, you could write a plain factory which would have some templated method for generating subclasses and use it to avoid writing new Derived1<std::string>..., but write something like

std::string a;
Base* base = Factory.Create(a)
Kotti