Ok, the context is some serialization / deserialization code that will parse a byte stream into an 'object' representation that's easier to work with (and vice-versa).
Here's a simplified example with a base message class and then depending on a 'type' header, some more data/function are present and we must choose the right subclass to instantiate:
class BaseMessage {
public:
enum Type {
MyMessageA = 0x5a,
MyMessageB = 0xa5,
};
BaseMessage(Type type) : mType(type) { }
virtual ~BaseMessage() { }
Type type() const { return mType; }
protected:
Type mType;
virtual void parse(void *data, size_t len);
};
class MyMessageA {
public:
MyMessageA() : BaseMessage(MyMessageA) { }
/* message A specific stuf ... */
protected:
virtual void parse(void *data, size_t len);
};
class MyMessageB {
public:
MyMessageB() : BaseMessage(MyMessageB) { }
/* message B specific stuf ... */
protected:
virtual void parse(void *data, size_t len);
};
In a real examples, there would be hundreds of different message types and possibly several level or hierarchy because some messages share fields/functions with each other.
Now, to parse a byte string, I'm doing something like:
BaseMessage *msg = NULL;
Type type = (Type)data[0];
switch (type) {
case MyMessageA:
msg = new MyMessageA();
break;
case MyMessageB:
msg = new MyMessageB();
break;
default:
/* protocol error */
}
if (msg)
msg->parse(data, len);
But I don't find this huge switch very elegant, and I have the information about which message has which 'type value' twice (once in the constructor, one in this switch)
It's also quite long ...
I'm looking for a better way that would just be better ... How to improve this?
One way of approaching it would be using a map and register some kind of factory function for each message type. This means that you get rid of the switch case and can add and remove messages dynamically.
The code would look something like:
// Create the map (most likely a member in a different class)
std::map<BaseMessage::Type, MessageCreator*> messageMap;
...
// Register some message types
// Note that you can add and remove messages at runtime here
messageMap[BaseMessage::MyMessageA] = new MessageCreatorT<BaseMessageA>();
messageMap[BaseMessage::MyMessageB] = new MessageCreatorT<BaseMessageB>();
...
// Handle a message
std::map<Type, MessageCreator*>::const_iterator it = messageMap.find(msgType);
if(it == messageMap.end()) {
// Unknown message type
beepHang();
}
// Now create the message
BaseMessage* msg = it->second.createMessage(data);
The MessageCreator class would look something like this:
class MessageCreator {
public:
virtual BaseMessage* createMessage(void* data, size_t len) const = 0;
};
template<class T> class MessageCreatorT : public MessageCreator {
public:
BaseMessage* createMessage(void* data, size_t len) const {
T* newMessage = new T();
newMessage.parse(data, len);
return newMessage;
}
};
It's a pretty basic question in fact (as you can imagine, you are definitely not the only one deserializing in C++).
What you are looking for is called Virtual Construction.
C++ does not define Virtual Construction, but it's easy to approximate it using the Prototype Design Pattern or using a Factory method.
I personnally prefer the Factory approach, for the reason that the Prototype one means having some kind of default instance that is replicated and THEN defined... the problem is that not all classes have a meaningful default, and for that matter, a meaningful Default Constructor.
The Factory approach is easy enough.
You need a common base class for the Messages, and another for the Parsers
Each Message has both a Tag and an associated Parser
Let's see some code:
// Framework
class Message
{
public:
virtual ~Message();
};
class Parser
{
public:
virtual ~Parser();
virtual std::auto_ptr<Message> parse(std::istream& serialized) const;
};
// Factory of Messages
class MessageFactory
{
public:
void register(std::string const& tag, Parser const& parser);
std::auto_ptr<Message> build(std::string const& tag, std::istream& serialized) const;
private:
std::map<std::string,Parser const*> m_parsers;
};
And with this framework (admittedly simple), some derived classes:
class MessageA: public Message
{
public:
MessageA(int a, int b);
};
class ParserA: public Parser
{
public:
typedef std::auto_ptr<MessageA> result_type;
virtual result_type parse(std::istream& serialized) const
{
int a = 0, b = 0;
char space = 0;
std::istream >> a >> space >> b;
// Need some error control there
return result_type(new MessageA(a,b));
}
};
And at last, the use:
int main(int argc, char* argv[])
{
// Register the parsers
MessageFactory factory;
factory.register("A", ParserA());
// take a file
// which contains 'A 1 2\n'
std::ifstream file = std::ifstream("file.txt");
std::string tag;
file >> tag;
std::auto_ptr<Message> message = factory.parse(tag, file);
// message now points to an instance of MessageA built by MessageA(1,2)
}
It works, I know for I use it (or a variation).
There are some things to consider:
You may be willing to make MessageFactory a singleton, this then allows it to be called at library load, and thus you can register your parsers by instantiating static variables. This is very handy if you don't want main to have to register every single parser type: locality > less dependencies.
The tags have to be shared. It is not unusual either for the tag to be served by a virtual method of the Message class (called tag).
Like:
class Message
{
public:
virtual ~Message();
virtual const std::string& tag() const = 0;
virtual void serialize(std::ostream& out) const;
};
The logic for serialization has to be shared too, it is not unusual for an object to handle its own serialization/deserialization
Like:
class MessageA: public Message
{
public:
static const std::string& Tag();
virtual const std::string& tag() const;
virtual void serialize(std::ostream& out) const;
MessageA(std::istream& in);
};
template <class M>
class ParserTemplate: public Parser // not really a parser now...
{
public:
virtual std::auto_ptr<M> parse(std::istream& in) const
{
return std::auto_ptr<M>(new M(in));
}
};
What's great with templates is that it never stops to amaze me
class MessageFactory
{
public:
template <class M>
void register()
{
m_parsers[M::Tag()] = new ParserTemplate<M>();
}
};
//skipping to registration
factory.register<MessageA>();
Now isn't it pretty :) ?
Related
For an event system i'm writing i want to bind callbacks to a list of functions.
Here is a basic example of what i want to do:
#include <iostream>
#include <functional>
#include <string>
class Base {
public:
virtual std::string getType() const = 0;
};
class Derived : public Base {
protected:
int some_data;
public:
Derived(int some_data): some_data(some_data) {}
virtual std::string getType() const {
return "Derived";
}
int getData() const {
return this->some_data;
}
};
class DerivedTwo : public Base {
protected:
double some_data;
public:
DerivedTwo(double some_data): some_data(some_data) {}
virtual std::string getType() const {
return "DerivedTwo";
}
// The type of data is not always the same.
double getData() const {
return this->some_data;
}
};
// The type of member should ALWAYS be Derived but then i can't store it in <callback>
void onDerivedEvent(Base& member) {
std::cout << member.getType() << std::endl;
// This is obviously not possible with member being a base class object
// member.getData();
}
// The type of member should ALWAYS be DerivedTwo but then i can't store it in <callback>
void onDerivedTwoEvent(Base& member) {
std::cout << member.getType() << std::endl;
}
int main() {
std::function<void(Base&)> callback;
callback = std::bind(onDerivedEvent, std::placeholders::_1);
callback(Derived(2));
callback = std::bind(onDerivedTwoEvent, std::placeholders::_1);
callback(DerivedTwo(3.0));
return 0;
}
The only thing i would like to change is that onCallback() should take a derived class member as argument instead of a reference to a base object, so i can call getData() for example.
In this example this would mean:
void onCallback(Derived& derived);
However, if i do this, i can no longer bind() the method to callback because the argument types are not matching.
Does anyone know how to make this work?
// EDIT
Sorry for the confusion here, i updated the source code with some more specifics and examples to maybe clarify what im doing here.
Note:
Since it seems like this is very relevant, here is the specific use case for what i'm trying to do here:
It's part of an event system for an engine i'm building. There are basic events pre-defined but it should be extendable with more specific events by a user using this engine. So there is not definitive list of derived classes. Then some object can subscribe to a specific event type and whenever the central event bus recieves such an event, it calls all subscribed callback functions with the event as argument. The reason i am not adding a one and for all handle function in the derived class is, the events an be used in multiple ways.
Answers to some questions from the comments:
What should happen if you pass onCallback an object that isn't that specific Derived&? (ie, add a Derived2 which has a doStuff2. Pass it to callback. What do you want to happen?
That should not be possible.
I might have not calrified that and also had a misleading information at the beginning which i have editted since then. The type of the passed derived class is always known beforehand. For example: onKeyEvent will always recieve a KeyEvent object and not a base class object or any other derived variants.
However, the variable to which this function is bound should be able to store functions which accept different derived classes from Base
This is my storage for all events:
std::map<EventType, std::list<std::function<void(const Event&)>>> listener_map;
Why isn't onCallback a method in Base that Derived overrides
I answered this in a comment. ...The reason i am not adding a one and for all handle function in the derived class is, the events an be used in multiple ways...
Meaning, i might have an KeyEvent which has the data to a key (which key, is it pressed/released/held) and the listening function(s) can use this data for whatever it wants. (Check if some specific key is pressed, chech if any random key is pressed and so on.) Some other events might not have any data at all and just notify a listener that something happened or have multiple sets of data etc.
Is there, or can there be, a finite, bounded at compile time, central list of all of the types that derive from Base at any point in your code?
In theory yes. During compilation there will be a finite number of Derived classes. However these might be different for the compilation of the library and the compilation of the project using this library.
template<class Base>
struct poly_callback {
template<class T>
static poly_callback make( std::function<void(T&)> f ) {
return { std::function<void(void*)>( [f]( void* ptr ) { f(*static_cast<T*>(static_cast<Base*>(ptr))); }) };
}
template<class T>
poly_callback( void(*pf)(T&) ):poly_callback( make<T>( pf ) ) {}
poly_callback( poly_callback const& ) = default;
poly_callback( poly_callback && ) = default;
void operator()( Base& b ) {
return type_erased( static_cast<void*>(std::addressof(b)) );
}
private:
std::function<void(void*)> type_erased;
poly_callback( std::function<void(void*)> t ):type_erased(std::move(t)) {}
};
A poly_callback<Event> can store a callable with signature compatible to void(Derived&), where Derived is derived from Event. It must be called with exactly an instance of the Derived& type or undefined behavior results as it blindly downcasts.
Stop using std::bind, it is functionally obsolete.
class Base {
public:
virtual std::string getType() const = 0;
};
class Derived : public Base {
protected:
int some_data;
public:
Derived(int some_data): some_data(some_data) {}
virtual std::string getType() const {
return "Derived";
}
int getData() const {
return this->some_data;
}
};
class DerivedTwo : public Base {
protected:
double some_data;
public:
DerivedTwo(double some_data): some_data(some_data) {}
virtual std::string getType() const {
return "DerivedTwo";
}
// The type of data is not always the same.
double getData() const {
return this->some_data;
}
};
// The type of member should ALWAYS be Derived but then i can't store it in <callback>
void onDerivedEvent(Derived& member) {
std::cout << member.getType() << "\n";
std::cout << member.getData() << "\n";
}
// The type of member should ALWAYS be DerivedTwo but then i can't store it in <callback>
void onDerivedTwoEvent(DerivedTwo& member) {
std::cout << member.getType() << "\n";
std::cout << member.getData() << "\n";
}
struct callbacks {
std::unordered_map< std::string, std::vector< poly_callback<Base> > > events;
void invoke( std::string const& name, Base& item ) {
auto it = events.find(name);
if (it == events.end())
return;
for (auto&& f : it->second)
f( item );
}
template<class Derived>
void connect( std::string const& name, void(*pf)(Derived&) )
{
events[name].push_back( pf );
}
template<class Derived>
void connect_T( std::string const& name, std::function<void(Derived&)> f )
{
events[name].push_back( std::move(f) );
}
};
int main() {
callbacks cb;
cb.connect("one", onDerivedEvent );
cb.connect("two", onDerivedTwoEvent );
Derived d(7);
DerivedTwo d2(3.14);
cb.invoke( "one", d );
cb.invoke( "two", d2 );
return 0;
}
Live example.
This can be tweaked for safety and usability. For example, check that the typeid actually matches.
Output is:
Derived
7
DerivedTwo
3.14
and as you can see, the callback functions take Derived& and DerivedTwo& objects.
In my experience this is a bad plan.
Instead, have a broadcaster<KeyboardEvent> keyboard; and don't look up your event registry systems with strings.
A map from string-to-callback only makes sense if there is some way to treat the callbacks uniformly. And you don't want to treat these callbacks uniformly. Even if you chose to store them uniformly for efficiency sake (useful in ridiculously huge frameworks), I'd want type-safe APIs not a map.
This is a question for the Object Design Pattern specialists.
Let's assume I have a Parser class that is in charge of reading/parsing a stream of data (that carry information packets of different types). Each of these packets carry a different type of information, so ideally I would have a class for each type of packet (PacketTypeA, PacketTypeB, ... each one with its own interface).
class Parser {
public:
/* ctor */
/* dtor */
void read_packet(/* arguments */);
// methods...
private:
// more methods...
}
The method Parser::read_packet would then go through the stream and return a class (or pointer or reference to a class) to the appropriate packet type.
Would you use void pointers for this? How about a generic class (PacketBasicInterface) that would provide a common (partial) interface to query about the type of packet (so that any decision could then be made at runtime)?
// Pure virtual (abstract) class to provide a common (and partial) interface
class PacketBasicInterface {
public:
std::string whoAmI() const = 0;
bool amIofType(const std::string& type) const = 0;
}
// Class to access data of type A packet
class PacketTypeA : public PacketBasicInterface {
public:
// methodA_1()
// methodA_2(), ...
}
// Class to access data of type A packet
class PacketTypeB : public PacketBasicInterface {
public:
// methodB_1()
// methodB_2(), ...
}
Any thought or feedback would be very much appreciated!
Many thanks!
This is what std::variant is for.
I would define an enumeration class, that enumerates all possible packet types:
enum class packet_type {initialization_packet, confirmation_type, ... };
And have read_packet return a tuple of packet_type and a variant:
typedef std::variant< ... > packet_info;
std::tuple<packet_type, packet_info> read_packet();
Don't really need a formal enumeration, but it makes it easier to figure out what to do with the variant.
A few variations on this general approach include:
Using an opaque std::string, rather than a fixed enumeration, to specify the packet type.
Using std::any instead of a formal std::variant.
Instead of using a simple enumeration, or an opaque token like a std::string, use a slightly non-trivial class to define the packet type, with the class's methods taking the variant metadata as parameters, and encapsulating the operations that can be done on the packet.
Of course, as noted in the cited link, std::variant requires C++17. Which would be a good argument for you to update your compiler: you get a simple way to implement a completely type-safe approach.
Double dispatching can be the way to go if you are looking for a design pattern from the realm of object oriented programming.
It follows a minimal, working example:
#include<iostream>
struct Visitor;
struct PacketBasicInterface {
virtual void accept(Visitor &) = 0;
};
struct PacketTypeA: PacketBasicInterface {
void accept(Visitor &) override;
};
struct PacketTypeB: PacketBasicInterface {
void accept(Visitor &) override;
};
struct Visitor {
void visit(PacketTypeA) {
std::cout << "PacketTypeA" << std::endl;
}
void visit(PacketTypeB) {
std::cout << "PacketTypeB" << std::endl;
}
};
void PacketTypeA::accept(Visitor &visitor) {
visitor.visit(*this);
}
void PacketTypeB::accept(Visitor &visitor) {
visitor.visit(*this);
}
struct Parser {
PacketBasicInterface * read_packet() {
return new PacketTypeB{};
}
};
int main() {
Visitor visitor;
auto *packet = Parser{}.read_packet();
packet->accept(visitor);
delete packet;
}
Would you use void pointers for this?
No.
How about a generic class (PacketBasicInterface) that would provide a common (partial) interface to query about the type of packet (so that any decision could then be made at runtime)?
That makes most sense to me.
Let me refine that. Yes, it will be good to have a generic base class. However, when parsing the stream to construct sub-types of the base class, don't rely on an if-else type approach. Rather, use a factory pattern. Let the various factories construct the right object types based an on a key, which I assume will be available from the data being parsed.
If you encounter the string "PacketTypeA" in your data, you would expect that PacketTypeAFactory will be responsible for constructing the object.
FWIW, this approach is scalable for lots of sub-types of the base class. We use this approach at my work and it has served us well for over twenty years.
Here's the skeletal structure of the code base I am thinking of:
The classes.
class PacketBasicInterface { };
class PacketTypeA : public PacketBasicInterface { };
class PacketTypeB : public PacketBasicInterface { };
The interface of the factory.
// PacketFactory.h
class PacketFactory
{
public:
static PacketBasicInterface* makePacket(std::string const& packetData);
static void registerFactory(std::string const& key, PacketFactory* factory);
virtual PacketBasicInterface* make(std::string const& packetData) = 0;
virtual ~PacketFactory() {}
};
Implementation of the framework that makes the factory work.
// PacketFactory.cpp
#include "PacketFactory.h"
namespace PacketBasicInterface_Impl
{
using PacketFactoryMap = std::map<std::string, PacketFactory*>;
PacketFactoryMap& getPacketFactoryMap()
{
static PacketFactoryMap theMap;
return theMap;
}
};
uisng namespace PacketBasicInterface_Impl;
PacketBasicInterface* PacketFactory::makePacket(std::string const& packetData)
{
std::string key = extractKey(packetData);
PacketFactoryMap& theMap = getPacketFactoryMap();
PacketFactoryMap::iterator iter = theMap.find(key);
if ( iter == theMap.end() )
{
return nullptr;
}
return iter->second->make(packetData);
}
void registerFactory(std::string const& key, PacketFactory* factory)
{
getPacketFactoryMap()[key] = factory;
}
Code for making objects of type PacketTypeA using the factory pattern.
// PacketTypeAFactory.cpp
#include "PacketFactory.h"
#include "PacketTypeA.h"
class PacketTypeAFactory : public PacketFactory
{
public:
virtual PacketBasicInterface* make(std::string const& packetData)
{
PacketTypeA* packet = new PacketTypeA();
// Flesh out packet with data pulled from packetData
// ...
//
return packet;
}
struct Initializer
{
Initializer() { PacketFactory::registerFactory("PacketTypeA", new PacketTypeAFactory); }
};
};
// Constructing this object at static initialization time makes sure
// that PacketTypeAFactory is registered with PacketFactory when the
// stream data need to be parsed.
static PacketTypeAFactory::Initializer initializer;
The code for making objects of type PacketTypeB is very similar to the
code for making objects of type PacketTypeA using the factory pattern.
// PacketTypeBFactory.cpp
#include "PacketFactory.h"
#include "PacketTypeB.h"
class PacketTypeBFactory : public PacketFactory
{
public:
virtual PacketBasicInterface* make(std::string const& packetData)
{
PacketTypeA* packet = new PacketTypeA();
// Flesh out packet with data pulled from packetData
// ...
//
return packet;
}
struct Initializer
{
Initializer() { PacketFactory::registerFactory("PacketTypeB", new PacketTypeBFactory); }
};
};
// Constructing this object at static initialization time makes sure
// that PacketTypeBFactory is registered with PacketFactory when the
// stream data need to be parsed.
static PacketTypeBFactory::Initializer initializer;
Client code.
std::string packetData;
while ( getPacketData(packetData) )
{
PacketBasicInterface* packet = PacketFactory::makePacket(packetData);
if ( packet == nullptr )
{
// Deal with error.
}
else
{
// Use packet
}
}
Here is my MESSAGE structure:
struct tEventMessage
{
// Type of the event
int Type;
// (void*) Allows those to be casted into per-Type objects
void *pArgument1;
void *pArgument2;
};
Can i add some kind of 'template' member to this structure, so that later on when building message i can pass those pointers + and any other data i wish ? ( see example below )
struct tEventMessage
{
// Type of the event
int Type;
// (void*) Allows those to be casted into per-Type objects
void *pArgument1;
void *pArgument2;
// Template
T tSomeTemplateMember;
};
void HandleClick(....)
{
CVector3 vNewPosition = ....
tEventMessage _msg;
_msg.Type = xxxx;
_msg.pArgument1 = pA->GetObjectPointer();
//
// Wrong!
// Because this CVector3 will not be alive in next tick
// - my pointer will point to nothing.
//
_msg.pArgument2 = static_cast<CVector3*>(&vNewPosition)
//
// Something like that would be great
// And would allow me to use CVector2,CVector3,CVector4 with one template member
//
_msg.tSomeTemplateMember = vNewPosition;
}
I think you're over complicating the problem. Instead of one problem, how to pass arbitrary data in a message, you now have two, how to cope with templates as well.
The usual method to implement this sort of thing is to use inheritance:-
class Message
{
public:
int Type () { return type; }
protected:
int type;
};
class ClickMessage : public Message
{
public:
ClickMessage () { type = ClickMessageID; }
private:
// the message data
};
void HandleMessage (Message *message)
{
switch (message->Type ())
{
case ClickMessageID:
HandleClick (reinterpret_cast <ClickMessage *> (message));
break;
default:
// unhandled message error
break;
}
}
void HandleClick (ClickMessage *message)
{
// do stuff
}
The problem is you end up repeating a lot of code, i.e the cast in the switch statement. There's also a maintenance issue too - added new messages requires a bit of careful updating. You could hack the code a bit and use function pointers and a map to convert message types to functions and replace the switch statement.
There might be a clever template solution, but I can't think what it might be.
Using RTTI might help (at a cost).
This is one problem that reflection is really good at solving!
Perhaps I am missing something however I am wondering why you do not start with an abstract class from which you then derive your various kinds of event messages. By taking advantage of abstract classes and deriving classes from them, you let the compiler figure out the logic that you are using a switch statement for. See this C++ Polymorphism and Abstract Base Class tutorial.
Also see this from MSDN on Abstract classes.
For instance you might have an abstract class that looks like the following. However you may not want as much of this and in fact may just want the single processEvent() method only. Any derived classes will need to provide their own versions of each of the functions specified in the abstract class.
class EventMessage abstract {
public:
virtual void *getArgument1 (void) = 0;
virtual void *getArgument2 (void) = 0;
virtual int processEvent (void) = 0;
protected:
void *pArgument1;
void *pArgument2;
};
What this abstract class defines is a class that basically contains the data that is used by all of the various event messages along with a method that is called to process the the actual message. The class itself is not instantiated however it is used as the parent or super class for other derived class that are actually instantiated as objects.
What you would then do is to derive new classes that would implement the EventMessage interface. For instance here are two different classes that would do that:
class JoJoEvent : public EventMessage {
public:
JoJoEvent(void *arg1, void *arg2);
void *getArgument1 (void);
void *getArgument2 (void);
int processEvent (void);
};
JoJoEvent::JoJoEvent(void *arg1, void *arg2)
{
pArgument1 = arg1;
pArgument2 = arg2;
}
void * JoJoEvent::getArgument1 (void) {
return pArgument1;
}
void * JoJoEvent::getArgument2 (void) {
return pArgument2;
}
int JoJoEvent::processEvent (void) {
// do stuff with the arguments
return 1;
}
class KoKoEvent : public EventMessage {
public:
KoKoEvent(void *arg1, void *arg2);
void *getArgument1 (void);
void *getArgument2 (void);
int processEvent (void);
};
KoKoEvent::KoKoEvent(void *arg1, void *arg2)
{
pArgument1 = arg1;
pArgument2 = arg2;
}
void * KoKoEvent::getArgument1 (void) {
return pArgument1;
}
void * KoKoEvent::getArgument2 (void) {
return pArgument2;
}
int KoKoEvent::processEvent (void) {
// do stuff with the arguments
return 1;
}
Then when using these you would do something like the following code:
EventMessage *myMessage = new JoJoEvent(0, 0);
EventMessage *myMessage2 = new KoKoEvent(0, 0);
myMessage2->processEvent();
myMessage->processEvent();
If you need to add additional data into the derived classes you can do so just provide a mechanism to put the data into the derived class.
I have an implementation of a State Pattern where each state handles events it gets from a event queue. Base State class therefore has a pure virtual method void handleEvent(const Event*). Events inherit base Event class but each event contains its data that can be of a different type (e.g. int, string...or whatever). handleEvent has to determine the runtime type of the received event and then perform downcast in order to extract event data. Events are dynamically created and stored in a queue (so upcasting takes place here...).
I know that downcasting is a sign of a bad design but is it possible to avoid it in this case? I am thinking of Visitor Pattern where base class State would contain virtual handlers for each event but then again downcast will need to take place in the piece of code which dequeues event from a queue and passes it to the current state. (At least in this case big switch(eventID) would be only at one place...). Is Visitor Pattern the best way (best practice) to avoid downcasting?
Here is the pseudo-code (I am passing boost::shared_ptr in this example but downcasting happens anyway):
enum EventID
{
EVENT_1,
EVENT_2,
...
};
class Event
{
EventID id;
public:
Event(EventID id):id(id){}
EventID id() const {return id;}
virtual ~Event() = 0;
};
class Event1 : public Event
{
int n;
public:
Event1(int n):Event(EVENT_1), n(n){}
int getN() const {return n;}
};
class Event2 : public Event
{
std::string s;
public:
Event2(std::string s):Event(EVENT_2), s(s){}
std::string getS() const {return s;}
};
typedef boost::shared_ptr<Event> EventPtr;
class State
{
...
public:
...
virtual ~State() = 0;
virtual void handleEvent(const EventPtr& pEvent) = 0;
};
class StateA : public State
{
...
public:
void handleEvent(const EventPtr& pEvent)
{
switch(pEvent->id())
{
case EVENT_1:
int n = boost::static_pointer_cast<Event1>(pEvent)->getN();
...
break;
case EVENT_2:
std::string s = boost::static_pointer_cast<Event2>(pEvent)->getS();
...
break;
...
}
}
}
The typical visitor pattern performs no downcast, thanks to a double-dispatch strategy:
// Visitor.hpp
class EventBar;
class EventFoo;
class Visitor {
public:
virtual void handle(EventBar const&) = 0;
virtual void handle(EventFoo const&) = 0;
};
// Event.hpp
class Visitor;
class Event {
public:
virtual void accept(Visitor&) const = 0;
};
And the implementations:
// EventBar.hpp
#include <Event.hpp>
class EventBar: public Event {
public:
virtual void accept(Visitor& v);
};
// EventBar.cpp
#include <EventBar.hpp>
#include <Visitor.hpp>
void EventBar::accept(Visitor& v) {
v.handle(*this);
}
The key point here is that in v.handle(*this) the static type of *this is EventBar const&, which selects the correct virtual void handle(EventBar const&) = 0 overload in Visitor.
The idea of events is to pass detailed objects through generalized (and agnostic) interface.
Downcast is inevitable and part of the design. Bad or good, it's disputable.
Visitor pattern only hides the casting away from you. It's still performed behind the scenes, types resolved via virtual method address.
Because your Event already has the id, it's not completely agnostic of the type, so casting is perfectly safe. Here you're watching the type personally, in visitor pattern you're making compiler take care of that.
"Whatever goes up must go down".
I want to create a class factory and I would like to use reflection for that. I just need to
create a object with given string and invoke only few known methods.
How i can do that?
You will have to roll your own. Usually you have a map of strings to object creation functions.
You will need something like the follwing:
class thing {...};
/*
class thing_A : public thing {...};
class thing_B : public thing {...};
class thing_C : public thing {...};
*/
std::shared_ptr<thing> create_thing_A();
std::shared_ptr<thing> create_thing_C();
std::shared_ptr<thing> create_thing_D();
namespace {
typedef std::shared_ptr<thing> (*create_func)();
typedef std::map<std::string,create_func> creation_map;
typedef creation_map::value_type creation_map_entry;
const creation_map_entry creation_map_entries[] = { {"A", create_thing_A}
, {"B", create_thing_B}
, {"C", create_thing_C} };
const creation_map creation_funcs(
creation_map_entries,
creation_map_entries + sizeof(creation_map_entries)
/ sizeof(creation_map_entries[0] );
}
std::shared_ptr<thing> create_thing(const std::string& type)
{
const creation_ma::const_iterator it = creation_map.find(type);
if( it == creation_map.end() ) {
throw "Dooh!"; // or return NULL or whatever suits you
}
return it->second();
}
There are other ways to do this (like having a map of strings to objects from which to clone), but I think they all boil down to having a map of strings to something related to the specific types.
There is no reflection in C++, directly supported by the standard.
However C++ is sufficiently low-level that you can implement some minimal support for reflection to complete the task at hand.
For the simple task of creating a Factory, you usually use the Prototype approach:
class Base
{
public:
virtual Base* clone() const = 0;
virtual ~Base();
};
class Factory
{
public:
std::unique_ptr<Base> get(std::string const& name);
void set(std::string const& name, std::unique_ptr<Base> b);
private:
boost::ptr_map<std::string,Base> mExemplars;
};
Of course, those "known methods" that you are speaking about should be defined within the Base class, which acts as an interface.
There is no reflection in C++, so you should restate your question trying to explain what are the requirements that you would have fulfilled with the reflection part of it.
Depending on your actual constraints and requirements, there are a few things that you can do. The first approach that I would take would be creating an abstract factory where concrete factories can register and provide a simple interface:
class Base {}; // shared base by all created objects
class ConcreteFactoryBase {
public:
virtual ~ConcreteFactoryBase() {}
virtual Base* create() const = 0; // actual construction
virtual std::string id() const = 0; // id of the types returned
};
class AbstractFactory
{
typedef std::map<std::string, ConcreteFactory* > factory_map_t;
public:
void registerFactory( ConcreteFactoryBase* factory ) {
factories[ factory->id() ] = factory;
}
Base* create( std::string const & id ) const {
factory_map_t::const_iterator it = factories.find( id );
if ( it == factories.end() ) {
return 0; // or throw, or whatever makes sense in your case
}
return (*it)->create();
}
~AbstractFactory(); // ensure that the concrete factories are deleted
private:
std::map<ConcreteFactoryBase*> factories;
};
The actual concrete factories can be implemented manually but they can probably be templated, unless the constructors for the different types require different arguments:
template <typename T>
class ConcreteFactory : public ConcreteFactoryBase {
public:
ConcreteFactory( std::string const & id ) : myid(id) {}
virtual Base* create() const {
return new T;
}
virtual std::string id() const {
return myid;
}
private:
std::string myid;
};
class Test : public Base {};
int main() {
AbstracFactory factory;
factory.register_factory( new ConcreteFactory<Test>("Test") );
}
Optionally you could adapt the signatures so that you can pass arguments to the constructor through the different layers.
Then again, by knowing the actual constraints some other approaches might be better. The clone() approach suggested elsewhere is good (either by actually cloning or by creating an empty object of the same type). That is basically blending the factory with the objects themselves so that each object is a factory of objects of the same type. I don't quite like mixing those two responsabilities but it might be one of the simplest approaches with less code to write.
You could use typeid & templates to implement the factory so you won't need strings at all.
#include <string>
#include <map>
#include <typeinfo>
//***** Base *****
class Base
{
public:
virtual ~Base(){} //needs to be virtual to make typeid work
};
//***** C1 *****
class C1 : public Base
{};
//***** Factory *****
class Factory
{
public:
template <class T>
Base& get();
private:
typedef std::map<std::string, Base> BaseMap;
BaseMap m_Instances;
};
template <class T>
Base& Factory::get()
{
BaseMap::const_iterator i = m_Instances.find(typeid(T).name());
if(i == m_Instances.end()) {
m_Instances[typeid(T).name()] = T();
}
return m_Instances[typeid(T).name()];
}
//***** main *****
int main(int argc, char *argv[])
{
Factory f;
Base& c1 = f.get<C1>();
return 0;
}