Visual C++ Developer Center >
Visual C++ Forums
>
Visual C++ Language
>
template recursion to reduce specializations
template recursion to reduce specializations
- I have a set of decorators for a base class. I don't like the way the user would have to call my class, though.
AddOn_A< AddOn_B < CBase > > AB( 100 );
I would prefer something more like this:
CMyClass< AddOn_A, AddOn_B > AB( 100 );
So, I've created a "CMyClass" below to accomplish that. But, I don't want to have to explicitely define every partial specialization manually (the final product will have up to 11 add-ons).
Is there a way I can use template meta-processing to reduce the number of specializations I have to manually define?
template< class T > class AddOn_A : public T { public: AddOn_A( int x ) : T( x ) {}; int AddOne() { return this->GetValue() + 1; }; }; template< class T > class AddOn_B : public T { public: AddOn_B( int x ) : T( x ) {}; int AddTwo() { return this->GetValue() + 2; }; }; class CBase { public: CBase( int x ) : x_( x ) {}; virtual ~CBase() {}; 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; template <> class CMyClass< empty, empty, empty >: public CBase { public: CMyClass( int x ) : CBase( x ) {}; }; template< template< class > class AddOn1 > class CMyClass< AddOn1, empty, empty > : public AddOn1< CBase > { public: CMyClass( int x ) : AddOn1< CBase >( x ) {}; }; template < template< class > class AddOn1, template< class > class AddOn2 > class CMyClass< AddOn1, AddOn2, empty > : public AddOn1< AddOn2< CBase > > { public: CMyClass( int x ) : AddOn1< AddOn2< CBase > >( x ) {}; }; template < template< class > class AddOn1, template< class > class AddOn2, template< class > class AddOn3 > class CMyClass : public AddOn1< AddOn2< AddOn3< CBase > > > { public: CMyClass( int x ) : AddOn1< AddOn2< AddOn3< CBase > > >( x ) {}; }; int _tmain( int argc, _TCHAR* argv[] ) { CMyClass<> nothing( 100 ); _ASSERT( nothing.GetValue() == 100 ); CMyClass< AddOn_A > A( 100 ); _ASSERT( A.GetValue() == 100 ); _ASSERT( A.AddOne() == 101 ); CMyClass< AddOn_A, AddOn_B > AB( 100 ); _ASSERT( AB.GetValue() == 100 ); _ASSERT( AB.AddOne() == 101 ); _ASSERT( AB.AddTwo() == 102 ); return 0; }
Thanks,
PaulH
Answers
- I'll also note that CBase probably shouldn't have a virtual destructor. The whole point of an approach like this is to avoid runtime polymorphism (using static polymorphism instead), so making your class polymorphic with the presence of a virtual destructor (or any other function) defeats this purpose.
- Marked As Answer byRong-Chun ZhangMSFT, ModeratorTuesday, November 03, 2009 10:32 AM
- Sorry for the lack of followup -- I moved last week and forgot all about this thread.The virtual destructor does incur some expense (the class gains a vtable), but the only penalty there is in the size of the class, so unless you're allocating many instances it won't have a measurable impact. However, it seems to me misguided to mix static and dynamic polymorphism, so personally I wouldn't use any virtual functions in the class hierarchy out of principle.The aspect of your last approach that I greatly dislike is that CMyClass ends up in the inheritance hierarchy multiple times, i.e. CMyClass<AddOn_A, AddOn_B> : AddOn_A<CMyClass<AddOn_B> > : CMyClass<AddOn_B> : AddOn_B<CMyClass<> > : CMyClass<> : CBase. This can have negative repercussions -- one that I can think of off the top of my head is that if CMyClass were given data members, those data members would be duplicated and hidden N times (N == number of addons), needlessly making the size of CMyClass very large and expensive to construct/copy. If nothing is added to CMyClass (only to CBase or the CMyClass<empty, empty, empty> specialization) then this doesn't present any issue that I can think of, but the class being in the inheritance hierarchy multiple times still just doesn't smell right to me.Having put some thought into it this time, I came up with the following. This produces a very straightforward inheritance chain -- CMyClass<AddOn_A, AddOn_B> : CMyClass_impl<...> : AddOn_A<AddOn_B<CBase> > : AddOn_B<CBase> : CBase. The advantage over my first approach is that the user doesn't need to use make_addons; the disadvantage is that the number of possible addons must be implemented in two places (CMyClass and CMyClass_seq) rather than just one (make_addons).
#pragma inline_depth(255) #pragma inline_recursion(on) #include <typeinfo> #include <ostream> #include <iostream> #include <string> #include <boost/type_traits/is_same.hpp> #include <boost/mpl/assert.hpp> #include <boost/mpl/placeholders.hpp> #include <boost/mpl/apply.hpp> #include <boost/mpl/not.hpp> #include <boost/mpl/is_sequence.hpp> #include <boost/mpl/copy_if.hpp> #include <boost/mpl/reverse_fold.hpp> #include <boost/mpl/vector.hpp> struct CBase { explicit CBase(int const x) : x_(x) { }; int value() const { return x_; }; private: int x_; }; template<typename T> struct AddOn_A : T { explicit AddOn_A(int const x) : T(x) { }; int add_one() const { return this->value() + 1; }; }; template<typename T> struct AddOn_B : T { explicit AddOn_B(int const x) : T(x) { }; int add_two() const { return this->value() + 2; }; }; template<typename T> struct AddOn_C : T { explicit AddOn_C(int const x) : T(x) { }; int add_three() const { return this->value() + 3; }; }; template<typename T> struct AddOn_D : T { explicit AddOn_D(int const x) : T(x) { }; int add_four() const { return this->value() + 4; }; }; template<typename T> struct AddOn_E : T { explicit AddOn_E(int const x) : T(x) { }; int add_five() const { return this->value() + 5; }; }; template<typename T> struct AddOn_F : T { explicit AddOn_F(int const x) : T(x) { }; int add_six() const { return this->value() + 6; }; }; template<typename T> struct AddOn_G : T { explicit AddOn_G(int const x) : T(x) { }; int add_seven() const { return this->value() + 7; }; }; template<typename T> struct AddOn_H : T { explicit AddOn_H(int const x) : T(x) { }; int add_eight() const { return this->value() + 8; }; }; template<typename T> struct AddOn_I : T { explicit AddOn_I(int const x) : T(x) { }; int add_nine() const { return this->value() + 9; }; }; template<typename T> struct AddOn_J : T { explicit AddOn_J(int const x) : T(x) { }; int add_ten() const { return this->value() + 10; }; }; template<typename T> struct AddOn_K : T { explicit AddOn_K(int const x) : T(x) { }; int add_eleven() const { return this->value() + 11; }; }; namespace detail { namespace mpl = boost::mpl; using mpl::_1; template<template<typename> class T> struct wrap_addon { template<typename U> struct apply { typedef T<U> type; }; }; struct unwrap_addon { template<typename T, typename U> struct apply : mpl::apply<U, T> { }; }; template<typename T> struct no_addon_t; template< template<typename> class T1, template<typename> class T2, template<typename> class T3, template<typename> class T4, template<typename> class T5, template<typename> class T6, template<typename> class T7, template<typename> class T8, template<typename> class T9, template<typename> class T10, template<typename> class T11 > struct CMyClass_seq : mpl::copy_if< mpl::vector< wrap_addon<T1>, wrap_addon<T2>, wrap_addon<T3>, wrap_addon<T4>, wrap_addon<T5>, wrap_addon<T6>, wrap_addon<T7>, wrap_addon<T8>, wrap_addon<T9>, wrap_addon<T10>, wrap_addon<T11> >, mpl::not_<boost::is_same<_1, wrap_addon<no_addon_t> > > >::type { }; template<typename Seq> class CMyClass_impl : public mpl::reverse_fold<Seq, CBase, unwrap_addon>::type { BOOST_MPL_ASSERT_MSG(mpl::is_sequence<Seq>::value, NOT_A_VALID_ADDON_SEQUENCE, (Seq)); protected: typedef CMyClass_impl base_type; typedef typename mpl::reverse_fold<Seq, CBase, unwrap_addon>::type inheritance_chain_t; public: explicit CMyClass_impl(int const x) : inheritance_chain_t(x) { } }; } template< template<typename> class T1 = detail::no_addon_t, template<typename> class T2 = detail::no_addon_t, template<typename> class T3 = detail::no_addon_t, template<typename> class T4 = detail::no_addon_t, template<typename> class T5 = detail::no_addon_t, template<typename> class T6 = detail::no_addon_t, template<typename> class T7 = detail::no_addon_t, template<typename> class T8 = detail::no_addon_t, template<typename> class T9 = detail::no_addon_t, template<typename> class T10 = detail::no_addon_t, template<typename> class T11 = detail::no_addon_t > class CMyClass : public detail::CMyClass_impl<detail::CMyClass_seq<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> > { std::string inheritance_chain_; public: explicit CMyClass(int const x) : CMyClass::base_type(x), inheritance_chain_(typeid(CMyClass::inheritance_chain_t).name()) { } std::string const& inheritance_chain() const { return inheritance_chain_; } }; int main() { CMyClass<> nothing(1357); std::cout << "sizeof(nothing) :: " << sizeof(nothing) << '\n' << "nothing.inheritance_chain :: " << nothing.inheritance_chain() << '\n' << "nothing.value :: " << nothing.value() << "\n\n"; CMyClass<AddOn_A> A(2468); std::cout << "sizeof(A) :: " << sizeof(A) << '\n' << "A.inheritance_chain :: " << A.inheritance_chain() << '\n' << "A.value :: " << A.value() << '\n' << "A.add_one :: " << A.add_one() << "\n\n"; CMyClass<AddOn_B> B(3579); std::cout << "sizeof(B) :: " << sizeof(B) << '\n' << "B.inheritance_chain :: " << B.inheritance_chain() << '\n' << "B.value :: " << B.value() << '\n' << "B.add_two :: " << B.add_two() << "\n\n"; CMyClass<AddOn_A, AddOn_B> AB(4680); std::cout << "sizeof(AB) :: " << sizeof(AB) << '\n' << "AB.inheritance_chain :: " << AB.inheritance_chain() << '\n' << "AB.value :: " << AB.value() << '\n' << "AB.add_one :: " << AB.add_one() << '\n' << "AB.add_two :: " << AB.add_two() << "\n\n"; CMyClass<AddOn_A, AddOn_C, AddOn_E, AddOn_G, AddOn_I, AddOn_K> ACEGIK(5791); std::cout << "sizeof(ACEGIK) :: " << sizeof(ACEGIK) << '\n' << "ACEGIK.inheritance_chain :: " << ACEGIK.inheritance_chain() << '\n' << "ACEGIK.value :: " << ACEGIK.value() << '\n' << "ACEGIK.add_one :: " << ACEGIK.add_one() << '\n' << "ACEGIK.add_three :: " << ACEGIK.add_three() << '\n' << "ACEGIK.add_five :: " << ACEGIK.add_five() << '\n' << "ACEGIK.add_seven :: " << ACEGIK.add_seven() << '\n' << "ACEGIK.add_nine :: " << ACEGIK.add_nine() << '\n' << "ACEGIK.add_eleven :: " << ACEGIK.add_eleven() << "\n\n"; CMyClass<AddOn_A, AddOn_B, AddOn_C, AddOn_D, AddOn_E, AddOn_F, AddOn_G, AddOn_H, AddOn_I, AddOn_J, AddOn_K> ABCDEFGHIJK(6802); std::cout << "sizeof(ABCDEFGHIJK) :: " << sizeof(ABCDEFGHIJK) << '\n' << "ABCDEFGHIJK.inheritance_chain :: " << ABCDEFGHIJK.inheritance_chain() << '\n' << "ABCDEFGHIJK.value :: " << ABCDEFGHIJK.value() << '\n' << "ABCDEFGHIJK.add_one :: " << ABCDEFGHIJK.add_one() << '\n' << "ABCDEFGHIJK.add_two :: " << ABCDEFGHIJK.add_two() << '\n' << "ABCDEFGHIJK.add_three :: " << ABCDEFGHIJK.add_three() << '\n' << "ABCDEFGHIJK.add_four :: " << ABCDEFGHIJK.add_four() << '\n' << "ABCDEFGHIJK.add_five :: " << ABCDEFGHIJK.add_five() << '\n' << "ABCDEFGHIJK.add_six :: " << ABCDEFGHIJK.add_six() << '\n' << "ABCDEFGHIJK.add_seven :: " << ABCDEFGHIJK.add_seven() << '\n' << "ABCDEFGHIJK.add_eight :: " << ABCDEFGHIJK.add_eight() << '\n' << "ABCDEFGHIJK.add_nine :: " << ABCDEFGHIJK.add_nine() << '\n' << "ABCDEFGHIJK.add_ten :: " << ABCDEFGHIJK.add_ten() << '\n' << "ABCDEFGHIJK.add_eleven :: " << ABCDEFGHIJK.add_eleven() << std::endl; return 0; }
- Marked As Answer byPaulH79 Tuesday, November 03, 2009 7:42 PM
All Replies
- This is my rushed thought, using boost.mpl. There's probably a better approach (using mpl), but I couldn't spare much time to think about this. An obvious area for improvement is with make_addons -- this could be generated with trivial preprocessor macros to make changing the maximum number of addons much easier.
#include <iostream> #include <ostream> #include <boost/mpl/assert.hpp> #include <boost/mpl/placeholders.hpp> #include <boost/mpl/is_sequence.hpp> #include <boost/mpl/fold.hpp> #include <boost/mpl/remove.hpp> #include <boost/mpl/vector.hpp> template<typename T> class AddOn_A : public T { public: AddOn_A(int x) : T(x) { }; int AddOne() { return this->GetValue() + 1; }; }; template<typename T> class AddOn_B : public T { public: AddOn_B(int x) : T(x) { }; int AddTwo() { return this->GetValue() + 2; }; }; class CBase { public: CBase(int x) : x_(x) { }; virtual ~CBase() { }; int GetValue() { return x_; }; private: int x_; }; template<template<typename> class T> struct wrap_addon { template<typename U> struct apply { typedef T<U> type; }; }; template<typename T> struct empty_type; template< template<typename> class T1 = empty_type, template<typename> class T2 = empty_type, template<typename> class T3 = empty_type, template<typename> class T4 = empty_type, template<typename> class T5 = empty_type, template<typename> class T6 = empty_type, template<typename> class T7 = empty_type, template<typename> class T8 = empty_type, template<typename> class T9 = empty_type, template<typename> class T10 = empty_type, template<typename> class T11 = empty_type > struct make_addons : boost::mpl::remove< boost::mpl::vector< wrap_addon<T1>, wrap_addon<T2>, wrap_addon<T3>, wrap_addon<T4>, wrap_addon<T5>, wrap_addon<T6>, wrap_addon<T7>, wrap_addon<T8>, wrap_addon<T9>, wrap_addon<T10>, wrap_addon<T11> >, wrap_addon<empty_type> >::type { }; struct unwrap_addon { template<typename T, typename U> struct apply : boost::mpl::apply<U, T> { }; }; template<typename Seq> struct CMyClass_impl : boost::mpl::fold<Seq, CBase, unwrap_addon>::type { private: BOOST_MPL_ASSERT_MSG(boost::mpl::is_sequence<Seq>::value, NOT_A_VALID_ADDON_SEQUENCE, (Seq)); typedef typename boost::mpl::fold<Seq, CBase, unwrap_addon>::type base_type; public: CMyClass_impl(int x) : base_type(x) { } }; template<typename Seq = make_addons<> > struct CMyClass : CMyClass_impl<Seq> { CMyClass(int x) : CMyClass_impl<Seq>(x) { } }; int main() { CMyClass<> myclass1(123); std::cout << "myclass1.GetValue: " << myclass1.GetValue() << "\n\n"; CMyClass<make_addons<> > myclass2(234); std::cout << "myclass2.GetValue: " << myclass2.GetValue() << "\n\n"; CMyClass<make_addons<AddOn_A> > myclass3(345); std::cout << "myclass3.GetValue: " << myclass3.GetValue() << '\n' << "myclass3.AddOne: " << myclass3.AddOne() << "\n\n"; CMyClass<make_addons<AddOn_B> > myclass4(456); std::cout << "myclass4.GetValue: " << myclass4.GetValue() << '\n' << "myclass4.AddTwo: " << myclass4.AddTwo() << "\n\n"; CMyClass<make_addons<AddOn_A, AddOn_B> > myclass5(567); std::cout << "myclass5.GetValue: " << myclass5.GetValue() << '\n' << "myclass5.AddOne: " << myclass5.AddOne() << '\n' << "myclass5.AddTwo: " << myclass5.AddTwo() << std::endl; return 0; }
As you can see, CMyClass is instantiated by passing a list of addons via the make_addons helper. If make_addons is given no arguments (or if make_addons is not supplied to CMyClass), the only addon will be CBase.CMyClass_impl is more or less pointless; I wrote it under the assumption that CMyClass will eventually be a full fledged class, and you wouldn't want the mpl-oriented base class logic and MPL_ASSERT uglying it up. This way it's neatly packed away in another class that won't need to be touched unless another constructor is needed, and even that's simplified due to the base_type typedef. - I'll also note that CBase probably shouldn't have a virtual destructor. The whole point of an approach like this is to avoid runtime polymorphism (using static polymorphism instead), so making your class polymorphic with the presence of a virtual destructor (or any other function) defeats this purpose.
- Marked As Answer byRong-Chun ZhangMSFT, ModeratorTuesday, November 03, 2009 10:32 AM
- @ildjarn - I took a close look at your boost::mpl method and I wondered if it isn't doing something like this:
template< class T > class AddOn_A : public T { public: AddOn_A( int x ) : T( x ) {}; int AddOne() { return this->GetValue() + 1; }; }; template< class T > class AddOn_B : public T { public: AddOn_B( int x ) : T( x ) {}; int AddTwo() { return this->GetValue() + 2; }; }; class CBase { public: CBase( int x ) : x_( x ) {}; ~CBase() {}; 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; template <> class CMyClass< empty, empty, empty >: public CBase { public: CMyClass( int x ) : CBase( x ) {}; }; template < template< class > class AddOn1, template< class > class AddOn2, template< class > class AddOn3 > class CMyClass : public AddOn1< CMyClass< AddOn2, AddOn3 > > {<br/>public: CMyClass( int x ) : AddOn1< CMyClass< AddOn2, AddOn3 > >( x ) {}; }; int _tmain( int argc, _TCHAR* argv[] ) { CMyClass<> nothing( 100 ); _ASSERT( nothing.GetValue() == 100 ); CMyClass< AddOn_A > A( 100 ); _ASSERT( A.GetValue() == 100 ); _ASSERT( A.AddOne() == 101 ); CMyClass< AddOn_A, AddOn_B > AB( 100 ); _ASSERT( AB.GetValue() == 100 ); _ASSERT( AB.AddOne() == 101 ); _ASSERT( AB.AddTwo() == 102 ); return 0; }
Not that I have anything against using boost, but I think this reduces the code needed to produce the effect I'm looking for. (assuming I did it right.) Let me know if it's sane or if I would be better off with the boost::mpl solution.
Thanks,
PaulH I'll also note that CBase probably shouldn't have a virtual destructor. The whole point of an approach like this is to avoid runtime polymorphism (using static polymorphism instead), so making your class polymorphic with the presence of a virtual destructor (or any other function) defeats this purpose.
Great observation, thank you.
I'm curious: The virtual destructor is not necessary (as you pointed out), but does it incur a performance penalty?- Edited byPaulH79 Friday, October 23, 2009 3:43 PMdefined my pronouns
- Sorry for the lack of followup -- I moved last week and forgot all about this thread.The virtual destructor does incur some expense (the class gains a vtable), but the only penalty there is in the size of the class, so unless you're allocating many instances it won't have a measurable impact. However, it seems to me misguided to mix static and dynamic polymorphism, so personally I wouldn't use any virtual functions in the class hierarchy out of principle.The aspect of your last approach that I greatly dislike is that CMyClass ends up in the inheritance hierarchy multiple times, i.e. CMyClass<AddOn_A, AddOn_B> : AddOn_A<CMyClass<AddOn_B> > : CMyClass<AddOn_B> : AddOn_B<CMyClass<> > : CMyClass<> : CBase. This can have negative repercussions -- one that I can think of off the top of my head is that if CMyClass were given data members, those data members would be duplicated and hidden N times (N == number of addons), needlessly making the size of CMyClass very large and expensive to construct/copy. If nothing is added to CMyClass (only to CBase or the CMyClass<empty, empty, empty> specialization) then this doesn't present any issue that I can think of, but the class being in the inheritance hierarchy multiple times still just doesn't smell right to me.Having put some thought into it this time, I came up with the following. This produces a very straightforward inheritance chain -- CMyClass<AddOn_A, AddOn_B> : CMyClass_impl<...> : AddOn_A<AddOn_B<CBase> > : AddOn_B<CBase> : CBase. The advantage over my first approach is that the user doesn't need to use make_addons; the disadvantage is that the number of possible addons must be implemented in two places (CMyClass and CMyClass_seq) rather than just one (make_addons).
#pragma inline_depth(255) #pragma inline_recursion(on) #include <typeinfo> #include <ostream> #include <iostream> #include <string> #include <boost/type_traits/is_same.hpp> #include <boost/mpl/assert.hpp> #include <boost/mpl/placeholders.hpp> #include <boost/mpl/apply.hpp> #include <boost/mpl/not.hpp> #include <boost/mpl/is_sequence.hpp> #include <boost/mpl/copy_if.hpp> #include <boost/mpl/reverse_fold.hpp> #include <boost/mpl/vector.hpp> struct CBase { explicit CBase(int const x) : x_(x) { }; int value() const { return x_; }; private: int x_; }; template<typename T> struct AddOn_A : T { explicit AddOn_A(int const x) : T(x) { }; int add_one() const { return this->value() + 1; }; }; template<typename T> struct AddOn_B : T { explicit AddOn_B(int const x) : T(x) { }; int add_two() const { return this->value() + 2; }; }; template<typename T> struct AddOn_C : T { explicit AddOn_C(int const x) : T(x) { }; int add_three() const { return this->value() + 3; }; }; template<typename T> struct AddOn_D : T { explicit AddOn_D(int const x) : T(x) { }; int add_four() const { return this->value() + 4; }; }; template<typename T> struct AddOn_E : T { explicit AddOn_E(int const x) : T(x) { }; int add_five() const { return this->value() + 5; }; }; template<typename T> struct AddOn_F : T { explicit AddOn_F(int const x) : T(x) { }; int add_six() const { return this->value() + 6; }; }; template<typename T> struct AddOn_G : T { explicit AddOn_G(int const x) : T(x) { }; int add_seven() const { return this->value() + 7; }; }; template<typename T> struct AddOn_H : T { explicit AddOn_H(int const x) : T(x) { }; int add_eight() const { return this->value() + 8; }; }; template<typename T> struct AddOn_I : T { explicit AddOn_I(int const x) : T(x) { }; int add_nine() const { return this->value() + 9; }; }; template<typename T> struct AddOn_J : T { explicit AddOn_J(int const x) : T(x) { }; int add_ten() const { return this->value() + 10; }; }; template<typename T> struct AddOn_K : T { explicit AddOn_K(int const x) : T(x) { }; int add_eleven() const { return this->value() + 11; }; }; namespace detail { namespace mpl = boost::mpl; using mpl::_1; template<template<typename> class T> struct wrap_addon { template<typename U> struct apply { typedef T<U> type; }; }; struct unwrap_addon { template<typename T, typename U> struct apply : mpl::apply<U, T> { }; }; template<typename T> struct no_addon_t; template< template<typename> class T1, template<typename> class T2, template<typename> class T3, template<typename> class T4, template<typename> class T5, template<typename> class T6, template<typename> class T7, template<typename> class T8, template<typename> class T9, template<typename> class T10, template<typename> class T11 > struct CMyClass_seq : mpl::copy_if< mpl::vector< wrap_addon<T1>, wrap_addon<T2>, wrap_addon<T3>, wrap_addon<T4>, wrap_addon<T5>, wrap_addon<T6>, wrap_addon<T7>, wrap_addon<T8>, wrap_addon<T9>, wrap_addon<T10>, wrap_addon<T11> >, mpl::not_<boost::is_same<_1, wrap_addon<no_addon_t> > > >::type { }; template<typename Seq> class CMyClass_impl : public mpl::reverse_fold<Seq, CBase, unwrap_addon>::type { BOOST_MPL_ASSERT_MSG(mpl::is_sequence<Seq>::value, NOT_A_VALID_ADDON_SEQUENCE, (Seq)); protected: typedef CMyClass_impl base_type; typedef typename mpl::reverse_fold<Seq, CBase, unwrap_addon>::type inheritance_chain_t; public: explicit CMyClass_impl(int const x) : inheritance_chain_t(x) { } }; } template< template<typename> class T1 = detail::no_addon_t, template<typename> class T2 = detail::no_addon_t, template<typename> class T3 = detail::no_addon_t, template<typename> class T4 = detail::no_addon_t, template<typename> class T5 = detail::no_addon_t, template<typename> class T6 = detail::no_addon_t, template<typename> class T7 = detail::no_addon_t, template<typename> class T8 = detail::no_addon_t, template<typename> class T9 = detail::no_addon_t, template<typename> class T10 = detail::no_addon_t, template<typename> class T11 = detail::no_addon_t > class CMyClass : public detail::CMyClass_impl<detail::CMyClass_seq<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> > { std::string inheritance_chain_; public: explicit CMyClass(int const x) : CMyClass::base_type(x), inheritance_chain_(typeid(CMyClass::inheritance_chain_t).name()) { } std::string const& inheritance_chain() const { return inheritance_chain_; } }; int main() { CMyClass<> nothing(1357); std::cout << "sizeof(nothing) :: " << sizeof(nothing) << '\n' << "nothing.inheritance_chain :: " << nothing.inheritance_chain() << '\n' << "nothing.value :: " << nothing.value() << "\n\n"; CMyClass<AddOn_A> A(2468); std::cout << "sizeof(A) :: " << sizeof(A) << '\n' << "A.inheritance_chain :: " << A.inheritance_chain() << '\n' << "A.value :: " << A.value() << '\n' << "A.add_one :: " << A.add_one() << "\n\n"; CMyClass<AddOn_B> B(3579); std::cout << "sizeof(B) :: " << sizeof(B) << '\n' << "B.inheritance_chain :: " << B.inheritance_chain() << '\n' << "B.value :: " << B.value() << '\n' << "B.add_two :: " << B.add_two() << "\n\n"; CMyClass<AddOn_A, AddOn_B> AB(4680); std::cout << "sizeof(AB) :: " << sizeof(AB) << '\n' << "AB.inheritance_chain :: " << AB.inheritance_chain() << '\n' << "AB.value :: " << AB.value() << '\n' << "AB.add_one :: " << AB.add_one() << '\n' << "AB.add_two :: " << AB.add_two() << "\n\n"; CMyClass<AddOn_A, AddOn_C, AddOn_E, AddOn_G, AddOn_I, AddOn_K> ACEGIK(5791); std::cout << "sizeof(ACEGIK) :: " << sizeof(ACEGIK) << '\n' << "ACEGIK.inheritance_chain :: " << ACEGIK.inheritance_chain() << '\n' << "ACEGIK.value :: " << ACEGIK.value() << '\n' << "ACEGIK.add_one :: " << ACEGIK.add_one() << '\n' << "ACEGIK.add_three :: " << ACEGIK.add_three() << '\n' << "ACEGIK.add_five :: " << ACEGIK.add_five() << '\n' << "ACEGIK.add_seven :: " << ACEGIK.add_seven() << '\n' << "ACEGIK.add_nine :: " << ACEGIK.add_nine() << '\n' << "ACEGIK.add_eleven :: " << ACEGIK.add_eleven() << "\n\n"; CMyClass<AddOn_A, AddOn_B, AddOn_C, AddOn_D, AddOn_E, AddOn_F, AddOn_G, AddOn_H, AddOn_I, AddOn_J, AddOn_K> ABCDEFGHIJK(6802); std::cout << "sizeof(ABCDEFGHIJK) :: " << sizeof(ABCDEFGHIJK) << '\n' << "ABCDEFGHIJK.inheritance_chain :: " << ABCDEFGHIJK.inheritance_chain() << '\n' << "ABCDEFGHIJK.value :: " << ABCDEFGHIJK.value() << '\n' << "ABCDEFGHIJK.add_one :: " << ABCDEFGHIJK.add_one() << '\n' << "ABCDEFGHIJK.add_two :: " << ABCDEFGHIJK.add_two() << '\n' << "ABCDEFGHIJK.add_three :: " << ABCDEFGHIJK.add_three() << '\n' << "ABCDEFGHIJK.add_four :: " << ABCDEFGHIJK.add_four() << '\n' << "ABCDEFGHIJK.add_five :: " << ABCDEFGHIJK.add_five() << '\n' << "ABCDEFGHIJK.add_six :: " << ABCDEFGHIJK.add_six() << '\n' << "ABCDEFGHIJK.add_seven :: " << ABCDEFGHIJK.add_seven() << '\n' << "ABCDEFGHIJK.add_eight :: " << ABCDEFGHIJK.add_eight() << '\n' << "ABCDEFGHIJK.add_nine :: " << ABCDEFGHIJK.add_nine() << '\n' << "ABCDEFGHIJK.add_ten :: " << ABCDEFGHIJK.add_ten() << '\n' << "ABCDEFGHIJK.add_eleven :: " << ABCDEFGHIJK.add_eleven() << std::endl; return 0; }
- Marked As Answer byPaulH79 Tuesday, November 03, 2009 7:42 PM
Sorry for the lack of followup -- I moved last week and forgot all about this thread.
That's actually really cool. :) Thanks!<snip>Having put some thought into it this time, I came up with the following. This produces a very straightforward inheritance chain -- CMyClass<AddOn_A, AddOn_B> : CMyClass_impl<...> : AddOn_A<AddOn_B<CBase> > : AddOn_B<CBase> : CBase. The advantage over my first approach is that the user doesn't need to use make_addons; the disadvantage is that the number of possible addons must be implemented in two places (CMyClass and CMyClass_seq) rather than just one (make_addons).
Hope your move went okay? If you moved to east Iowa, let me know. I'll bring a beer-flavored house-warming gift.
-PaulH


