C++ Prototype Wrapper

C++ Prototype Wrapper

Imagine this: You have an existing class hierarchy, for instance a ‘Node’ class from which ‘StringNode’, ‘NumberNode’ and ‘BooleanNode’ classes derive. Now, you want to implement a nicely generic and easily extensible way to create an object of one of the three concrete classes depending on a value (for instance, a string) you get. How to do this nicely in C++?

I had a problem like this, and a core part of the problem is that the existing class hierarchy had nothing like a ‘clone()’ function or so (a ‘virtual constructor’ for the cool kids on the block). I solved this by writing a generic prototype template class:

class NodePrototypeWrapper
{
public:
    virtual Node *clone() const = 0;
};

template <class Product>
class GenericNodePrototypeWrapper : public NodePrototypeWrapper
{
public:
    virtual Node *clone() const {
        return new Product( 0 );
    }
};

class NodeFactory
{
public:
    static NodeFactory &self() {
        if ( !s_instance ) {
            s_instance = new NodeFactory;
        }
        return *s_instance;
    }

    ~NodeFactory() {
        QMap<QString, NodePrototypeWrapper *>::Iterator it, end = m_prototypes.end();
        for ( it = m_prototypes.begin(); it != end; ++it ) {
            delete *it;
        }
    }

    QStringList availableNodes() const { return m_prototypes.keys(); }

    Node *createNode( const QString &name ) const {
        return m_prototypes[ name ]->clone();
    }

    template <class NodeType>
    void registerNodeType() {
        m_prototypes[ NodeType( 0 ).descriptiveName() ] = new GenericNodePrototypeWrapper<NodeType>;
    }

private:
    NodeFactory() {}
    NodeFactory( const NodeFactory &rhs ); // disabled
    NodeFactory &operator=( const NodeFactory &rhs ); // disabled
    static NodeFactory *s_instance;

    QMap<QString, NodePrototypeWrapper *> m_prototypes;
};

So what I can do now is e.g.:

NodeFactory::self().registerNodeType<StringNode>();
NodeFactory::self().registerNodeType<NumberNode>();
NodeFactory::self().registerNodeType<BooleanNode>();

This would probably make a nice macro, too. REGISTER_NODE_TYPE or so. Anyway, lateron I can create nodes using

Node *n = NodeFactory::self().createNode( someString );

In my particular case, I’m using the availableNodes() function of the NodeFactory to get a list of strings which I can show in the GUI, so that the user can select some node to create. I then pass the same string to the createNode() function and voila, it works nicely!

In this trivial example a simple ‘if..else’ might have been easier (but even then, you would have to maintain the code which builds the GUI menu for selecting what node to create, too!) but in my case I have a few dozen node types, and this little trick makes things much easier. 🙂