Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to store an "object type" in a std::map?

Tags:

c++

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...

like image 755
Chris Avatar asked Aug 19 '12 11:08

Chris


1 Answers

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]();
like image 195
Christian Stieber Avatar answered Nov 03 '22 14:11

Christian Stieber