views:

122

answers:

3

I have a class that can be decorated with a set of add-on templates to provide additional functionality. Each add-on has an identifying addon_value that the base class needs to know.

The code below is an example of what I would like to do. Obviously, the main() function fails to compile. The goal is for CBase::GetValueOfAddOns() to know the value of OR-ing the addon_value for each add-on. The calculation does not actually have to be performed in GetValueOfAddOns(), it just has to be able to get at the result.

template< class T >
class AddOn_A : public T
{
public: 
    AddOn_A( int x ) : T( x ) 
    {};

    enum { addon_value = 0x00000001 };
};

template< class T >
class AddOn_B : public T
{
public: 
    AddOn_B( int x ) : T( x ) 
    {};

    enum { addon_value = 0x00000010 };
};

class CBase
{
public:
    explicit CBase( int x ) : x_( x ) 
    {
        // error LNK2001: unresolved external symbol "public: virtual int __thiscall CBase::GetValueOfAddOns(void)const " (?GetValueOfAddOns@CBase@@UBEHXZ)
        int z = GetValueOfAddOns();
    };

    virtual int GetValueOfAddOns() const = 0;

private:
    int x_;
};

// define an empty AddOn
template< class > class empty 
{
public:
    enum { addon_value = 0x00000000 };
};

// 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 ) 
    {};

    enum { addon_value = 0x00000000 };
};

// 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 )
    {};

    enum { addon_value = AddOn1< CBase >::addon_value | CMyClass< AddOn2, AddOn3 >::addon_value };

    int GetValueOfAddOns() const
    {
        return addon_value;
    }
};

int _tmain( int argc, _TCHAR* argv[] )
{
    CMyClass< AddOn_A > A( 0 );
    _ASSERT( A.GetValueOfAddOns() == AddOn_A< CBase >::addon_value );

    CMyClass< AddOn_A, AddOn_B > AB( 0 );
    _ASSERT( AB.GetValueOfAddOns() == ( AddOn_A< CBase >::addon_value | AddOn_B< CBase >::addon_value ) );

    return 0;
}

Thanks for any help, PaulH

+1  A: 

If you make that function pure virtual, you can implement it in CMyClass, where you have all information available. Just change your empty class to define enum { addon_value = 0x00000000 };, which will make this easier.

sbi
That + Eric's comment above work great, except CBase() still can't "know" the value. I've modified the code in the original post to show the linker error. I get when I try to access GetValueOfAddons() from the CBase::ctor. Any suggestions?Thanks
PaulH
Why would you want to call that function from the ctor of `CBase`? Virtual functions don't work the way most people expect them to from ctors and dtors. If it's just the ctor, you could just pass the value as an argument to it. Otherwise, the virtual function will work.
sbi
+2  A: 

Not sure this is the most elegant way, but the following is fairly straightforward:

Add this to CMyClass:

enum {AddonsValues = AddOn1<CBase>::addon_value | CMyClass<AddOn2, AddOn3>::AddonsValues};

int GetValueOfAddOns()
{
    // return the result of OR-ing the addon_value of each add-on.
    return AddonsValues;
};

and this to the specialized CMyClass<empty, empty, empty>:

enum {AddonsValues = 0};
Éric Malenfant
I dunno. It's no less elegant than the rest of the code at least. I like it. :)
PaulH
A: 

Let's make it better:

See your new question about the problem with hierarchy here I have solved the AddOnValues problem there as well, and there is no need for template meta-programming, really.

Matthieu M.