views:

84

answers:

3

Hello all.

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!

A: 

This problem to me sounds that the abstract factory pattern might be of help. Using this pattern the libraries client can define how your framework builds its types. The client can inject his own subclass of the factory into the framework and define there what types should be build.

What you need is (additionaly) A base class for the factory As a client: Derive a concrete factory A way to inject (as a client) a subtype of the factory into the framework Call the factory metods to create new types.

Does this help you?

schoetbi
Well, this - or similar - is something I have already looked into. The problem is that (like in the cloning situation posted by Charles Bailey) the users still have to implement their own version of a method. Seems inevitable.
Waldo Spek
+5  A: 

The problem is that you don't have the type information to perform the clone as you only have a pointer to base class type and no knowledge as to what derived types have been implemented and are available.

I think there's a reason that 4 hours of googling haven't turned anything up. If you want IStateTransit to be cloneable you have to have an interface where the derived class implementer provides some sort of clone method implementation.

I'm sorry if this isn't what you wanted to hear.

However, implementing a clone method shouldn't be a big burden. Only the class implementor knows how a class can be copied, given a correct copy constructor, clone can be implemented for a leaf-node class like this:

Base* clone() const
{
    return new MyType(*this);
}

You could even macro-alize it; although I wouldn't.

Charles Bailey
I guess you're right. I guess I'll just add an extra pure virtual clone() method to my base class. Arrrgh, I just wish there was cleaner way of arranging things, still. Or some (perhaps dirty) workaround like storing the type used during registering of the IStateTransit, converting it to a string somehow and using that string later on to type cast...Thanks for the effort though.
Waldo Spek
@Waldo Spek: Fundamentally, you don't even know what derived classes there might be, though, so there's no way you can possibly generate correct class copying code before the derived classes have be designed / implemented. You don't _have_ to make the clone function a virtual method, you could store a user supplied function pointer that performed the clone together with the pointer to the class but I don't think that it's going to be neater in any way.
Charles Bailey
@Waldo: It seems you don't understand "statically typed". Types are determined at compile-time, as are the destinations for casts. A string could never be used as a type. At *some point*, you have to delegate to a function that knows the static type and how to copy it. A `clone` method does this.
GMan
What I have omitted is that my IStateTransit class contains some properties. Let's say it contains a boolean. I merely want my new clone/copy to contain the value of that boolean. Now, I would say that since I am creating a new instance of the user defined derived class anyway I at lease have some type info. Since I only care for the info in my abstract base class being copied to a clone, I would think there might be a way. I do very well realize I'm pushing my luck though . (what I actually just want is to copy construct my base class)
Waldo Spek
@GMan: Yes, I understand the concept. But my mind kind of refuses to believe I am stuck to that explanation, since instances of my derived class are created at run-time through the use of a template function. I just wish I could capture the info the user is telling the program, i.e.: factory->RegisterStateTransit<LinearStateTransit>("linear");...capture "LinearStateTransit". But again: I realize I am pushing my luck.
Waldo Spek
@Waldo: Again, that template parameter is specified at compile-time, *not* at run-time.
GMan
@Gman:My mistake. But it is specified at compile-time of the user's program. Not at compile-time of my framework. I guess that's what I mean.
Waldo Spek
+1  A: 

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).

UncleBens
Hey wow, judging from here this might actually be the solution! My fingers are itching, but I'll try and implement this tomorrow and get back to you. Need some serious sleep now :)
Waldo Spek
It works! :D This is exactly what I was looking for. Thanks a million!!!
Waldo Spek