Visiting an element of fusion sequence, selected at run-time
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?
On Mon, Nov 29, 2010 at 12:53 AM, Max Motovilov <max@motovilov.net> wrote:
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);
Weird, this is the second time I've recommended the following in a week -- the easiest way to do what you want is with Steven Watanabe's switch utility. It was up for review a couple years ago and was accepted pending some changes but I don't think it was ever updated Documentation: http://dancinghacker.com/switch/ and you can get it from the Boost vault here: http://tinyurl.com/23qn7w -- -Matt Calabrese
Weird, this is the second time I've recommended the following in a week -- the easiest way to do what you want is with Steven Watanabe's switch utility. It was up for review a couple years ago and was accepted pending some changes but I don't think it was ever updated
Documentation: http://dancinghacker.com/switch/ and you can get it from the Boost vault here: http://tinyurl.com/23qn7w
-- -Matt Calabrese
Matt: Thanks very much! I _KNEW_ there'd be an [almost] exact solution somewhere. Was looking at phoenix::switch_ and wondering why isn't there a version based on sequences :-) Regards, ...Max...
participants (2)
-
Matt Calabrese
-
Max Motovilov