tags:

views:

146

answers:

3

I have an abstract base class

class IThingy
{
  virtual void method1() = 0;
  virtual void method2() = 0;
};

I want to say - "all classes providing a concrete instantiation must provide these static methods too"

I am tempted to do

class IThingy
{
  virtual void method1() = 0;
  virtual void method2() = 0;
  static virtual IThingy Factory() = 0;
};

I know that doesnt compile, and anyway its not clear how to use it even if it did compile. And anyway I can just do

Concrete::Factory(); // concrete is implementation of ITHingy

without mentioning Factory in the base class at all.

But I feel there should be some way of expressing the contract I want the implementations to sign up to.

Is there a well known idiom for this? Or do I just put it in comments? Maybe I should not be trying to force this anyway

Edit: I could feel myself being vague as I typed the question. I just felt there should be some way to express it. Igor gives an elegant answer but in fact it shows that really it doesn't help. I still end up having to do

   IThingy *p;
    if(..)
       p = new Cl1();
    else if(..)
       p = new Cl2();
    else if(..)
       p = new Cl3();
    etc.

I guess reflective languages like c#, python or java could offer a better solution

A: 

There is no sure way to prescribe such a contract in C++, as there is also no way to use this kind of polymorphism, since the line

Concrete::Factory()

is always a static compile-time thing, that is, you cannot write this line where Concrete would be a yet unknown client-provided class.

You can make clients implement this kind of "contract" by making it more convenient than not providing it. For example, you could use CRTP:

class IThingy {...};

template <class Derived>
class AThingy : public IThingy
{
public:
  AThingy() { &Derived::Factory; } // this will fail if there is no Derived::Factory
};

and tell the clients to derived from AThingy<their_class_name> (you could enforce this with constructor visibility tweaking, but you cannot ensure the clients don't lie about their_class_name).

Or you could use the classic solution, create a separate hierarchy of factory classes and ask the clients to provide their ConcreteFactory object to your API.

jpalecek
Nathan Ernst
Sorry, I should have said you cannot do this directly from `IThingy` if `Concrete` has not been defined, as you have mentioned. I meant to say you need to impart a means to `IThingy` to indirectly instantiate `Concrete`.
Nathan Ernst
+3  A: 

The problem that you are having is partly to do with a slight violation a single responsibility principle. You were trying to enforce the object creation through the interface. The interface should instead be more pure and only contain methods that are integral to what the interface is supposed to do.

Instead, you can take the creation out of the interface (the desired virtual static method) and put it into a factory class.

Here is a simple factory implementation that forces a factory method on a derived class.

template <class TClass, class TInterface>
class Factory {
public:
    static TInterface* Create(){return TClass::CreateInternal();}
};

struct IThingy {
    virtual void Method1() = 0;
};

class Thingy : 
    public Factory<Thingy, IThingy>,
    public IThingy {
        //Note the private constructor, forces creation through a factory method
        Thingy(){}
public:
        virtual void Method1(){}
        //Actual factory method that performs work.
        static Thingy* CreateInternal() {return new Thingy();}
};

Usage:

//Thingy thingy; //error C2248: 'Thingy::Thingy' : cannot access private member declared in class 'Thingy'

IThingy* ithingy = Thingy::Create(); //OK

By derinving from Factory<TClass, TInterface>, the derived class is forced to have a CreateInternal method by the compiler. Not deifining it will result in an error like this:

error C2039: 'CreateInternal' : is not a member of 'Thingy'

Igor Zevaka
A: 

Static methods cannot be made virtual (or abstract, for that matter) in C++.

To do what you're intending, you can have have an IThingy::factory method that returns a concrete instance, but you need to somehow provide a means for factory to create the instance. For instance, define a method signature like IThing* (thingy_constructor*)() and have a static call in IThingy that you can pass such a function to that defines how IThingy will construct the factory instance. Then, in a dependent library or class, you can call this method with an appropriate function that, in turn, nows how to properly construct an object implementing your interface.

Supposing you haven't had your factory 'initializer' called, you'd want to take appropriate action, such as throwing an exception.

Nathan Ernst