f10w
f10w

Reputation: 1586

How to instantiate different parent/child classes conditionally?

Similar questions were posted and answered here and here but the proposed solutions don't work for me.

I have three classes with multilevel inheritance:

class Model
{
public:
    Model();
    template <typename InputModelType>
    void importModel(const InputModelType &m);
    virtual void process(); 
};

class SpecialModel : public Model
{
public:
    SpecialModel();
    template <typename InputModelType>
    void importSpecialModel(const InputModelType &m);
    virtual void process() override; 
};

class SpecialSpecialModel : public SpecialModel
{
public:
    SpecialModel();
    template <typename InputModelType>
    void importSpecialSpecialModel(const InputModelType &m);
    virtual void process() override; 
};

The child model is a special case of the parent model and can be stored in a simpler structure and thus, can be processed faster.

What I would like to do is to instantiate the model, depending on an input parameter model_type that is specified by the user, something like this:

Model* model;
switch(model_type){
case 1:
    model = new SpecialModel;
    model->importSpecialModel(gm);
    break;
case 2:
    model = new SpecialSpecialModel;
    model->importSpecialSpecialModel(gm);
    break;
default:
    model = new Model;
    model->importModel(gm);
    break;
}

model->process();

Using the above code, I got the following errors:

‘class Model’ has no member named ‘importSpecialModel’

‘class Model’ has no member named ‘importSpecialSpecialModel’

The problem is that, the import functions are templated, therefore it is not valid to define them as virtual functions in the base class and then override in the child class.

Upvotes: 1

Views: 330

Answers (2)

R Sahu
R Sahu

Reputation: 206627

This is a variation of the answer by @Jarod42. You can avoid using a switch statement by using a map of function pointers.

// Independent functions to import the various model types

std::unique_ptr<Model> importPairwiseMRF(GmType gm)
{
   auto model = std::make_unique<PairwiseMRF>();
   model->importSpecialModel(gm);
   return model;
}

std::unique_ptr<Model> importPairwiseMetricMRF(GmType gm)
{
   auto model = std::make_unique<PairwiseMetricMRF>();
   model->importSpecialSpecialModel(gm);
   return model;
}

std::unique_ptr<Model> importModel(GmType gm)
{
   auto model = std::make_unique<Model>();
   model->importModel(gm);
   return model;
}

// Function to import a model given a model_type and the import data.
std::unique_ptr<Model> importModel(int model_type, GmType gm)
{
   // Define a function type that can take gm and return a Model*
   typedef = std::unique_ptr<Model> (*Function)(GmType gm);

   // Create a map of the functions known so far.
   std::map<int, Function> theMap =
   {
      {1, importPairwiseMRF},
      {2, importPairwiseMetricMRF},
      {3, importModel}
   };

   // If there is a function for the given model_type, use it.
   // Otherwise, return nullptr.
   if ( theMap[model_type] != nullptr )
   {
      return theMap[model_type].second(gm);
   }
   else
   {
      return {};
   }
}

And use it as:

auto model = importModel(model_type, gm);
if ( model )
{
   model->process();
}

Upvotes: 1

Jarod42
Jarod42

Reputation: 217448

You can only use function from the static type of the object. You may do the following which use the derived type.

std::unique_ptr<Model> CreateModel(int model_type, const InputModelType &m)
{
    switch(model_type)
    {
        case 1:
        {
            auto model = std::make_unique<PairwiseMRF>();
            model->importSpecialModel(gm);
            return model; // or std::move(model)
        }
        case 2:
        {
            auto model = std::make_unique<PairwiseMetricMRF>();
            model->importSpecialSpecialModel(gm);
            return model; // or std::move(model)
        }
        default:
        {
            auto model = std::make_unique<Model>();
            model->importModel(gm);
            return model;
        }
    }
}

And then

auto model = CreateModel(model_type, gm);
model->process();

Upvotes: 5

Related Questions