Search code examples
c++generic-programmingboost-mpl

Changing the template arguments of derived classes


In the code below, C's base class B1's template argument OFFSET depends on B0, and B2 on B1.

This is done by manual write the code every time an instance of C is created (in the main method). Is there a way to move this functionality to the definition of C instead?

template<int OFFSET>
struct A {
    enum O { offset = OFFSET };
    enum S { size = 2 };
};

template<int OFFSET>
struct B {
    enum O { offset = OFFSET };
    enum S { size = 4 };
};

template < typename B0, typename B1, typename B2 >
struct C : public B0, B1, B2 {
};

int main(int argc, const char *argv[])
{
    // instance of C
    C< A<1>,

       B< A<1>::offset * A<1>::size >,

       A<
           B< A<1>::offset * A<1>::size >::offset *
           B< A<1>::offset * A<1>::size >::size
       >
    > c1;

    // does the same thing
    C< A<1>,

       B< A<1>::size >,

       A<
           A<1>::size *
           B< A<1>::size >::size
       >
    > c2;

    return 0;
}

EDIT:

To answer the comments, here are the steps I think needs to be taken to solve this:

  • Write a metafunction which can change the offset: set_new_offset which for T defines the type T<2>

  • Use boost::mpl::times to calculate the new offsets

  • Add more template magic...


Solution

  • You can do it with template templates in C, although I'm not 100% sold it's an improvement. If you only ever need three bases this should be fine. If you need an arbitrary number of bases...there must be a better way to do this than inheritance as this method will get unwieldy.

    template<int OFFSET>
    struct A {
        enum O { offset = OFFSET };
        enum S { size = 2 };
    };
    
    template<int OFFSET>
    struct B {
        enum O { offset = OFFSET };
        enum S { size = 4 };
    };
    
    template < typename B0, template <int T> class B1, template <int T> class B2 >
    struct C : public B0, B1<B0::offset * B0::size>, B2<B1<B0::offset * B0::size>::offset * B1<B0::offset * B0::size>::size> {
        enum
        {
            B0_offset = B0::offset,
            B1_offset = B1<B0::offset * B0::size>::offset,
            B2_offset = B2<B1<B0::offset * B0::size>::offset * B1<B0::offset * B0::size>::size>::offset,
            B0_size = B0::size,
            B1_size = B1<B0::offset * B0::size>::size,
            B2_size = B2<B1<B0::offset * B0::size>::offset * B1<B0::offset * B0::size>::size>::size
        };
    };
    
    int main()
    {
        // instance of C
        C< A<1>,
    
           B,
    
           A
        > c1;
    
        static_cast<void>(c1);
    
        // does the same thing
        C< A<1>,
    
           B,
    
           A
        > c2;
    
        static_cast<void>(c2);
    
        std::cout << c1.B0_offset << std::endl;
        std::cout << c1.B1_offset << std::endl;
        std::cout << c1.B2_offset << std::endl;
        std::cout << c1.B0_size << std::endl;
        std::cout << c1.B1_size << std::endl;
        std::cout << c1.B2_size << std::endl;
    
        std::cout << c2.B0_offset << std::endl;
        std::cout << c2.B1_offset << std::endl;
        std::cout << c2.B2_offset << std::endl;
        std::cout << c2.B0_size << std::endl;
        std::cout << c2.B1_size << std::endl;
        std::cout << c2.B2_size << std::endl;
    
        return 0;
    }