$include_dir="/home/hyper-archives/boost-users/include"; include("$include_dir/msg-header.inc") ?>
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