1

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...

Allan
  • 4,562
  • 8
  • 38
  • 59
  • 2
    What real problem are you trying to solve? – Mark B Jan 06 '12 at 17:09
  • @MarkB it is a long story, but part of the real problem is to learn metaprogramming in c++. – Allan Jan 06 '12 at 17:15
  • It does seem like you're trying to solve a problem in an unusual way. You're inheriting from three `struct`s only to gain access to 3 `OFFSET` and 3 `SIZE` enums. And the second and third can be calculated in terms of the first. – Drew Dormann Jan 06 '12 at 17:16

2 Answers2

1

How about defining a helper class:

template <template <int> class C, int N>
struct Composer
{
    enum O { offset = C<N>::offset * C<N>::size; };
    enum S { size = C<N>::size; };
};

Then you can say:

C<A<1>, Composer<A, 1>, Composer<B, Composer<A, 1>::offset> c2;

If necessary one could think up a higher-order composer that allows you to form higher "powers" of composition.

(Maybe Composer should have been called Bind1st or so...)

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • I'm sorry, but I do not understand your solution. Have you noticed that A and B are different? (size=2 and size=4). The code you have suggested C will derive from Composer, not from A and B? How would that work for more complex version of A and B? (and version where A and B are more different?) – Allan Jan 06 '12 at 17:59
  • @Allan: Sorry, the size was indeed an error. I think it's fixed now. – Kerrek SB Jan 06 '12 at 18:06
1

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;
}
Mark B
  • 95,107
  • 10
  • 109
  • 188
  • I did not know about template template arguments before, smart... In the current implementation I need this for 32 arguments so I hope for a further improvements... – Allan Jan 06 '12 at 18:11
  • @Allan If you need 32 templates arguments, I'm 99% sure you're doing something sub-optimally. – Mark B Jan 06 '12 at 18:18
  • I sure am, but the project I'm working on has still not switched to c++0x – Allan Jan 06 '12 at 18:22
  • Some preprocessor code makes it actually look okay: `#define B0_ B0<1>`, `#define B1_ B1`, `#define B2_ B2` – Allan Jan 06 '12 at 18:30