Re: polymorphic base class pointers and template classes
awm129@gmail.com wrote:
Excellent point! I had assumed some sort of special processing would
be needed in the handlers to get to the correct overload, but this
illustrates the point quite clearly. I didn't think about overload
resolution being based on the static type... This may throw my design
out the window altogether.
As a side note, I ended up abandoning templates for the derived
classes. It turns out each and every message type would need its own
specialization anyway which kinda defeats the point of the templates.
So now I have 20 some odd similar yet different message classes.
I want this, but I don't think it can work (forgive any typos as I
don't have a compiler on this machine):
It can work. Look up the Visitor design pattern, which is basically what
you need to implement.
It helps if you have an unchanging set of Message classes, which you
seem to have.
struct LegacyMessageA {};
You need a forward declaration of HandlerBase:
class HandlerBase;
class MessageBase
{
public:
virtual ~MessageBase() {}
Here you need a pure-virtual function to make a 'call back' to the
handler:
virtual void call_doProcess(HandlerBase*) = 0;
};
class MessageA : public MessageBase
{
public:
MessageA(const MessageA msg) : MessageBase() //assume msg is copied
to m_msg
// assume accessors into m_msg
// assume common functionality for all messages
Each concrete Message class must implement the call_doProcess to call
that function in the handler:
virtual void call_doProcess(HandlerBase* handler);
private:
LegacyMessageA m_msg
};
class HandlerBase
{
public:
HandlerBase(){}
virtual ~HandlerBase(){}
void process( MessageBase* msg );
protected:
HandlerBase must have the _doProcess functions public (unless you want
to juggle with friend declarations) and provide a default implementation
for each concrete Message class:
public:
virtual void _doProcess( MessageBase* msg );
virtual void _doProcess( MessageA* msg)
{ _doProcess(static_cast<MessageBase*>(msg)); }
};
HandlerBase::process( MessageBase* msg )
{
// this is only ever going to call _doProcess( MessageBase* msg )
// as opposed to the proper overload for the actual type of msg.
// I want it to call the proper overload in the derived handler!
Here you must ask the message to call _doProcess for you:
msg->call_doProcess(this);
this->_doProcess( msg )
}
HandlerBase::_doProcess( MessageBase* msg )
{
//do nothing
}
Implementation for the call_doProcess function can come here:
void MessageA::call_doProcess(HandlerBase* handler)
{
handler->_doProcess(this);
}
class MessageAHandler : HandlerBase
{
public:
DerivedHandler() : HandlerBase() {}
private:
_doProcess( MessageA* msg );
};
MessageAHandler::_doProcess( MessageA* msg )
{
// special handling for type MessageA.
// I don't think this ever gets called as all messages go
// to MessageBase::_doProcess( MessageBase* msg )
As this now overrides a function of the base class, it can get called by
MessageA::call_doProcess.
}
int main()
{
LegacyMessageA msg;
MessageBase* message1 = new MessageA( msg );
HandlerBase* handler = new MessageAHandler();
handler->process( message1 );
delete message1;
delete handler;
return 0;
}
Can this scheme work at all? It all seemed so elegant yesterday!
Yes. With the changes I indicated it becomes a bit less elegant (the
HandlerBase must know about all the specific messages), but it will
work.
Bart v Ingen Schenau
--
a.c.l.l.c-c++ FAQ: http://www.comeaucomputing.com/learn/faq
c.l.c FAQ: http://c-faq.com/
c.l.c++ FAQ: http://www.parashift.com/c++-faq-lite/