C++ using a vector of base classes to automate running several derived but with access to derived class attributes

84 Views Asked by At

I am in a situation where I need to run several image processing filters F1, F2 etc., all deriving from base class Filter and each overriding its virtual method run(). Each derived filter has a distinct set of parameters, int, double, bool. I thought I would use a vector<unique_ptr<Filter>> and store each F1, F2 in it, as they all derive from Filter. And then loop over the vector and run each item's run() which will be the overwritten version of each derived class. And that works fine.

BUT! my problem is how to set filter parameters for each derived class F1, F2 without casting.

Ideally, I would like the compiler to recognise what derived class each item of the vector is and do the cast itself. But from what I researched so far, this is not possible.

To add insult to injury, similar questions to mine, typically, get the response that this is not a good design (for example here: How to get access to the attributes of an derived class from the base class in C++?).

So, my question is what is a better design that this? And if there is not, how can vector items be recognised as derived class pointers without that cast.

Right now I have something like this:

// g++ test.cpp && a.out
#include <iostream>
#include <string>
#include <vector>
#include <memory>

class Filter {
  public:
    Filter(void){ std::cout << "Filter::Filter() : called" << std::endl; }
    virtual void run(int x) = 0;
};

class Filters {
  public:
    std::vector<std::unique_ptr<Filter>> f;
};

class F1 : public Filter {
  public:
    void run(int x){ std::cout << "F1::run(): p1=" << myparam1 << std::endl; }
    void param1(int x){ myparam1 = x; }
    int param1(void){ return myparam1; }
  private:
    int myparam1 = 12;
};
class F2 : public Filter {
  public:
    void run(int x){ std::cout << "F2::run(): p2=" << myparam2 << std::endl; }
    void param2(double x){ myparam2 = x; }
    double param2(void){ return myparam2; }
  private:
    double myparam2 = 11.0;
};


int main(void){
  Filters fs;

  fs.f.push_back(std::make_unique<F1>());
  // fails because it is not F1 but Filter class
  //fs.f.back()->param1(200);

  fs.f.push_back(std::make_unique<F2>());
  dynamic_cast<F2*>(fs.f.back().get())->param2(100.0);

  for(std::unique_ptr<Filter> &f : fs.f){
    f.get()->run(10);
  }
}
1

There are 1 best solutions below

0
Jarod42 On

Just insert fully initialized element in your vector, so

int main()
{
  Filters fs;

  auto f1 = std::make_unique<F1>();
  f1->param1(200);
  fs.f.push_back(std::move(f1));

  auto f2 = std::make_unique<F2>();
  f2->param2(100.0);
  fs.f.push_back(std::move(f2));


  for(std::unique_ptr<Filter> &f : fs.f){
    f.get()->run(10);
  }
}

Possibly, you might add constructor to allow to set those values directly:

class F1 : public Filter {
public:
    explicit F1(int value) : myparam1(x) {}
    // ...
};

and then

fs.f.push_back(std::make_unique<F1>(200));