6

Assume I have a hierarchy of classes:

class Shape {
};

class Circle : public Shape {
}

class Square : public Shape {
}

... hundreds of other shapes continue on...

When given the name of a shape class as a string, I need to instantiate objects of that class.

In java, I can do something like this (pseudo code!)

Shape createShape(String name) {
    return new Class.forName(name);
}

But in C++, I have to do this: (pseudo code!)

Shape * createShape(const string &name) {
    if (name.compare("Circle") == 0) {  
        return new Circle();
    }
    else if (name.compare("Square") == 0) {
        return new Square();
    }
    else if ... //hundreds of else if continues, one for each shape
}

Is there any better way in C++ to handle situation like this?

Paul Manta
  • 30,618
  • 31
  • 128
  • 208
ROTOGG
  • 1,106
  • 1
  • 7
  • 17
  • Yes, it basically is at this level. I don't think, however, that at the highest design level, you **have to** do something like this. –  Jul 26 '13 at 04:56
  • Yeah. There's no builtin way for C++ to convert a string containing the type of a class to the type. – sashang Jul 26 '13 at 04:58
  • I think in cases like this unless you are getting the data externally just decide what class to make instead of creating the string – aaronman Jul 26 '13 at 04:59
  • 3
    This _may_ be bad design if you're not interfacing with another language or some external data (the only reason I can think of to construct a class by a dynamic name?). I'd prefer an `unordered_map` but yea, you've got the idea. – Cory Nelson Jul 26 '13 at 05:03
  • Can't wait for reflection (they said 2014 standard right?) – Sellorio Jul 26 '13 at 05:04
  • 1
    Mr. Universe: I wouldn't hold my breath for this feature to be added to C++. The entire concept of static and dynamic linkage makes this a longshot at best – Mooing Duck Jul 26 '13 at 05:09
  • Class name in the example is indeed external data. – ROTOGG Jul 26 '13 at 05:15
  • @MooingDuck There is some precedent with RTTI, for better or worse. – Cory Nelson Jul 26 '13 at 05:16
  • @MrUniverse: Nobody said anything about reflection in C++14. And it won't be, since that's feature complete at this point. – Nicol Bolas Jul 26 '13 at 05:37
  • @MooingDuck And yet, Objective-C has done a pretty good job of attuning the two. –  Jul 26 '13 at 05:45
  • 2
    If you have hundreds of shapes, you probably don't want to represent each shape as a different class. – CB Bailey Jul 26 '13 at 06:33
  • Btw, in C++ you can compare `std::string` with `operator==`, you don't need `std::string::compare` if all you do is check if they are identical. – Paul Manta Jul 26 '13 at 07:20

6 Answers6

6

It's avoidable using the factory pattern, but you still need a bunch of boilerplate code to get off the ground. For example:

// Class factory functions -- these could also be inlined into their respective
// class definitions using a macro
Shape *createCircle() { return new Circle(); }
Shape *createSquare() { return new Square(); }
// etc.

// Create a map from type name to factory
typedef std::map<std::string, Shape *(*)()> ShapeFactoryMap;
ShapeFactoryMap factoryMap;
factoryMap["Circle"] = &createCircle;
factoryMap["Square"] = &createSquare;
// etc.

Then, when you want to instantiate an object, you can do this:

ShapeFactoryMap::iterator factory = factoryMap.find("Circle");
if (factory != factoryMap.end())
{
    Shape *circle = factory->second();  // Creates a Circle instance
    ...
}
else
{
    // Handle error
}

Whether this is better than just doing a series of if/else... string comparisons is not clear, since it depends on what exactly you're doing to be doing with this.

Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
  • 2
    Perhaps an [`std::unordered_map`](http://en.cppreference.com/w/cpp/container/unordered_map) might help too for potentially faster look-ups. According to [this](http://stackoverflow.com/a/2197015/10320) answer, `std::undordered_map` performed very well in situations where the map's size did not change (at the expense of a noticeable increase in memory usage). – dreamlax Jul 26 '13 at 05:36
  • 3
    `map["circle"] = []{ return new Circle(); };` would be even better, just so in order not to clutter the namespace with stand-alone functions. –  Jul 26 '13 at 05:39
  • @H2CO3 (* "just in order", etc., stupid 5-minute restriction...) –  Jul 26 '13 at 05:46
  • The factory functions need not necessarily be free functions, but could also be static to the class they produce instances of. – arne Jul 26 '13 at 05:51
  • @arne Yes, I know, but still... –  Jul 26 '13 at 05:52
  • 1
    @H2CO3 What do you think of `#define YOUNAMEIT(M_class) factoryMap[#M_class] = []{ return new M_class(); }` then `YOUNAMEIT(Circle);`, `YOUNAMEIT(Square);`, ... (and finally `#undef YOUNAMEIT` of course)? :p – gx_ Jul 26 '13 at 06:58
  • @gx_ Seems reasonable. –  Jul 26 '13 at 07:07
  • 1
    @H2CO3: Wouldn't it need to be `map["Circle"] = []()->Shape*{return new Circle;}` ? `[](){return new Circle;}` can be converted to `Cirle*(*)()`but not `Shape(*)()`. – MSalters Jul 26 '13 at 10:32
  • @MSalters Hm? `new` returns a pointer... (Or am I missing something obvious?) –  Jul 26 '13 at 10:33
  • @MSalters Also, `std::function` can be used as well in so that even pointers aren't needed: [link](http://ideone.com/VyK6G8) –  Jul 26 '13 at 10:36
  • @H2CO3: Yes, that's why I wrote `[]()-> Shape* {...}`. If you let the compiler deduce the return type, it uses the type of `new Circle` which is `Circle*`, not `Shape*`. – MSalters Jul 26 '13 at 10:43
  • This is a bit messy, in the sense that the method building that map needs to know about all relevant classes.Why not have each class add itself there? – einpoklum Dec 12 '15 at 16:09
1

I second Adam Rosenfield's solution using maps. However, a lower level interface to get your higher level functionality is to use a dlsym() lookup.

Assume that your generic Shape interface lies in the file Shape.hpp and has the following form:

class Shape {
public:
    virtual ~Shape () {}
    //...virtual methods
    virtual void draw () const = 0;
};

template <typename DERIVED>
class ShapeBridge : public Shape {
public:
    static Shape * create () { return new DERIVED; }
};

struct ShapeFactory {
    Shape * (*create) ();
};

Suppose you wanted to add a new shape dynamically by creating a new shared object, and then linking it dynamically into your existing running executable. Then, you can now create an abstract factory of sorts, which uses dynamic loading of shared objects to obtain the concrete factory functions:

#include <string>
#include <map>
#include <dlfcn.h>

struct ShapeCreator {
    void *dlhandle_;
    void *factory_;
    ShapeCreator () : dlhandle_(0), factory_(0) {}
    void open (std::string libname) {
        dlhandle_ = dlopen(libname.c_str(), RTLD_LAZY);
        factory_ = dlsym(dlhandle_, "factory");
    }
    void close () { if (dlhandle_) dlclose(dlhandle_); }
    ShapeFactory * factory () const {
        return static_cast<ShapeFactory *>(factory_);
    }
    static Shape * create (std::string name) {
        static std::map<std::string, ShapeCreator> lookup;
        static std::string dir = "./";
        if (lookup[name].factory() == 0) {
            lookup[name].open(dir + name + ".so");
    }
        return lookup[name].factory()->create();
    }
};

Your shared object could have the following implementation:

// gcc -fPIC  -shared -Wl,-export-dynamic -o Circle.so Circle.cpp -lc
#include "Shape.hpp"
#include <iostream>

class Circle : public ShapeBridge<Circle> {
public:
    //..
    void draw () const { std::cout << "I am a circle.\n"; }
};

extern "C" {
    ShapeFactory factory = { Circle::create };
}

Then to dynamically create the shape:

    Shape *s = ShapeCreator::create("Circle");
    s->draw();

Of course, the example is a little more interesting if it actually obtained its name dynamically (like from a configuration file, or from a user input).

Community
  • 1
  • 1
jxh
  • 69,070
  • 8
  • 110
  • 193
0

There is no support for what you are asing in the language. Nevertheless you can use the following pattern to streamline your design:

class Shape
{
    Shape *CreateShape(const char *name)
    {
       // Iterate single linked list of known derived classes.
       Node *item = ListOfDerivedClasses;
       while (item != NULL)
       {
           if (strcmp(item->name, name) == 0)
               return item->factory();
           item = item->next; 
       }
    }

    typedef Shape *CreateShapeInstance();

    struct Node
    {
        char *name;
        CreateShapeInstance *factory;
        Node *next;

        Node(char *n, CreateShapeInstance *f)
        {
            name = n; factory = f;
            next = Shape::ListOfDerivedClasses;
            Shape::ListOfDerivedClasses = this;
        }
    };

    static Node *ListOfDerivedClasses;
};

class Circle : public Shape
{
    static Shape *CreateInstance() { return new Circle(); }
}

static Shape::Node circle_info("Circle", Circle::CreateInstance);

The idea is that the single linked list that contains only static elements is created during initialization of static objects and it is never modified after that. This design allows adding derived classes without modifying the base class while CreateShape in the base class can create any derived class that registered itself in the list.

Kirill Kobelev
  • 10,252
  • 6
  • 30
  • 51
  • Why use a list if the number of shapes is constant (and also why not use `std::list`)? This is at least an O(n) solution where faster alternatives exist. – dreamlax Jul 26 '13 at 05:32
  • `std::list` uses heap allocations while my solution not. Consider this as a pseudo code sample for clarity. Better approach is to use the hash table. I tried to keep my answer short. – Kirill Kobelev Jul 26 '13 at 05:56
0

The main difference is that unlike Java, C++ doesn't have an in-built function like forName(String), which does the task for you. In C++ you have to implement it.

Now it's important how you do that stuff. The proposed way of switch/case is one way, which is straight forward but lengthy way. You can automate the things:

(1) First introduce an intermediate template class, which creates an object, so that you don't have to implement method for each and every class.

template<class Derived>
class ShapeCreator : public Shape {  // This class automates the creations
public:
  static Shape* Create () {
    new Derived();  // Assuming that no-argument default constructor is avaialable
  }
};

class Circle : public ShapeCreator<Circle> {
};

class Square : public ShapeCreator<Square> {
};

//... and so on

(2) Now inside the class Shape, introduce one static std::map, which holds a handle to every derived class.

class Shape {
public:
  typedef std::map<std::sting, Shape* (*)()> ShapeMap;
  static ShapeMap s_ShapeMap;

  static Shape* Create (const std::string name) {
    ShapeMap::iterator it = s_ShapeMap.find(name);
    if(it == s_ShapeMap.end())
      return 0;
    it->second();
  }
};

(3) Populating s_ShapeMap has to be done statically, you can choose to do it before the main() is called (be careful while doing this) or as the first function inside the main(). Use preprocessor trick to automate the things:

#define INIT(SHAPE) Shape::s_ShapeMap[#SHAPE] = &SHAPE::Create
Shape* InitializeShapeMap () {
  INIT(Circle);
  INIT(Square);
  INIT(Triangle);
  // ...
}
#undef INIT

Whenever any new shape is introduced, then just add it as an INIT inside the function.

iammilind
  • 68,093
  • 33
  • 169
  • 336
0

There's no way to do what you want the way it is in Java, but there are ways to make it slightly less painful than a giant switch statement. You will need some kind of factory. Personally I like to use something along these lines:

class ShapeBase
{
};

template<class TShape>
class Shape: public ShapeBase
{
public:
    typedef TShape shape_type;


    template< class TFactory >
    static void registerClass(TFactory* factory)
    {
        factory->registerShape(shape_type::name(), [](){ return new shape_type(); });
    }
};


class Circle: public Shape<Circle>
{
public:
    static const char* name() { return "Circle"; }
};

class Square: public Shape<Square>
{
public:
    static const char* name()  { return "Square"; }
};

class ShapeFactory
{
private:
    typedef std::function<ShapeBase*()> shape_creator;
    std::map<std::string,shape_creator> _creators;

public:
    ShapeFactory()
    {
        registerShapes();
    }

    void registerShapes()
    {
        Square::registerClass(this);
        Circle::registerClass(this);
    }


    void registerShape( const std::string& name, shape_creator creator )
    {
        _creators[name] = creator;
    }

    ShapeBase* create(const std::string& name)
    {
        return _creators[name]();
    }
};

int main( int argc, char** argv )
{
    ShapeFactory factory;

    ShapeBase* circle = factory.create("Circle");
    ShapeBase* square = factory.create("Square");

    return 0;
}

If you can get away with defining all of your Shape objects in an executable component or dynamic library, rather than a static library, then there are tricks that you can use to auto-register your classes with a singleton factory, but I think it's a better idea to do it this way and avoid the singleton.

Gerald
  • 23,011
  • 10
  • 73
  • 102
  • @C. Bailey can you elaborate on the alternative you are suggesting? I do have hundreds of shapes in the problem space. – ROTOGG Jul 26 '13 at 07:15
0

C++ is a 'class based' language which means the structure of a class is only known at compile time. Hence you cannot generate a type at runtime.

It's better to avoid that sort of class instanciation unless you only know the class name at runtime.

If need to do that at large scale, have a look at third-party code generators such as jinja. It'll help you create a factory off a template and a given mapping "string" -> "class name".

Karadur
  • 1,226
  • 9
  • 16