First off: I searched half the web to find an answer with this as a solution that came closest. It is, however, too heavyweight for me though so I am looking a little less complex.
Well then, some context: I am building a system which should be able to process incoming messages from a queue and then store the outcome of these messages in another queue. I would like to store these responses in a generic class because I am storing it in a multimap
.
The response class currently is defined as follows
class COutboundMsg
{
public:
enum eTypeHint {
thNone,
thSomeType,
thLast
};
eTypeHint m_TypeHint;
void* m_OutboundData;
COutboundMsg开发者_开发百科(COutboundMsg::eTypeHint TypeHint, void* data);
};
COutboundMsg::COutboundMsg(COutboundMsg::eTypeHint TypeHint, void* data) :
m_TypeHint(TypeHint),
m_OutboundData(data)
{
}
Now, the current way of working would involve a user to do something like this:
CSomeType* data = new CSomeType();
COutboundMsg(COutboundMsg::thSomeType , (void*) data);
It would be up to the user at the other end to cast the void*
back to CSomeType*
(using the type hint) and delete it.
It don't like it.
I'd rather have the m_OutboundData contained in an auto_ptr
or something and make sure that it deletes itself when done.
Any ideas? Maybe a different approach altogether
Normally polymorphism is used for a system like this; any item that can be put in the Outbound queue derives from QueueItem
and the queue contains QueueItem*
or a smart pointer to a QueueItem
.
Since the items all derive from a common base, you can safely delete the QueueItem*
or allow a smart pointer to handle it. This approach also allows you to use dynamic_cast
to make sure that the pointer is, in fact, pointing to an object of the type you think it is.
If this approach is possible, be sure to make the QueueItem
destructor virtual, otherwise the derived class's destructor won't be called.
shared_ptr
stores a deleter to use to delete the object, and so does not depend on the static type of the pointer at the time of deletion. So something like this should do the right thing:
COutboundMsg(eTypeHint TypeHint, shared_ptr<void> msg);
shared_ptr<CSomeType> data(new CSomeType);
COutboundMsg(COutboundMsg::thSomeType, data);
If the set of messages is known before hand (transmit/receive), rather than polymorphism, consider a variant type, I'm taking specifically about boost::variant<>
.
The advantage of this approach is that each message is a specific type (I know it seems like you don't want to implement this - however as a long term design feature, I think you'll find this approach extensible) and the variant describes the set of messages, let me show a quick example:
let's say I have two messages
struct Logon{};
struct Logout{};
I define a variant to hold the possible set of messages, at a given time it's only ever one of them..
typedef boost::variant<Logon, Logout> message_type;
Now I can store this in any container, and pass it around like normal, the only difference is that the truly generic way to access them is via visitors, e.g.
struct process : boost::static_visitor<>
{
void operator()(Logon const& cLogon)
{
// do stuff
}
void operator()(Logout const& cLogout)
{
// do stuff
}
};
// method that processes a given message
void processMessage(message_type const& cMsg)
{
// don't know what this message is, that's fine, hand it to the visitor
boost::apply_visitor(process(), cMsg); // the correct overload will be called for the message
}
...I think it's slightly better than void*
;)
精彩评论