Boost logo

Boost Users :

From: David Abrahams (dave_at_[hidden])
Date: 2005-05-31 14:24:21


"White Wolf" <wolof_at_[hidden]> writes:

> Hi Dave,

Hi Atilla,

In future please post your questions to the boost-users list so that
the whole community can benefit.

> I am trying to do something very simple, but I am going bananas. I have
> types, each of them has a "static member" (enum label) called nr_. I
> make a boost::mpl::list of them, and I would like to add them all
> together. But if I write _2::nr_, it says there is no nr_.

Well, the author of _2 can't anticipate all the nested values you
might be using. _2 is not magic; it's just a type.

  // !! untested code !!
  #include <boost/mpl/size_t.hpp>
  #include <boost/mpl/plus.hpp>
  using namespace mpl::placeholders;

  // A metafunction to get an Element's ::nr_ value
  template <class Element>
  struct get_nr_
  {
      // all metadata are types, so wrap it in a type.
      typedef mpl::size_t<Element::nr_> type;
  };

  typedef mpl::fold<
      some_list, mpl::size_t<0>
    , mpl::plus<
          _1
        , get_nr_<_2>
>
>::type sum;

  // sum::value contains the value you're interested in.

> I fugured it wants a type. So I have made a typedef inside called
> type, which has a boost::mpl::integer_c<size_t, nr_> in it. But it
> does not work, it gives me back the type of the last list element as
> a result. I try to use mpl::accumulate.

Yes, that's a synonym for fold if you have a version of the MPL that
includes accumulate.

> Finally I guess I will have to use transform in some way, because I
> need a list which has an mpl::pair (if there is one)

http://www.boost.org/libs/mpl/doc/refmanual/pair.html

> of the original type and the sum of all nr_'s before it.

Seems like it's still a job for fold. In this case I'd use mpl::vector
because lists can only be push_front-ed and you probably don't want to
end up with your elements in "reverse" order.

  typedef mpl::fold<
      some_list

    , mpl::pair< // initial state
          mpl::vector0 // initial sequence
        , mpl::size_t<0> // initial sum
>

    , mpl::pair< // the state at each step is a pair containing

          mpl::push_front< // added to the front of...
               mpl::first<_1> // the sequence from the previous step

             , mpl::pair< // a new element, a pair containing
                   _2 // the original type

                  , mpl::plus< // and the sum of
                       mpl::second<_1> // the sum from previous step
                     , get_nr_<_2> // the ::nr_ of the original type
>
>
>

        , mpl::plus< // and the sum of
              mpl::second<_1> // the sum from previous step
            , get_nr_<_2> // the ::nr_ of the original type
>
>
>::type pair_seq_sum;
  
  typedef pair_seq_sum::first new_sequence;

So this is pretty verbose using lambdas because the sum is repeated,
among other things. You might do better to build a custom metafunction
that gets you the new state:

    template <class PrevState, class Element>
    struct next_state
    {
         typedef typename PrevState::first prev_sequence;
         typedef typename PrevState::second prev_sum;
         typedef mpl::size_t<(prev_sum::value + Element::nr_)> new_sum;
         typedef typename mpl::push_back<
             prev_sequence
           , mpl::pair<Element, new_sum>
>::type new_sequence;
         typedef mpl::pair<new_sequence, new_sum> type;
    };

    typedef mpl::fold< some_list, mpl::fold<_,_> >::type pair_seq_sum;

    typedef pair_seq_sum::first new_sequence;

> Can you help me? I have
> something like this:
>
> struct T1 {
> enum x {
> label1,
> label2,
> nr_;
> };
> };
>
> // Same for T2, possibly less or more labels etc.
>
> And I would like to end up with a type which looks like this:
>
> struct something {
> int get(T1::x n) {
> return arr_[n];
> }
> int get(T2::x n) {
> return arr_[n+2];

Where does n+2 come from?

> }
> // etc.
> private:
> int arr_[sum_of_all_nr_];
> };

?? that doesn't look anything like what you just described.

Sheesh. The above is still a job for fold.

I suggest you start with a base type:

  template <class Derived>
  struct base
  {
      enum { sum = 0 };
      void get(); // never used.
  };

then build layers:

  template <class Base, class T, class MostDerived>
  struct layer : mpl::apply<Base, MostDerived>::type
  {
       typedef typename mpl::apply<Base, MostDerived>::type super;

       enum { sum = T::nr_ + super::sum };

       using Base::get;

       int get(T::x n)
       {
           return static_cast<MostDerived*>(this)->arr_[n];
       }
  };

  template <class Folded>
  struct outer : Folded
  {
       int arr_[Folded::sum];
  };

Well, the above "solution" won't work and I don't have time to write
something that does for you right now. It would take maybe another 15
minutes and I've already spent 30 on this email. I guess you'll
probably do best to make 2 passes over the sequence: one to compute
the sums and the other to build the result type. Good luck!

-- 
Dave Abrahams
Boost Consulting
www.boost-consulting.com

Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net