Boost logo

Boost Users :

Subject: [Boost-users] Visiting an element of fusion sequence, selected at run-time
From: Max Motovilov (max_at_[hidden])
Date: 2010-11-29 00:53:38


Here's a seemingly simple problem that I have spent most of my
Thanksgiving weekend on, and still don't have enough to boast:

Given:

1. A Boost.Fusion-compatible sequence;
2. An integral value i computed at runtime, known to be within
    0..size(sequence);

implement an algorithm applying some sort of a generic visitor to the
i-th element of the sequence.

However long I looked through the documentation, I didn't find a ready
solution. Here's the best I was able to come up with on my own:

======================================================================
struct at_v {
   template<typename Seq, typename N> struct result :
     public fusion::result_of::at<Seq,N>
   {};

   template<typename Seq, typename N>
   typename result<Seq,N>::type operator()( Seq& seq, N ) const {
     return fusion::at<N>( seq );
   }
};

template<typename F, typename Seq, typename IntV>
void apply_at( F f, Seq& seq, IntV i )
{
   fusion::for_each(
     fusion::as_vector(
       mpl::range_c<
         IntV,
         0, IntV(
           fusion::result_of::size<Seq>::type::value
         )
>()
     ),
     phoenix::if_( phoenix::arg_names::arg1 == i ) [
       phoenix::function<F>( f )(
         phoenix::function<at_v>()(
           phoenix::ref( seq ), phoenix::arg_names::arg1
         )
       )
     ]
   );
}
======================================================================

Drawbacks:

1. Requires a Boost.Phoenix-compliant visitor -- even though I managed
to hide the Phoenix dependency inside apply_at(), it still requires a
full-blown templatized result metafunction as part of the visitor even
though the latter is not expected to return a value. Be nice to have
simpler requirements for a visitor.

2. Can't figure out an easy way of returning a value from the visitor. I
tried some constructs using fusion::fold() but only halfheartedly: the
big problem is that there is no obvious way to specify that the visitor
always returns results of the same type, no matter what argument type it
is given. Note that the function calls the visitor only once, so, in
principle, there should always be exactly one value to return (index out
of bounds should naturally be a contract violation, which is not checked
in my version).

3. The complexity is O( size(sequence) ) which is quite atrocious for
random access (even though in practice it should not be much of a
problem given that it simply means N integer comparisons with a
relatively small N). Still, a O( log N ) or even a constant-time
implementation should be quite possible...

Even though the solution above should be sufficient for my purposes I'm
still openly wondering whether I have overlooked something in the Boost
libraries and/or whether someone more versed in Fusion/Phoenix/MPL might
suggest a better approach.

Thanks in advance for any responses,
...Max...

PS. Without fusion::as_vector() around the index sequence constructor,
GCC 4.4.3 complained of not being able to match operator(), suggestive
of a forwarding function problem: looks like operator* on an iterator
over mpl::range_c<> returns something that can't be digested by a
Phoenix actor. I wonder if this should really be taken care of by the
Fusion adapter for MPL sequences?

PPS. Is there better documentation for Boost.Phoenix than what's at
http://www.boost.org/doc/libs/1_45_0/libs/spirit/phoenix ? Not even a
straightforward reference manual?


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