views:

119

answers:

5

In C++ methods can have optional arguments, like this:

void myFunction (int arg1, int arg2=0);

In this case myFunction can be called with 1 integer or with two. If you omit the second integer, the value 0 is passed instead.

I am now looking for ways to obtain the same functionality, but from the callee's side. Suppose I have an interface I need to implment (e.g. an observer interface). At this moment it looks like this:

class IObserver
   {
   public:
      virtual bool operator() (OtherClass *instance) = 0;
   };

Until now, the code calling the observers only wanted to pass the instance argument. But, in order to implement some new functionality, I want to add some other arguments, like this:

class IObserver
   {
   public:
      virtual bool operator() (OtherClass *instance, int firstOptional, int secondOptional) = 0;
   };

But, given that these new arguments only makes sense for a small number of very specific observers, I don't want to add the additional arguments to the other 99% of the observers.

To make the situation more complex, the code to which you give the observer also accepts a lambda expression. So you could write something like this:

addObserver (observee, [](OtherClass *instance)->bool {/*do something here*/ return true;}

The simplicity of the original signature made it nice and easy to pass a lambda expression. With the additional arguments I am now forced to add these arguments to the lambda expressions even if they don't make sense for that observer.

I know the problem can be solved by introducing an additional adapter, but I what I'm actually looking for is a way to have real optional arguments from the callee's side.

Any suggestions on how to solve this in a clean but easy way?

+1  A: 

But you can overload operator() on the number and types of arguments. Why isn't this satisfactory?

zvrba
I'm just guessing- but he probably doesn't want to include arguments that would tightly couple his abstract interface to various implementations of that interface.
luke
Indeed, see my comment on Alf's answer.
Patrick
+3  A: 

If you don't want to implement overloads of the operator bool in every concrete class that implements the interface, then you can just let the interface provide non-virtual overloads that forward the calls to a single virtual member functions (with however many arguments) that must be implemented.

Yeah, it's a bit different from Java interfaces, which cannot provide that kind of functionality.

Another way it's good, is that if you want you can let the non-virtual forwarders do pre- and post-condition checking and/or argument validation, and you can set breakpoints there for debugging. :-)

Cheers & hth.,

Alf P. Steinbach
That's my 'emergency' solution at this moment, but it implies that I write an if-test in the code that calls the observers. Something like: if (observerexcepts1argument) result = observer(instance) else result = observer(instance,arg1,arg2);
Patrick
@Patrick: in that case, would it be acceptable for the code that calls an observer to *always* supply the extra args, but that they're ignored by some observers? Cheers,
Alf P. Steinbach
@Patrick: or alternatively, you can have a single argument pack argument, of a type that provides defaults. That's known as the ["named parameters idiom"](http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.20). There are more fancy ways to do it, including Boost Parameters lib and my cppx Options. Cheers,
Alf P. Steinbach
@Alf. If it would be 'normal' interface that would be the correct approach, but in this case I really want to support simple lambda expressions, which makes this a bit harder. But thanks for the suggestion.
Patrick
+1  A: 

In this case, I would consider abstract concept of additional parameters, since they are for only some specific implementations. Your interface will become like:

class IObserver 
   { 
   public: 
      virtual bool operator() (OtherClass *instance, IObserverArgs* args) = 0;
   }; 

And implementation need additional arguments can have their own IObserverArgs implementation. However, this may not be flexible enough towards lambda.

Sheen
Good idea. Although it's not a appropriate in this case, it's a good idea which I could use in other suggestions. Thanks for the tip.
Patrick
A: 

You could use a nullable type (pointer, or boost::optional) for the optional arguments, defaulting to null. Implementations that care about the arguments can check for null, and substitute whatever default they choose. Implementations that don't care can ignore them, although unfortunately they will still have to include them in their function signatures.

Mike Seymour
A: 

You could consider the version of operator() with extra arguments just as another functionality of your concrete class.

Overload the operator() in the few concrete classes that need extra argument.

class Observer1
{
public:
   virtual bool operator() (OtherClass *instance);
   bool operator() (OtherClass *instance, int firstOptional, int secondOptional);
};
Ugo
This still requires the caller to have an if-test that chooses between calling the first variant or calling the second variant.
Patrick
?? I didn't get it then. I understood that for some specific observers the call to `operator()` was made with extra argument. In this case the caller just use the operator as usual, only providing the extra arguments...
Ugo