views:

109

answers:

6

Is it possible at all to control the creation of different classes from configurations?

Say, I have a function which does this which is hard-coded:

BaseClass* getClassObject(int type)
{
    switch (type)
    {
    case 1:
        return new DerivedClass1();
    case 2:
        return new DerivedClass2();
    default:
        return 0;
    }
}

Is it possible to transfer the control to some kind of data structure so that the user just needs to fill in the data structure to control how the function behaves?

A: 

I'm not a C++ guru - I'm coming from a Delphi/C# background. In Delphi or C# I'd use reflection to dynamically create a list of possible classes and then pick the class that was configured in some configuration file. However, I'm not sure whether this is possible in C++ as well?

Thorsten Dittmar
C++ does not support reflection of the kind you are talking about.
anon
Well, that's a shame then ;-) Take my answer off the list of useful replies then...
Thorsten Dittmar
Or, you could just delete it, before you start getting marked down ;)
Patrick McDonald
There is no need to apologise. Because of your reply, I am now investigating using C# for my application. And it is always good to know what is available in other programming languages. In research, finding out what does not work is as useful as finding out what works. It stops others from wasting too much time on a persuing a solution which does not exist.
Andy
+1  A: 

In C++ the names of all types must be known at compile time. You cannot allow the user to invent classes at runtime. The function you posted will work however - the configuration file would contain an integer indicating whther to create DerivedClass1 or DerivedClass2. This is read and used to perform the switch.

anon
Yes, you are right. The value "type" itself can be derived from a configuration data structure/file.
Andy
+1  A: 

You may find useful the Factory method pattern.

Nick D
Er, the function in my question IS the factory pattern! I am trying to see if there is a way which does not involve writing down everything in concrete.
Andy
It looked like a factory pattern, thats why I posted that link. I didn't know if that pattern was familiar to you :)
Nick D
Don't worry. I hope you know what I mean now.
Andy
+2  A: 

As Neil mentioned in C++ you can't invent classes at runtime.

I may have not understood, your question correctly, but if you would like making a function/object behave differently on user provided data , you might try any of following as per your need.

1] As mentioned in your code snippet you can use either switch or conditional statement ( To behave a function differently on user provided data )

or

2] You can use Factory pattern to create required object type ( To create different object as per user provided data, this object can have a common base class to be replaceable in code to generate different behavior )

or

3] You can use combination of a) creator function, b) inheritance and interface c) and dynamic library loading to create and load the appropriate type of object at run time. ( This is to load only specific object or function at run time as per given user data )

nurxb01
Thank you for your reply. I am not interested in inventing classes at runtime. I am interested in ways to make the code more editable (the code is going to work with existable classes only).
Andy
One more way is to use scripting functionality together with c/c++.I'm not sure if the efforts required for inclusion of scripting justify it's benefit or would be an overkill.1] You can use TCL like language with c/c++, which can be used with C with little efforts.or2] You can create your on limited functionality scripting language.fist approach will create external dependency on TCL, but will save you from providing parser and control statement.Second is good till you need limited functionality and don't need much expansion in future.
nurxb01
+1  A: 

From the limited information provided in the question, you might want to consider reading up on and/or considering use of C++ dependency injection containers.

This may well be a little heavy for your requirements, but that is difficult to tell from the question. FYI, Dependency injection (http://martinfowler.com/articles/injection.html) is a term coined by Martin Fowler, and is a more specific kind of Inversion of Control (I'll leave you to further research both topics).

I personally have worked with Castle Windsor, which is a C#/.NET dependency injection container that enables the runtime construction of requested objects via configuration files.

There's a list of C++ dependency injection containers on Wikipedia (http://en.wikipedia.org/wiki/Dependency_injection#Existing_frameworks) - it's been a few years since I did any C++ so I can't tell you much more about them.

Patrick Simpe-Asante
+1  A: 

Another alternative is to use the prototype pattern, where part of the interface to BaseClass is a clone() function. Then you create an array of prototype instances, and call clone on them to get your new object. This is useful if you want to create instances which differ in value as well as ones which differ in behaviour only, and is often a little less work than creating factory methods.

class BaseClass
{
public:
    virtual BaseClass* clone () const = 0;
    virtual ~BaseClass() { }
    virtual void write () const = 0;
};

template <class C>
class Cloneable : public BaseClass
{
public:
    virtual BaseClass* clone () const {
        return new C ( *static_cast<const C*> ( this ) );
    }
};

class TypeA : public Cloneable<TypeA>
{
    int value;

public:
    TypeA ( int value ) : value ( value ) { }

    virtual void write () const {
        std::cout << "TypeA ( " << value << " ) @ " << ( void* ) this << std::endl;
    }
};

class TypeB : public Cloneable<TypeB>
{
public:
    TypeB () { }

    virtual void write () const {
        std::cout << "TypeB @ " << ( void* ) this << std::endl;
    }
};



int main ( int argc, char* argv[] )
{
    std::vector<BaseClass*> prototypes;

    prototypes.push_back ( new TypeA ( 1 ) );
    prototypes.push_back ( new TypeA ( 2 ) );
    prototypes.push_back ( new TypeB );

    // create some objects
    std::vector<BaseClass*> instances;

    for ( size_t i ( 0 ); i < 8; ++i )
        instances.push_back ( prototypes [ i % 3 ] -> clone() );

    for ( size_t i ( 0 ); i < 8; ++i )
        instances[ i ] -> write();

    // todo: delete everything
}
Pete Kirkham