2

is it possible to return exemplar of object using passed type name (string) in c++? I have some base abstract class Base and a few derivates. Example code:

class Base
{
   /* ... */
};

class Der1 : public Base
{
   /* ... */
};

class Der2 : public Base
{
   /* ... */
};

And I need function like:

Base *objectByType(const std::string &name);

Number of derivates classes are changeable and I don't want to make something like switching of name and returning by hands new object type. Is it possible in c++ to do that automatically anyway?

p.s. usage should looks like:

dynamic_cast<Der1>(objectByType("Der1"));

I need pure c++ code (crossplatform). Using boost is permissible.

idunno
  • 245
  • 1
  • 9
Max Frai
  • 61,946
  • 78
  • 197
  • 306

6 Answers6

2

It's not possible to do this in C++.

One options is to write a factory and switch on the name passed in, but I see you don't want to do that. C++ doesn't provide any real runtime reflection support beyond dynamic_cast, so this type of problem is tough to solve.

Sean
  • 60,939
  • 11
  • 97
  • 136
2

There is a nice trick which allows you to write a factory method without a sequence of if...else if....

(note that, AFAIK, it is indeed not possible to do what you want in C++ as this code is generated in the compile time. A "Factory Method" Design Pattern exists for this purpose)

First, you define a global repository for your derived classes. It can be in the form std::map<std::string, Base*>, i.e. maps a name of the derived class to an instance of that class.

For each derived class you define a default constructor which adds an object of that class to the repository under class's name. You also define a static instance of the class:

// file: der1.h
#include "repository.h"

class Der1: public Base {
public:
  Der1() { repository[std::string("Der1")] = this; }
};

// file: der1.cpp
static Der1 der1Initializer;

Constructors of static variables are run even before main(), so when your main starts you already have the repository initialized with instances of all derived classes.

Your factory method (e.g. Base::getObject(const std::string&)) needs to search the repository map for the class name. It then uses the clone() method of the object it finds to get a new object of the same type. You of course need to implement clone for each subclass.

The advantage of this approach is that when you are adding a new derived class your additions are restricted only to the file(s) implementing the new class. The repository and the factory code will not change. You will still need to recompile your program, of course.

davka
  • 13,974
  • 11
  • 61
  • 86
  • 2
    Default constructor is a BAD way to do this. Just create an ordinary global variable, like an `int`, that is initialized with a call to the factory's setup function. e.g. `static int g_factoryDer1Registration = Der1Factory::Register();` Or create an instance of a nested helper class. But don't create an instance of the type the factory is generating, in order to register the factory, that violates the single responsibility principle in every possible way. – Ben Voigt Jan 16 '11 at 12:50
  • @Ben Voigt: thanks for pointing this out. Indeed, using a dedicated `Der1Factory::Register()` method looks better. – davka Jan 16 '11 at 13:04
  • Good idea, and without switch/if statements. – Max Frai Jan 16 '11 at 13:06
  • How about the order of initialization of static variables? Won't this be a problem if the variable der1Initialzer/g_factoryDer1Registration is initialized before the repository map? – mmmmmmmm Jan 16 '11 at 13:12
  • @rstevens: I suppose you can enforce some order, e.g. if you define a `Repository` (or `Factory`) class with a `static Repository::Register()` method that uses a static local `std::map` – davka Jan 16 '11 at 13:23
0

Yes that is possible! Check this very funny class called Activator You can create everything by Type and string and can even give a List of parameters, so the method will call the appropriate constructor with the best set of arguments.

Marnix
  • 6,384
  • 4
  • 43
  • 78
0

Unless I misunderstood, the typeid keyword should be a part of what you are looking for.

Neal P
  • 599
  • 1
  • 8
  • 16
  • No. `typeid` provides a mapping from types to type-describing objects (including the type name as a string), but the inverse mapping is not available in C++. – Philipp Jan 16 '11 at 12:04
0

It is not possible. You have to write the objectByType function yourself:

Base* objectByType(const std::string& name) {
  if (name == "Der1")
    return new Der1;
  else if (name == "Der2")
    return new Der2;
  // other possible tests
  throw std::invalid_argument("Unknown type name " + name);
}
Philipp
  • 48,066
  • 12
  • 84
  • 109
0

C++ doesn't support reflection.

In my opinion this is the single point where Java beats C++.
(ope not to get too many down votes for this...)

You could achieve something like that by using a custom preprocessor, similar to how MOC does for Qt.

peoro
  • 25,562
  • 20
  • 98
  • 150