tags:

views:

103

answers:

3

Say I have a template class that takes msgs from source, does something smart to them, and then sends them to a sink:

template <typename Source, typename Sink>
class MsgHandler
{
MsgHandler(Source* pSource)
: m_pSource(pSource)
{
  m_pSource->setHandler(this);
}
};

//Now the definition of the Source:

template <typename Handler>
class Source
{
void setHandler(Handler* pHandler)
{
  m_pHandler = pHandler;
}
};

All fine, but now I can't really make a Source or Handler. Eg:

MsgHandler<FileSource<MsgHandler<FileSource.... recursing parameters...
FileSource<MsgHandler<FileSource<MsgHandler.... same problem when trying to build a source

Is there a way to solve this problem without using a virtual base class for the Handler?

Virtual base class solution:

class MyHandler
{
virtual ~MyHandler() {};
virtual void handleSomething() = 0;
};

template <typename Source, typename Sink>
class MsgHandler : public MyHandler
{
  MsgHandler(Source* pSource)
  : m_pSource(pSource)
  {
    m_pSource->setHandler(this);
  }
  void handleSomething() {}
 };

class Source
{
void setHandler(MyHandler* pHandler)
{
m_pHandler = pHandler;
}
};
+1  A: 

I don't understand why your Source needs to be parameterized on its handler. If Source and Handler really do need to be tightly coupled in the way you describe, it does not seem like templates buy you very much beyond interface definition. Seems to me like you could just have a non-template Source class that encapsulates Handler<Source, Sink>.

Steve Townsend
True, your proposed solution would work but then the Source would know about the Handler. Particularly what type of handler is being used. And it would make the Source unusable if I wanted a different handler. Hence I proposed a virtual class for the Handler. I'm more interested in knowing if the problem can be solved maintaining templates. Is there some trick?
anio
Can't think of a sensible way using class templates. See here for a model that uses function templates instead: http://www.boost.org/doc/libs/1_44_0/doc/html/boost_asio/reference/async_read/overload1.html
Steve Townsend
+1  A: 

It looks like the Handler shouldn't know anything about the Source. How about simple linear dependency:

template <typename Sink>
class Handler {
private:
    Sink* sink; // get this pointer in the constructor?
public:
    void handle( const Msg& m ) {
        // processing
        sink->accept( m );
    }
};

template <typename Handler>
class Source {
private:
    Handler* handler; 
public:
    void genMessage() {
        Msg m;
        // get message off the wire?
        handler->handle( m );
    }
};

Could also be twisted to have "handling" and "sinking" as policies.

Nikolai N Fetissov
True, in a perfect world the handler would not know about the source. My design is not that clean, a conscious decision that I made. Sometimes the sink will cause the handler to do something to the source. However, your solution is very clean and would work well.
anio
+2  A: 

You could use a templated parameter for the source parameter of your handler:

class MySink;
template <template<typename Handler> class Source, typename Sink>
class MsgHandler
{
    Source<MsgHandler>* m_pSource;

    MsgHandler(Source<MsgHandler>* pSource)
    : m_pSource(pSource)
    {
      m_pSource->setHandler(this);
    }
};

//Now the definition of the Source:

template <typename Handler>
class Source
{
    void setHandler(Handler* pHandler)
    {
      m_pHandler = pHandler;
    }
};

//Now you can define variables like this
MsgHandler<Source, MySink> myHandler;

Of course that requires the Source parameter of MsgHandler to be a template with exactly one parameter (the handler), but if you can live with that constraint this would solve your definition problem (otherwise you might (or might not depending on what exactly you would be trying) be able to use some extra template foo to circumvent this restriction (creating another template which takes the handler as parameter and has a typedef for the corresponding SourcesType comes to mind).

In this scenario it might also be a good idea to add an typedef Source<MsgHandler> SourceType to MsgHandler to make the Source-Instantiation visible to the caller (instead of requiring the programmer to guess that MsgHandler will instantiate Source.

Grizzly
Just tried this out and got it working. Neat. Going to play around with it a bit. Thank you!
anio