Search code examples
c++copyabstract-classclone

Clone abstract base class (without meddling with derived)


I'm experiencing a challenging problem, which has not been solvable - hopefully until now. I'm developing my own framework and therefore trying to offer the user flexibility with all the code complexity under the hood.

First of all I have an abstract base class which users can implement, obviously simplified:

class IStateTransit
{
public:
    bool ConnectionPossible(void) = 0;
}

// A user defines their own class like so
class MyStateTransit : public IStateTransit
{
public:
    bool ConnectionPossible(void){ return true; }
}

Next, I define a factory class. Users can register their own custom state transit objects and refer to them later by simply using a string identifier they have chosen:

class TransitFactory : public Singleton<TransitFactory>
{
public:
    template<typename T> void RegisterStateTransit(const string& name)
    {
        // If the transit type is not already registered, add it.
        if(transits.find(name) == transits.end())
        {
            transits.insert(pair<string, IStateTransit*>(name, new T()));
        };
    }

    IStateTransit* TransitFactory::GetStateTransit(const string& type) const
    {
        return transits.find(type)->second;
    };

private:
    map<string, IStateTransit*> transits;
}

Now the problem is (probably obviously) that whenever a user requests a transit by calling GetStateTransit the system currently keeps returning the same object - a pointer to the same object that is. I want to change this.

PROBLEM: How can I return a new (clone) of the original IStateTransit object without the user having to define their own copy constructor or virtual constructor. Ideally I would somehow like the GetStateTransit method to be able to cast the IStateTransit object down to the derived type it is at runtime and return a clone of that instance. The biggest hurdle is that I do not want the user to have to implement any extra (and probably complex) methods.

4 hours of Googling and trying has led me nowhere. The one who has the answer is a hero!


Solution

  • If I understand the problem correctly, you shouldn't insert new T -s into the map, but rather objects that create new T-s.

    struct ICreateTransit
    {
        virtual ~ICreateTransit() {}
        virtual IStateTransite* create() const = 0;
    
    };
    
    template <class T>
    struct CreateTransit: public ICreateTransit
    {
        virtual IStateTransit* create() const { return new T(); }
    };
    

    And now insert:

    transits.insert(pair<string, ICreateTransit*>(name, new CreateTransit<T>()));
    

    And retrieve "copies" with:

    return transits.find(type)->second->create(); //hopefully with error handling
    

    It shouldn't be impossible to modify StateTransit<T> so it holds a T of which to make copies of, should the default one not do.

    I think the general name for techniques like this is called "type erasure" (derived types "remember" particular types, although the base class is unaware of those).