ambiguous access when calling a decorated base class
Question
I have a class that can be decorated with a set of add-on templates to provide additional functionality. Each add-on needs to be able to call the base class and the user needs to be able to call the base class (either directly or using the CMyClass as a proxy). Unfortunately, the compiler can't tell which base class I'm calling and I get ambiguous access errors.
template< class T >
class AddOn_A : public T
{
public:
AddOn_A( int x ) : T( x )
{};
int AddOne()
{
T* pT = static_cast< T* >( this );
return pT->GetValue() + 1;
};
};
template< class T >
class AddOn_B : public T
{
public:
AddOn_B( int x ) : T( x )
{};
int AddTwo()
{
T* pT = static_cast< T* >( this );
return pT->GetValue() + 2;
};
};
class CBase
{
public:
explicit CBase( int x ) : x_( x )
{
};
int GetValue()
{
return x_;
};
private:
int x_;
};
// define an empty AddOn
template< class > struct empty {};
// forward declaration and Add-On defaults
template< template< class > class AddOn1 = empty,
template< class > class AddOn2 = empty,
template< class > class AddOn3 = empty >
class CMyClass;
// specialized template for the default case
template<> class CMyClass< empty, empty, empty > : public CBase
{
public:
CMyClass( int x ) : CBase( x )
{};
};
// actual definition
template< template< class > class AddOn1,
template< class > class AddOn2,
template< class > class AddOn3 >
class CMyClass : public AddOn1< CBase >,
public CMyClass< AddOn2, AddOn3 >
{
public:
CMyClass( int x ) : AddOn1< CBase >( x ),
CMyClass< AddOn2, AddOn3 >( x )
{};
};
int _tmain( int argc, _TCHAR* argv[] )
{
CMyClass< AddOn_A > A( 100 );
// error C2385: ambiguous access of 'GetValue'
// 1> could be the 'GetValue' in base 'CBase'
// 1> or could be the 'GetValue' in base 'CBase'
_ASSERT( A.GetValue() == 100 );
// error C2385: ambiguous access of 'GetValue'
// 1> could be the 'GetValue' in base 'CBase'
// 1> or could be the 'GetValue' in base 'CBase'
_ASSERT( A.AddOne() == A.GetValue() + 1 );
// works
_ASSERT( A.AddOne() == 101 );
CMyClass< AddOn_A, AddOn_B > AB( 100 );
// same errors as above
_ASSERT( AB.GetValue() == 100 );
// same errors as above
_ASSERT( AB.AddTwo() == AB.GetValue() + 2 );
// works
_ASSERT( AB.AddTwo() == 102 );
return 0;
}
Can anybody point out what I may be doing wrong?
Thanks, PaulH
Solution
Well, since I launched on the Decorator approach, I might as well :)
EDIT: let's add the AddOnValues to solve this as well
The problem here is the Multi-Inheritance. Tracing such a diagram is not easy but if you look closely you'll see that CMyClass<AddOn_A>
inherits twice from CBase.
CMyClass<AddOn_A>
<--AddOn_A<CBase>
<--CBase
CMyClass<AddOn_A>
<--CMyclass<empty,empty,empty>
<--CBase
The problem is that you used a policy approach, instead of a Decorator approach. In a proper Decorator approach, the hierarchy is strictly linear and you only have one template parameter at a time. Let's get the basis:
// Note that the static_cast are completely unnecessary
// If you inherit from T then you can freely enjoy
// its public and protected methods
template< class T >
class AddOn_A : public T
{
public:
enum { AddOnValues = T::AddOnValues | 0x01 }; // this hides T::AddOnValues
AddOn_A( int x ) : T( x ) {};
int AddOne()
{
return this->GetValue() + 1;
};
};
template< class T >
class AddOn_B : public T
{
public:
enum { AddOnValues = T::AddOnValues | 0x02 }; // this hides T::AddOnValues
AddOn_B( int x ) : T( x ) {};
int AddTwo()
{
return this->GetValue() + 2;
};
};
class CBase
{
public:
enum { AddOnValues = 0x00 };
explicit CBase( int x ) : x_( x ) {}
virtual ~CBase() {} // virtual destructor for inheritance
int GetValue() const { return x_; }; // const method
private:
int x_;
};
Now we can get to the actual use!
// First, the typedef approach
typedef AddOn_B< AddOn_A< CBase > > CMyClass;
CMyClass myObject(3);
std::cout << myObject.GetValue() << std::endl;
std::cout << myObject.AddOne() << std::endl;
std::cout << myObject.AddTwo() << std::endl;
Quite easy isn't it ? The obvious drawback is that you don't add functionality there...
// I want more!
template < class T >
class CMyClassImpl: public T
{
// Whatever you want
};
CMyClassImpl< AddOn_B< AddOn_A< CBase > > > myObject(3);
Okay... not so beautiful I guess... Even better ? Well, we can just use a wrapper!
// Even better
template <>
class CMyClass: public CMyClassImpl < CBase > {};
template < template <class> class AddOn1>
class CMyClass: public CMyClassImpl <AddOn1 < CBase > > {};
template < template <class> class AddOn1,
template <class> class AddOn2 >
class CMyClass: public CMyClassImpl < AddOn2 < AddOn1< CBase > > > {};
template < template <class> class AddOn1,
template <class> class AddOn2,
template <class> class AddOn3 >
class CMyClass: public CMyClassImpl < AddOn3 < AddOn2< AddOn1< CBase > > > > {};
// Go on with as much specializations as you wish
CMyClass < AddOn_A, AddOn_B > myObject(3);
Of course, the last solution saves typing at the calling site, but you've got to really work on your class :)
Also, you have to repeat the various constructors at each step of the inheritance, which my rapidly prove... boring.
There are preprocessor macros out there, but... last time it took me some 500 lines to generate something quite simple, so don't bother and type, really :)