Dynamically register constructor methods in an AbstractFactory at compile time using C++ templates
- by Horacio
When implementing a MessageFactory class to instatiate Message objects I used something like:
class MessageFactory
{
public:
static Message *create(int type)
{
switch(type) {
case PING_MSG:
return new PingMessage();
case PONG_MSG:
return new PongMessage();
....
}
}
This works ok but every time I add a new message I have to add a new XXX_MSG and modify the switch statement.
After some research I found a way to dynamically update the MessageFactory at compile time so I can add as many messages as I want without need to modify the MessageFactory itself. This allows for cleaner and easier to maintain code as I do not need to modify three different places to add/remove message classes:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
class Message
{
protected:
inline Message() {};
public:
inline virtual ~Message() { }
inline int getMessageType() const { return m_type; }
virtual void say() = 0;
protected:
uint16_t m_type;
};
template<int TYPE, typename IMPL>
class MessageTmpl: public Message
{
enum { _MESSAGE_ID = TYPE };
public:
static Message* Create() { return new IMPL(); }
static const uint16_t MESSAGE_ID; // for registration
protected:
MessageTmpl() { m_type = MESSAGE_ID; } //use parameter to instanciate template
};
typedef Message* (*t_pfFactory)();
class MessageFactory·
{
public:
static uint16_t Register(uint16_t msgid, t_pfFactory factoryMethod)
{
printf("Registering constructor for msg id %d\n", msgid);
m_List[msgid] = factoryMethod;
return msgid;
}
static Message *Create(uint16_t msgid)
{
return m_List[msgid]();
}
static t_pfFactory m_List[65536];
};
template <int TYPE, typename IMPL>
const uint16_t MessageTmpl<TYPE, IMPL >::MESSAGE_ID = MessageFactory::Register(
MessageTmpl<TYPE, IMPL >::_MESSAGE_ID, &MessageTmpl<TYPE, IMPL >::Create);
class PingMessage: public MessageTmpl < 10, PingMessage >
{·
public:
PingMessage() {}
virtual void say() { printf("Ping\n"); }
};
class PongMessage: public MessageTmpl < 11, PongMessage >
{·
public:
PongMessage() {}
virtual void say() { printf("Pong\n"); }
};
t_pfFactory MessageFactory::m_List[65536];
int main(int argc, char **argv)
{
Message *msg1;
Message *msg2;
msg1 = MessageFactory::Create(10);
msg1->say();
msg2 = MessageFactory::Create(11);
msg2->say();
delete msg1;
delete msg2;
return 0;
}
The template here does the magic by registering into the MessageFactory class, all new Message classes (e.g. PingMessage and PongMessage) that subclass from MessageTmpl.
This works great and simplifies code maintenance but I still have some questions about this technique:
Is this a known technique/pattern? what is the name? I want to search more info
about it.
I want to make the array for storing new constructors MessageFactory::m_List[65536]
a std::map but doing so causes the program to segfault even before reaching main().
Creating an array of 65536 elements is overkill but I have not found a way to
make this a dynamic container.
For all message classes that are subclasses of MessageTmpl I have to implement
the constructor. If not it won't register in the MessageFactory.
For example commenting the constructor of the PongMessage:
class PongMessage: public MessageTmpl < 11, PongMessage >
{
public:
//PongMessage() {} /* HERE */
virtual void say() { printf("Pong\n"); }
};
would result in the PongMessage class not being registered by the MessageFactory and the
program would segfault in the MessageFactory::Create(11) line. The question is
why the class won't register? Having to add the empty implementation of the 100+
messages I need feels inefficient and unnecessary.