Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically creating an instance of a class from a string containing the class name in C++

Lets say I have a base class with 100 children:

class Base { 
  virtual void feed();
  ...   
};
class Child1 : public Base {
  void feed();  //specific procedure for feeding Child1
  ... 
};
...
class Child100 : public Base { 
  void feed();  //specific procedure for feeding Child100
  ...
};

At runtime I want to read a file that contains which children to create and feed. Lets say I've read the file and the vector of strings "names" contains the names of the child classes (ie. Child1, Child4, Child99). Now I'm going to iterate through these strings, create an instance of the specific child, and feed it with its specific feeding procedure:

vector<Base *> children;    
for (vector<string>::iterator it = names.begin(); it != names.end(); ++it) {
  Base * child = convert_string_to_instance(*it)       
  child->feed()
  children.push_back(child);
}

How would I create the function convert_string_to_instance() such that if it takes in the string "Child1" it returns a "new Child1", if the string argument is "Child4" it returns a "new Child4", etc

<class C *> convert_string_to_instance(string inName) {
  // magic happens
  return new C;  // C = inName

  // <brute force?>
  // if (inName == "Child1")
  //   return new Child1;
  // if (inName == "Child2")
  //   return new Child2;    
  // if (inName == "Child3")
  //   return new Child3;    
  // </brute force>
  }
like image 353
Scott Avatar asked Jun 04 '11 02:06

Scott


People also ask

How do I create an instance of a string?

If you have a class 'ReportClass' is available, you can instantiate it directly as shown below. ReportClass report = new ReportClass(); The code ReportClass report = (ReportClass)Activator. CreateInstance(Type.

How do I create an instance of a class in C#?

In C#, there are two types of class members, instance and static. Instance class members belong to a specific occurrence of a class. Every time you declare an object of a certain class, you create a new instance of that class. The ExampleClass Main() method creates an instance of the OutputClass named outCl.

How dynamically create an object in Java from a class name given as string format?

In order to dynamically create an object in Java from an inner class name, you should use the $ sign. For Example: String className = "MyTestClass"; String innerClassName = "MyInnerTestClass"; String fullPathOfTheClass = "full.


7 Answers

C++ does not provide a method for dynamic construction of class instances like this. However, you may be able to use code generation to generate the "brute force" code (like you showed above) from a list of classes. Then, #include the generated code in your convert_string_to_instance method.

You can also set up your project build system to rebuild the generated code anytime the list of classes changes.

like image 127
Greg Hewgill Avatar answered Oct 09 '22 09:10

Greg Hewgill


I asked a question entitled automatic registration of object creator function with a macro that has the following example program that runs:

#include <map>
#include <string>
#include <iostream>

struct Object{ virtual ~Object() {} }; // base type for all objects

struct ObjectFactory {
  static Object* create(const std::string& id) { // creates an object from a string
    const Creators_t::const_iterator iter = static_creators().find(id);
    return iter == static_creators().end() ? 0 : (*iter->second)(); // if found, execute the creator function pointer
  }

 private:
  typedef Object* Creator_t(); // function pointer to create Object
  typedef std::map<std::string, Creator_t*> Creators_t; // map from id to creator
  static Creators_t& static_creators() { static Creators_t s_creators; return s_creators; } // static instance of map
  template<class T = int> struct Register {
    static Object* create() { return new T(); };
    static Creator_t* init_creator(const std::string& id) { return static_creators()[id] = create; }
    static Creator_t* creator;
  };
};

#define REGISTER_TYPE(T, STR) template<> ObjectFactory::Creator_t* ObjectFactory::Register<T>::creator = ObjectFactory::Register<T>::init_creator(STR)

namespace A { struct DerivedA : public Object { DerivedA() { std::cout << "A::DerivedA constructor\n"; } }; }
REGISTER_TYPE(A::DerivedA, "A");

namespace B { struct DerivedB : public Object { DerivedB() { std::cout << "B::DerivedB constructor\n"; } }; }
REGISTER_TYPE(B::DerivedB, "Bee");

namespace C { struct DerivedC : public Object { DerivedC() { std::cout << "C::DerivedC constructor\n"; } }; }
REGISTER_TYPE(C::DerivedC, "sea");

namespace D { struct DerivedD : public Object { DerivedD() { std::cout << "D::DerivedD constructor\n"; } }; }
REGISTER_TYPE(D::DerivedD, "DEE");

int main(void)
{
  delete ObjectFactory::create("A");
  delete ObjectFactory::create("Bee");
  delete ObjectFactory::create("sea");
  delete ObjectFactory::create("DEE");
  return 0;
}

compile and run output is:

> g++ example2.cpp && ./a.out
A::DerivedA constructor
B::DerivedB constructor
C::DerivedC constructor
D::DerivedD constructor
like image 42
McKay.CPP Avatar answered Oct 09 '22 09:10

McKay.CPP


If you have a lot of classes, you'd usually choose a less brute force approach. A trie or hash_map between class names and factory functions is a good approach.

You can use a codegen approach as suggested by Greg to build this factory table, for example doxygen can parse your source code and output a list of all classes in xml format along with inheritance relationships, so you could easily find all classes deriving from a common "interface" base class.

like image 29
Ben Voigt Avatar answered Oct 09 '22 07:10

Ben Voigt


It sounds like you might be using subclasses for things that should be encoded as fields.

Instead of coding the different behaviour in 100 classes, consider building a look-up table with rules/constants/function-pointers that allow you to implement the proper behaviour from one class.

For example, instead of:

class SmallRedSquare  : public Shape {...};
class SmallBlueSquare : public Shape {...};
class SmallBlueCircle : public Shape {...};
class SmallRedCircle  : public Shape {...};
class BigRedSquare    : public Shape {...};
class BigBlueSquare   : public Shape {...};
class BigBlueCircle   : public Shape {...};
class BigRedCircle    : public Shape {...};

try:

struct ShapeInfo
{
   std::string type;
   Size size;
   Color color;
   Form form;
};

class Shape
{
public:
    Shape(std::string type) : info_(lookupInfoTable(type)) {}

    void draw()
    {
        // Use info_ to draw shape properly.
    }

private:
    ShapeInfo* lookupInfoTable(std::string type) {info_ = ...;}

    ShapeInfo* info_;
    static ShapeInfo infoTable_[];
};

const ShapeInfo Shape::infoTable_[] =
{
    {"SmallRedSquare",  small,  red, &drawSquare},
    {"SmallBlueSquare", small, blue, &drawSquare},
    {"SmallRedCircle",  small,  red, &drawCircle},
    {"SmallBlueCircle", small, blue, &drawCircle},
    {"BigRedSquare",      big,  red, &drawSquare},
    {"BigBlueSquare",     big, blue, &drawSquare},
    {"BigBlueCircle",     big,  red, &drawCircle},
    {"BigRedCircle",      big, blue, &drawCircle}
}

int main()
{
    Shape s1("SmallRedCircle");
    Shape s2("BigBlueSquare");
    s1.draw();
    s2.draw();
}

This idea might not be applicable to your problem, but I figure it couldn't hurt to present it anyway. :-)

My idea is like the Replace Subclass with Fields refactoring, but I go a bit further.

like image 34
Emile Cormier Avatar answered Oct 09 '22 09:10

Emile Cormier


You can abuse the preprocessor and set up some static class members that register your classes with a factory via a hash_map like Ben describes. If you have visual studio, look at how DECLARE_DYNCREATE is implemented in MFC. I've done something similar to implement a class factory. Non-standard for sure but since C++ does not offer any kind of support for this type of mechanism any solution is probably going be non-standard.

Edit

I said in a comment earlier I was working on documenting a scaled down version of something I had done. The scaled down version is still rather large so I posted it here. If there is enough interest I can copy/paste it on this site. Let me know.

like image 32
Dave Rager Avatar answered Oct 09 '22 08:10

Dave Rager


This is the skeleton of a horrible, horrible way to do it:

class Factory {
  public:
    virtual Base * make() = 0;
};

template<typename T> class TemplateFactory : public Factory {
  public:
    virtual Base * make() {
      return dynamic_cast<Base *>(new T());
    }
};

map<string, Factory *> factories;

#define REGISTER(classname) factories[ #classname ] = new TemplateFactory<classname>()

Then call REGISTER(classname); for every relevant derived class of Base, and use factories["classname"]->make() to get a new object of type classname. Obvious flaws with the above code as written include massive potential for memory leaks, and the general awfulness of combining macros and templates.

like image 42
Peter Milley Avatar answered Oct 09 '22 08:10

Peter Milley


Behold the mighty Boost.

The one thing you have to do in order to use my solution is to add a new member to all your classes, and that is a static const string that contains the name of the class. There are probably other ways to do it too, but that's what I have right now.

#include <iostream>
#include <vector>
#include <string>

#include <boost/fusion/container/list/cons.hpp>
#include <boost/fusion/algorithm/iteration/for_each.hpp>
#include <boost/fusion/view/iterator_range.hpp>

using namespace std;
using boost::fusion::cons;


class Base { virtual void feed(){ } };

class Child1 : public Base{
  void feed(){ }

public:
  static const string name_;
};
const string Child1::name_ = "Child1";

class Child3 : public Base{
  void feed(){ }

public:
  static const string name_;
};
const string Child3::name_ = "Child3";

//...
class Child100 : public Base{

  void feed(){ }

public:
  static const string name_;
};
const string Child100::name_ = "Child100";

// This is probably the ugliest part, but I think it's worth it.
typedef cons<Child1, cons<Child3, cons<Child100> > > MyChildClasses;

typedef vector<Base*> Children;
typedef vector<string> Names;

struct CreateObjects{      // a.k.a convert_string_to_instance() in your example.

  CreateObjects(Children& children, string name) : children_(&children), name_(name){ }

  template <class T>
  void operator()(T& cs) const{

    if( name_ == cs.name_ ){
      cout << "Created " << name_ << " object." << endl;
      (*children_).push_back(new T);
    }else{
      cout << name_ << " does NOT match " << cs.name_ << endl;
    }
  }

  Children* children_;
  string name_;
};

int main(int argc, char* argv[]){

  MyChildClasses myClasses;

  Children children;
  Names names;
  names.push_back("Child1");
  names.push_back("Child100");
  names.push_back("Child1");
  names.push_back("Child100");

  // Extra test.
  // string input;
  // cout << "Enter a name of a child class" << endl;
  // cin >> input;
  // names.push_back(input);

  using namespace boost::fusion;
  using boost::fusion::begin;
  using boost::fusion::for_each;

  for(Names::iterator namesIt = names.begin(); namesIt != names.end(); ++namesIt){

    // You have to know how many types there are in the cons at compile time.
    // In this case I have 3; Child1, Child3, and Child100
    boost::fusion::iterator_range<
      result_of::advance_c<result_of::begin<MyChildClasses>::type, 0>::type,
      result_of::advance_c<result_of::begin<MyChildClasses>::type, 3>::type
      > it(advance_c<0 >(begin(myClasses)),
       advance_c<3>(begin(myClasses)));
    for_each(it, CreateObjects(children, *namesIt));
  }

  cout << children.size() << " objects created." << endl;
  return 0;
}
like image 43
Arlen Avatar answered Oct 09 '22 09:10

Arlen