Long introduction, question is at the end:
Assume I have a base class that's creating an interface
class base
{
public:
virtual ~base();
virtual void calc( int* variables ) = 0;
}
and a few classes that are inherited that do the work (only two are shown here):
class add : public base
{
const int a, b, c;
public:
add( int a_, int b_, int c_ ) : a(a_), b(b_), c(c_) {}
void calc( int* variables )
{
variables[a] = variables[b] + variables[c];
}
}
class inc : public base
{
const int a;
public:
inc( int a_ ) : a(a_) {}
void calc( int* variables )
{
variables[a]++;
}
}
and finally some code that's using this construct:
base* task[2];
task[0] = new add( 0, 1, 2 );
task[1] = new inc( 3 );
int data[4];
/* ... */
for( int i = 0; i < 2; i++ )
task[i]->calc( data );
That's working so far - but it's defining my tasks during compile time. This should be changed to runtime by parsing an input file. Assume the parsing has been done and in std::string
variable command
is the object type (like add
or inc
) and in a std::vector<int> params
are the parameters for the constructor.
Now I could have a long list of
if( command.compare( "add" ) )
task[end] = new add( params[0], params[1], params[2] );
else if( command.compare( "inc" ) )
task[end] = new inc( params[0] );
else /... */
apart from becoming quite unreadable that's just a linear seach. So in the spirit of Why switch statement cannot be applied on strings? I'd like to replace that linear search by a std::map
(or hash map...).
So after this long introduction I can finally come to the question:
How can I define and fill a std::map
so that references(?) to an object are stored in such a way that I can create late on the object dynamically out of that informations?
So with the code above I'd like to do something that might look like this in the end:
// define and fill
std::map< std::sting, ???? > lookup;
lookup["add"] = add;
lookup["inc"] = inc;
/* ... */
// use:
while( linesInConfigAvailable )
{
/* ... parse ... */
switch( params.size() )
{
case 1:
task[end] = new lookup[command]( params[0] );
break;
case 3:
task[end] = new lookup[command]( params[0], params[1], params[2] );
break;
}
}
PS: So far I didn't need RTTI in my code. It'd be nice if that could stay so...
Well, you can't. In C++, classes are not objects, they are more of an abstract construct that only exists within the compilers working data while it compiles.
You could, however, create so called factory functions with a given signature:
class A : public Base
{
public:
static Base* Create() { return new A; }
};
class B : public Base
{
public:
static Base* Create() { return new B; }
};
...
EDIT: if the "Create" functions are uniform like this, you can of course make a template.
Then, you can store function pointers in the map:
typedef Base* (*FactoryType)();
std::map< std::string, FactoryType >
lookup["A"] = A::Create;
lookup["B"] = B::Create;
and call them appropiately:
task[end] = lookup[command]();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With