|
The Vector::add() method imposes a constraint on the
contained type: T must implement an add()
method for Vector::add() to be well-formed.
Now suppose that 90% of the methods in Vector don't
actually require the T::add() method to be implemented
and that the contained type T does in fact not
implement add(). Is there any hope of creating a vector
of T's? There are several trivial solutions to the
problem, and as far as I can tell, none of them are suitable, so
I resort to conditional interfaces.
Vector which impose different requirements on the
contained type. For example, we could write a
Vector_without_add<T> which implements all of
Vector<T> except for the add()
method. This is trivially accomplished using inheritance:
|
Unfortunately, using inheritance as a code reuse mechanism doesn't help at all in the more general case when the contained type can influence the interface of the container in more than one way.
For example, using inheritance how do you deal with a
Vector::mult() method which must invoke
T::mult()? Certainly, we could create a
Vector_simple without either add() or
multiply(), then derive Vector_with_add and
Vector_with_mult, AND
Vector_with_add_and_mult:
|
This works for two constraints, but the number of classes we have to define now grows exponentially with the number of switchable features. Besides, we've required the user to wade through many classes and pick the right combination of keywords. Inheritance is clearly not an acceptable code reuse tool for this situation, so we have to try something else.
Vector<T_without_add> generates errors only
if Vector<T_without_add>::add() is called. You get
compile-time error which points somewhere in the middle of
Vector::add() (specifically, at the line
sum[i]=add(a[i],self[i]) above).
This solution is nice because you don't get a runtime error, but it's a pain in every other way because template compile-time bugs are REALLY difficult to track down.
T ALWAYS provide an
add() method, even if it's a stub that throws a
NotImplemented exception.
This solution is both lame and stupid because by the time the
programmer realizes that the Vector::add() method doesn't
work, he's already compiled and run the program, and wasted a
significant amount of design, programming, and debugging time. Even
more insidious, since there is no compile time checking, it might be
years before the call to Vector::add() is made, and the
program crashes on the user's lap. Deeply lobotomized monkeys seem to
like this technique however.
Throwing NotImplemented is much worse than not
implementing the method, as we are converting a statically checkable
problem into a run-time check. If we go for that kind of solution, we
might as well put our coats back on and program in lisp.
Container which derives from
class Base1 or Base2 depending on some
condition:
|
where META_IF is a cleverly constructed template class
which uses partial specialization to select between its second and third
template arguments:
|
Equipped with this concept and multiple inheritance, it's pretty easy to implement a conditional interface.
Container based on a condition. As a first
step to solving our problem, let's assume we conditionally inherit
from forwarder classes:
|
where a typical forwarder looks like:
|
If T::HasAdd is true, a call to
Container::add() is actually a call to
mult_forwarder::add(), which forwards the call to
Container::_add() by calling the pure virtual function
mult_forwarder::_add().
If T::HasAdd is false, Container inherits from
unimplemented, which does not implement any methods, and hence
add() does not appear in Container (although
_add() is still there).
This simple example which
illustrates the concept, using the COND_INTERFACE construct.
yaroslav> so you may have just discovered a use for multiple inheritance.