Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to randomly select a class to instantiate without using switch?

Tags:

c++

types

class

I'm refactoring a single 3000+-line class with a tangled web of conditionals and switches into a set of worker classes. Previously part of the constructor would select which "type" of thing to use via code like the following:

enum Type { FOO, BAR, BAZ };

Type choices[] = { FOO, FOO, BAR, BAZ }; // weighted towards FOO
m_type = choices[rand()%4];

[...later...]

void Run() {
    switch (m_type) {
        case FOO: do_foo(); break;
        case BAR: do_bar(); break;
        case BAZ: do_baz(); break;
    }
}

After refactoring I have separate TypeFoo, TypeBar and TypeBaz classes that each have their own Run() methods to do their job. Sadly, its complicated the class selection code. I don't know of any way to keep a list of possible classes to construct, so I have this:

Type *m_type;

switch (mrand()%4) {
    case 0: case 1: m_type = new TypeFoo(); break;
    case 1:         m_type = new TypeBar(); break;
    case 2:         m_type = new TypeBaz(); break;
}

This is still worth the change because this initialisation code is not called regularly, but its now harder to modify this list, change weightings, etc.

Is there a relatively straightforward to achieve the clarity of the original code?

like image 924
Rob N Avatar asked Oct 18 '11 06:10

Rob N


1 Answers

The answer is : a base class and an array of function pointers can help you do that.

struct Base { virtual ~Base() {} }; //make ~Base() virtual
struct Foo : Base {};
struct Bar : Base {};
struct Baz : Base {};

template<typename T>
Base *Create() { return new T(); }

typedef Base* (*CreateFn)();

CreateFn create[] = 
         {
              &Create<Foo>, 
              &Create<Foo>,   // weighted towards FOO
              &Create<Bar>, 
              &Create<Baz>
         }; 
const size_t fncount = sizeof(create)/sizeof(*create);

Base *Create()
{
   return create[rand() % fncount](); //forward the call
}

Then use it as (ideone demo):

int main() {
        Base *obj = Create();
        //work with obj using the common interface in Base

        delete obj; //ok, 
                    //the virtual ~Base() lets you do it 
                    //in a well-defined way
        return 0;
}   
like image 174
Nawaz Avatar answered Dec 25 '22 17:12

Nawaz