how to get an object instance from its class name?

D

dotNeter

Is there any way for this?

If I get a string representing a class name, how do I new an instance
upon this name?
 
M

Michael DOUBEZ

dotNeter a écrit :
Is there any way for this?

If I get a string representing a class name, how do I new an instance
upon this name?

There is no native way. You have to use a factory i.e. an object that
knows how to build an object from a parameter. That also means that all
such object have a common ancestor.

There are many way to implement such a factory (builder, prototype ...).
But you have to know the types in advance or have a way to register
them into the factory.

An alternative non-portable way is to put those classes in a dynamic
library and define for each a factory function (make_<Class>() by
example) and then use the dlopen(), dlsym() calls to open the library
and call the factory function.

Michael
 
M

mdatye

Another, evil ... very very evil way to write a macro of kind

#define BAD_NEW(name) new #name

(you can explore more if you are free and nothing else to do!)

which has its own set of problems and issues but could be used in case
you don't want to learn anything new... that said, what Michael has
written is more apt and right way to go.

~M
 
O

owebeeone

Is there any way for this?

If I get a string representing a class name, how do I new an instance
upon this name?

The only standard C++ way is to use factories. Austria C++ has a
generic factory system that might help you.
 
D

dotNeter

dotNeter a écrit :



There is no native way. You have to use a factory i.e. an object that
knows how to build an object from a parameter. That also means that all
such object have a common ancestor.

There are many way to implement such a factory (builder, prototype ...).
   But you have to know the types in advance or have a way to register
them into the factory.

An alternative non-portable way is to put those classes in a dynamic
library and define for each a factory function (make_<Class>() by
example) and then use the dlopen(), dlsym() calls to open the library
and call the factory function.

Michael

This is very fascinating. I've spent some time on factory pattern, but
I don't like to write so many switch-case in my system.
One further question is that if it truly fit my future requirement
since the classes may get enriched someday.
 
D

dotNeter

Another, evil ... very very evil way to write a macro of kind

#define BAD_NEW(name)  new #name

(you can explore more if you are free and nothing else to do!)

which has its own set of problems and issues but could be used in case
you don't want to learn anything new... that said, what Michael has
written is more apt and right way to go.

~M

yep, this is really tricky. I won't prefer this way. Thanks anyway.
 
R

red floyd

dotNeter said:
This is very fascinating. I've spent some time on factory pattern, but
I don't like to write so many switch-case in my system.
One further question is that if it truly fit my future requirement
since the classes may get enriched someday.

Don't use a switch-case. Use a std::map.

E.g.:

class base { };
class derived1 : public base { };
class derived2 : public base { };
....
class derivedn : public base { };

typedef base (*createfunc )(/*params if needed*/);
std::map<std::string, createfunc> factory_map;

then look up in the map instead of using a case statement.
 
M

Michael DOUBEZ

dotNeter a écrit :
This is very fascinating. I've spent some time on factory pattern, but
I don't like to write so many switch-case in my system.
One further question is that if it truly fit my future requirement
since the classes may get enriched someday.

In the case of the dynamic library, you don't need a switch case statement.
Let say that you have a plugin directory with shared lib of the name of
the class:
plugin/class1.so
plugin/class2.so
plugin/class3.so

Then if you have the string "class1(1,2,toto)"

you do:
typedef DynObject* (*make_type_t)(int,char**);

void* handle = dlopen("plugin/class1.so",RTLD_LAZY);
DynObject sym = reintrepret_cast<make_type_t>(dlsym(handle,"make_class1"));

and then
//argc=4 argv={"class1","1","2","toto"}; from parser
DynObject* obj=(*sym)(argc,argv);

And you get a dynamic object of class DynObject that you can dynamically
cast to class1 if you need to.

Instead of redoing that every time, you can create a nice portable
library that reference count the dlopens and manage all the stuff around
that. There should be some library available on the net (I have not
search but there should be).

At worse, you can have a look into the service system of the ACE library
which uses this pattern for its service configuration.

Michael
 
J

James Kanze

This is very fascinating. I've spent some time on factory
pattern, but I don't like to write so many switch-case in my
system. One further question is that if it truly fit my
future requirement since the classes may get enriched someday.

The usual solution is to use some sort of dynamic registration
with a map. And you don't need to use dynamic linking for it to
work. Basically, for each type, you define a static factory
object for that specific type, whose constructor registers it
with the factory map. Roughly speaking:

class AbstractFactory
{
public:
virtual ~AbstractFactory() {}
Base* create() const = 0 ;

protected:
AbstractFactory( std::string const&
typename ) ;
} ;

class FactoryMap
{
typedef std::map< std::string, AbstractFactory const* >
Map ;

public:
static FactoryMap&
instance() ;
void enrol( std::string const& typename,
AbstractFactory const* factory ) ;
Base* create( std::string const& typename ) const ;
private:
Map myMap ;
// plus the usual singleton stuff...
} ;

AbstractFactory::AbstractFactory(
std::string const& typename )
{
FactoryMap::instance().enrol( typename, this ) ;
}

void
FactoryMap::enrol(
std::string const& typename,
AbstractFactory const*
factory )
{
assert( myMap.find( typename ) == myMap.end() ) ;
myMap.insert( Map::value_type( typename, factory ) ) ;
}

Base*
FactoryMap::create(
std::string const& typename ) const
{
Map::const_iterator entry
= myMap.find( typename ) ;
return entry == myMap.end()
? NULL
: entry->second->create() ;
}
 
J

James Kanze

Don't use a switch-case. Use a std::map.

class base { };
class derived1 : public base { };
class derived2 : public base { };
...
class derivedn : public base { };
typedef base (*createfunc )(/*params if needed*/);
std::map<std::string, createfunc> factory_map;
then look up in the map instead of using a case statement.

If you use polymorphic static objects, then you can arrange for
the map to be initialized automatically. (Of course, you can do
that using a map of functions as well, but it's a little bit
more obscure, and IMHO, easier to forget one.)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,020
Latest member
GenesisGai

Latest Threads

Top