Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ : calling the right method of a derived class according to the types of the arguments

Let say we have a base class and its two derived classes; The base class owns a method execute and each derived class implements a different version of this method with different types and number of arguments; I can't use a virtual method because signature should be then exactly the same for each derived class; My goal is to offer a base execute method which accepts any kind of arguments, deducts their types, and dispatch them to the right method in the right derived class; I took a look at the Visitor pattern, but I'm looking for a more flexible and elegant solution;

edit : I want to store those classes in a vector, so I need a base class

Here is my try (I don't know what to put in the body of base execute) under gcc 4.5:

class Base {

  public:

  Base();
  ~Base();

  template<typename ...Args>
  void execute(Args... arg)
  {
    //calls the right method
    //execute(int i) or execute(int i, float f)
    //as Args are int or int and float
  }

};

class DerivedA : public Base
{

  public:

  DerivedA();
  ~DerivedA();

  void execute(int i){ /*do something with i*/}

};

class DerivedB : public Base
{

  public:

  DerivedB();
  ~DerivedB();

  void execute(int i, float f){/*do something with i and f*/}

};

void test()
{
  Base* b1 = new DerivedA();
  Base* b2 = new DerivedB();

  int i = 5;
  b1->execute(i); //should call DerivedA.execute(int i)
  float f = 5.0f;
  b2->execute(i, f); //should call DerivedB.execute(int i, float f)

}
like image 748
codablank1 Avatar asked Nov 13 '11 15:11

codablank1


1 Answers

The following uses an intermediate class in between the base and the derived class:

#include <utility>
#include <iostream>
#include <stdexcept>

template<typename... Args> class Intermediate;

class Base
{
public:
  virtual ~Base() {}

  template<typename ...Args>
  void execute(Args... args)
  {
    typedef Intermediate<Args...>* pim;
    if (pim p = dynamic_cast<pim>(this))
    {
      p->execute(std::forward<Args>(args)...);
    }
    else
    {
      throw std::runtime_error("no suitable derived class");
    }
  }
};

template<typename... Args> class Intermediate:
  public Base
{
public:
  virtual void execute(Args ... arg) = 0;
};

class DerivedA:
  public Intermediate<int>
{
public:
  void execute(int i)
  {
    std::cout << "DerivedA: i = " << i << "\n";
  }
};

class DerivedB:
  public Intermediate<int, float>
{
public:
  void execute(int i, float f)
  {
    std::cout << "DerivedB: i = " << i << ", f = " << f << "\n";
  }
};

int main()
{
  Base* b1 = new DerivedA();
  Base* b2 = new DerivedB();

  int i = 5;
  b1->execute(i); //should call DerivedA.execute(int i)
  float f = 5.0f;
  b2->execute(i, f); //should call DerivedB.execute(int i, float f)
}
like image 140
celtschk Avatar answered Oct 26 '22 22:10

celtschk