I have the following C++ design problem and I would really appreciate any suggestion/solution. Please notice that my background is not in computer science, so I may be missing some obvious solution.
The way I usually separate key components in the code is to define interfaces via abstract classes and pure virtual functions.
Example1:
class B
{
public:
virtual double f( double x ) = 0;
};
class D1 : public B
{
public:
double f( double x ) const
{
return 0.0;
}
};
class D2 : public B
{
public:
double f( double x ) const
{
return 1.0;
}
};
This way I can nicely separate interface from implementation. This approach is also quite fast (and as what I am working on is a numerical library this is important :P).
Now, the problem I am facing is the following one.
I have a set of "functionalities" which can be summarized by functions (defined below) f(), g(), and h() Notice that all these functions will in general differ in arguments and return types.
Suppose I have some code that expects a pointer to an object that implements the functionalities f() and g(). What I would like to do is to being able to pass something which has "more or equal" functionalities, for example something which supports f(), g() and h().
To better explain myself here is some code. Please notice that instead of multiple inheritance I can have used a "nested intheritance" approach, like in boost::operators. The point here is that I will never have the case in which f() is the same as g (). All the features are different. Te problem is that in order to make this work I need to use reinterpret_cast as in the example below (so this is not really a solution):
Example2:
class F {
public:
virtual double f( double x ) = 0;
};
class G {
public:
virtual double g( double x ) = 0;
};
class H {
public:
virtual double h( double x ) = 0;
};
class N {};
template<class T1, class T2=N, class T3=N>
class Feature : public T1 , public T2 , public T3
{
};
template<class T1, class T2>
class Feature<T1,T2,N> : public T1, public T2
{
};
template<class T1>
class Feature<T1,N,N> : public T1
{
};
//Supp for Supports/Implements
class SuppFandG : public Feature<F,G>
{
public:
double f( double x ) { return 0.0; }
double g( double x ) { return 1.0; }
};
class SuppFandH : public Feature<F,H>
{
public:
double f( double x ) { return 0.0; }
double h( double x ) { return 1.0; }
};
class SuppFandGandH : public Feature<F,G,H>
{
public:
double f( double x ) { return 0.0; }
double g( double x ) { return 1.0; }
double h( double x ) { return 2.0; }
};
int main()
{
Feature<F,G>* featureFandGPtr;
Feature<F,H>* featureFandHPtr;
Feature<H,F>* featureHandFPtr;
Feature<F,G,H>* featureFandGandHPtr;
SuppFandGandH suppFandGandH;
featureFandGandHPtr = &suppFandGandH;
//featureFandGPtr = featureFandGandHPtr; //Illegal. static_cast illegal too.
//the reason to do this is that I would like to pass a pointer to an object
//of type Feature<F,G,H> to a function (or constructor) that expects a pointer to Feature<F,G>
featureFandGPtr = reinterpret_cast< Feature<F,G>* >( featureFandGandHPtr );
featureFandHPtr = reinterpret_cast< Feature<F,H>* >( featureFandGandHPtr );
featureHandFPtr = reinterpret_cast< Feature<H,F>* >( featureFandGandHPtr );
featureFandGPtr->f( 1.0 );
featureFandGandHPtr->h( 1.0 );
}
Or I can try to construct a inheritance hierarcy but changing the definition of Feature but the following example makes the Visual studio 2008 professional compiler crash, so I cannot test it.
Example 3:
//This will not work, Visual studio 2008 professional crash.
template<class T1, class T2=N, class T3=N>
class Feature : public Feature<T1,T2> , public Feature<T1,T3> , public Feature<T2,T3>
{
};
template<class T1, class T2>
class Feature<T1,T2,N> : public Feature<T1>, public Feature<T2>
{
};
template<class T1>
class Feature<T1,N,N> : public T1
{
};
With this approach I still have the problems 1) Feature is logically equivalent (for what I want to achieve) to Feature but their types are different. This can however be solved by some fancy metaprogramming using the MPL boost library (always "sort" the types), so for simplicity let's assume this is not a problem.
2) Problem of multiple bases, and I want to avoid virtual inheritance via virtual bases (performance penalty). This is probably solvable by using directives inside the Feature specializations.
Still I am not 100% sure I can make this work and it will not scale well for a large number of features.
In fact the number of elements composing the hierarchi is given by the binomial coefficient, almost factorial:
F - > F (1)
F,G - > FG, F, G (3)
F,G,H -> FGH, FG, GH, FH, F, G, H (7)
I would like to know if there is a solution to the design problem which involves the following conditions:
1) The code should have a runtime performance equivalent to Example 1.
2) I want to be able to specify easily some set of features and being able to "pass in" any pointers to objects that have this (and usually extra) functionality.
3) I want code that depends on features f() and g() not to require re-compiling whenever I consider a new feature h() somewhere else.
4) I do not want to template everything that want to use such features (almost all the code). There should be some kind of "separation", see point 3.
Looking in the (numerical) libraries I have usually found the two approaches:
1) Define a huge abstract base class B that have f(),g(),h(),...... Problems: whenever I want to add a new feature z(), B has to be modified, everything needs to be re-compiled (even if this code does not care about z() at all), all the existing implementations D1, D2,... of B needs to be modified (usually by having them throw an exception for z() apart for the new implementation that supports z()). The solution of enlarging B progressively when I need to add features is not a good one for the problem at hand, as the featurs f() and g() are really "as important" as h() and i(), an neither is "more basic" then the others.
2) Separate all the functionalities and use one pointer for each functionality. However, this is cumbersome for the user (in most situations 4 or more pointers would have to be carried around), and for the problem at hand this approach is not optimal (here really it is 1 object that may or may not do something, in fact calling f() will modify the result obtained by g() and vice-versa).
Thank you in advance for your help.
KRao.