views:

238

answers:

5

I have implemented the following interface:

template <typename T>
class Variable
{
public:
  Variable (T v) : m_value (v) {}
  virtual void Callback () = 0;
private:
  T m_value;
};

A proper derived class would be defined like this:

class Derived : public Variable<int>
{
public:
  Derived (int v) : Variable<int> (v) {}
  void Callback () {}
};

However, I would like to derive classes where Callback accepts different parameters (eg: void Callback (int a, int b)). Is there a way to do it?

+1  A: 

don't think this will be possible, because you can never interface it back to Variable. This is what i mean

int a=0; int b = 0;
Variable<int>* derived = new Derived();
derived->Callback(a, b); //this won't compile because Variable<int> does not have Callback with 2 vars.
PoweRoy
That's the point! What you wrote is what I want to achieve.I also thought of using function pointers. Still, pointer functions have to match a function signature (args+return type), so we're back to square one.
Jir
If you want to call `Callback(a,b)` from a reference/pointer to base, then that must be part of the base interface.
David Rodríguez - dribeas
A: 

You will have to add an overload of Callback in the base class that accepts these parameters. It would also be possible to do bad things, like accept a void*, or pass in a raw pointer-to-bytes. The only scenario in which it is valid to alter virtual function signature is when you override the return value to something polymorphic to the original return value, e.g. *this.

DeadMG
+1  A: 

Even if such a thing were possible, it no longer makes much sense to have it as a virtual function, as the derived instantiations couldn't be called polymorphically via a pointer to the base class.

Oli Charlesworth
+2  A: 

This is a problem I ran in a number of times.

This is impossible, and for good reasons, but there are ways to achieve essentially the same thing. Personally, I now use:

struct Base
{
  virtual void execute() = 0;
  virtual ~Base {}
};

class Derived: public Base
{
public:
  Derived(int a, int b): mA(a), mB(b), mR(0) {}

  int getResult() const { return mR; }

  virtual void execute() { mR = mA + mB; }

private:
  int mA, mB, mR;
};

In action:

int main(int argc, char* argv[])
{
  std::unique_ptr<Base> derived(new Derived(1,2));
  derived->execute();
  return 0;
} // main
Matthieu M.
Enlighting solution indeed.Just out of my curiosity, why do you think what I'd like to achieve is impossible 'for good reasons'?
Jir
Because the language has not been designed as such. The only leeway you have on virtual methods is the covariance of the return type. You could (conceivably) ask for a 'parameter' interface class and then use a derived version of this class and proceed to `dynamic_cast` within the method... but ugh, that smells :p
Matthieu M.
+1  A: 

I know this there is an accepted answer, but there is one (ugly) way to achieve what you want, although I would not recommend it:

template <typename T> 
class Variable 
{ 
public: 
  Variable (T v) : m_value (v) {}
  virtual void Callback (const char *values, ...) = 0; 

private: 
  T m_value; 
};

class Derived : public Variable<int> 
{ 
public: 
  Derived (int v) : Variable<int> (v) {} 
  virtual void Callback (const char *values, ...) {
  } 
};  

Now, you can use:

  int a=0; 
  double b = 0; 
  Variable<int>* derived = new Derived(3); 
  derived->Callback("");
  derived->Callback("df", a, b);

You need the values argument in order to obtain the remaining arguments inside the method. You also need to know the argument types, and pass them like printf does.

This method is error prone, as you must match the argument types on values with the real argument types.

J. Calleja
Interesting solution as well! Thanks for pointing it out.I agree that it's more error prone, yet it's also more flexible.
Jir
Technically, you don't need the argument string, so long as each derived class only provides one "overload." You do need some set parameter, but it can be ignored if you know in advance how many parameters will be passed.
Dennis Zickefoose