tags:

views:

88

answers:

4

Hi, I am hoping this is possible in C++.

I have a list of classes that inherit from a common base class (Base). All of these will be compiled as part of the library linked to my program.

I could be adding more classes that derive from Base and will be recompiling everything after I do this.

When my program starts it will gather a vector of all classes (using some preprocessing method I have already implemented) that implement from Base.

What I want to do (you have probably guessed by now) is to create objects from these classes just based on the string names. Is this possible?

Regards

Mark

+2  A: 

Well, if you have a preprocessed list of all classes then you can create a construction object that will "know" each of those classes and will construct (by manually searching through the list) them upon request.

Let_Me_Be
This is what I think im looking for. I could make the preprocessor generate code based on the classes it finds, pre compilation.
Mark
@Mark Well the code is identical to what dgnorton posted, you will just generate it from your preprocessed list.
Let_Me_Be
Ok now I need a way of running the preprocessing from cmake before compilation. This looks like the way http://www.vtk.org/Wiki/CMake_FAQ#How_do_I_generate_an_executable.2C_then_use_the_executable_to_generate_a_file.3F
Mark
+2  A: 

Depends on what you're trying to do. There are probably better ways to do what you need to do but here's an oversimplified example...

#include <string>

using namespace std;

class b1{};

class d1 : public b1{};

class d2 : public b1{};

...

int main(int argc, char* argv[]) {
   string name(argv[1]);

   b1* b;

   if (name == "d1")
      b = new d1;
   else if (name == "d2")
      b = new d2;
}
dgnorton
Then I need to add and else everytime I add a new class. Not really what I was looking for.
Mark
A: 

Sure, but not the way you would like. C++ has no reflection, so you have to build the mechanism yourself: (See below for EDIT which implements a map of factory pointers)

#include <string>
#include <vector>
#include <memory>
using namespace std;

class Base
{
};

class Dog : public Base
{
};

class Cat : public Base
{
};

int main()
{
    typedef vector<string> strings;
    strings names;
    names.push_back("Dog");
    names.push_back("Cat");
    names.push_back("Dog");
    names.push_back("Cat");

    typedef vector<Base*> Bases;
    Bases my_objs;

    for( strings::const_iterator it = names.begin(), it_end = names.end(); it != it_end; ++it )
    {
        if( *it == "Dog" )
            my_objs.push_back(new Dog);
        else if( *it == "Cat" )
            my_objs.push_back(new Cat);

    }
}

EDIT:

Any solution you come up with in C++ is going to be fundamentally some variation of the above. One common variation is to try to get rid of the if block by doing some kind of lookup. This can be implemented using a map object which links the name of the object to a function that instantiates it. One thing to note about this approach is that the function pointers in the map have to have the same signature, meaning theDog and Cat factory methods have to take the same number and type of parameters. This is a problem that can be solved using Boost::Any (link) or other heroic methods, but that's beyond the scope of this post.

Here's a solution that implements a map of factory method pointers:

#include <string>
#include <vector>
#include <map>
using namespace std;

class Base
{
public:
    virtual ~Base()  {};
};

class Dog : public Base
{
public:
    static Base* make_dog() { return new Dog; }

};

class Cat : public Base
{
public:
    static Base* make_cat() { return new Cat; }
};

typedef Base*(*ObjFactoryFn)();
typedef map<string, ObjFactoryFn> Factories;


int main()
{
    // set up the map of object factory pointers
    Factories factories;
    factories["Dog"] = &Dog::make_dog;
    factories["Cat"] = &Cat::make_cat;

    // build list of objects we want
    typedef vector<string> strings;
    strings names;
    names.push_back("Dog");
    names.push_back("Cat");
    names.push_back("Dog");
    names.push_back("Cat");

    // build objects using the factory map
    for( strings::const_iterator it = names.begin(), it_end = names.end(); it != it_end; ++it )
    {
        // construct
        Base* my_obj = factories[*it]();    // this should use map's "find" method for robustness
        // destroy
        delete my_obj;
    }

}
John Dibling
You have failed to understand the question. Anything that requires me to add code after creating the class is not what I am looking for. I have found the answer, it involves generating code pre compilation, based on the list of classes. This can be hooked into cmake to make it seamless.
Mark
Perhaps you've failed to understand the answer? Any "code pre-compilation" is generating something equivalent to what John has recommended. Even if it's a pre-processor macro or a macro executed by cmake, somewhere somehow you are adding something to make the system aware of your new classes. Your question doesn't explain the constraints on where that can occur. This response clearly took some time to write out and answers your question (as posted) perfectly. Stick around, feel welcome, but show a little gratitude to people willing to teach you _exactly_ what you asked to learn about.
Darryl
@Mark: I'd be interested in seeing the pre-compilation generation of code you've come up with to see how it's different that what I've posted. I'd also be interested in knowing why you feel it necessary to respond with so much attitude to someone who took 20 minutes out of his day to try to help you in good faith. I assume you realize we are unpaid.
John Dibling
Sorry I apologise for my earlier comment, the solution provided is very helpful and I appreciate the time involved to produce it. Again sorry.
Mark
A: 

Yes, it's very easy to do. You'll need to follow the factory pattern. Optionally, you could create a creation function for each object like so:

MyBase* createMyDerived(){ return new MyDerived(); }
MyBase* createAnother(){ return new Another(); }

and so on and so forth as long as they all have the same signature. Next, you'll create a map

typedef MyBase* factoryFunc(void);
map<string, factoryFunc> factoryMap;
factoryMap.insert( make_pair( "MyDerived", &createMyDerived ) );

if it's not found within the map you'll know immediately and you'll have to set up logic to handle it.

Then simply call your function as so:

 MyBase* createObject(string _name){
     map<string, factoryFunc>::iterator found = factoryFunc.find( _name );
     if( found != factoryFunc.end() ){
         return *found();
     }

     return NULL;
 }
wheaties
Then I need to add a line for each new class???
Mark
Yes, you'll have to `insert` each new class and provide a function to create it.
wheaties