Boost logo

Boost :

Subject: Re: [boost] [mpl] Nested Scopes w/Placeholders
From: Aleksey Gurtovoy (agurtovoy_at_[hidden])
Date: 2012-01-23 02:30:21


Hi Nicholas,

On Fri, 13 Jan 2012 19:44:07 -0600, Kitten, Nicholas
<nkitten_at_[hidden]> wrote:

> I've recently been doing a lot mpl::folding (in boost 1.47), and I was
> disappointed when I realized that a nested fold wouldn't work with a
> lambda expression passed to the inner call, as in this example which
> extracts elements of sequences:
>
> using namespace mpl;
> using namespace mpl::placeholders;
>
> typedef vector< vector<char>, vector<int> > vec_of_vecs;
> typedef vector< char, int > expected_result;
>
> typedef fold< vec_of_vecs,
> vector<>,
> fold< _2,
> _1,
> *push_back< _1, _2 >* // wrong - refers to
> outer arguments
> >
> >::type result;
>
> BOOST_MPL_ASSERT(( equal<result, expected_result> ));
>
>
> After looking around for a solution I came across an old
> thread<http://lists.boost.org/boost-users/2004/12/9269.php> on
> the same subject, where it was suggested using mpl::protect might work.
> Indeed, the following modification does what I want, without any changes
> to the library:
>
> typedef fold< vec_of_vecs,
> vector<>,
> fold< _2,
> _1,
> *protect< lambda< push_back< _1, _2 >
> >::type
>> * // correct
> >
> >::type result;

Actually, you don't need 'protect' here, plain 'lambda< push_back< _1, _2
> >::type' would suffice, the resulting Metafunction Class is already
shielded from being interpreted as a placeholder expression. Or you can
just go with simple 'quote2<push_back>'.

>
> Of course, this is a little on the verbose side. In the old thread,
> Daniel Wallin suggested suggested changing the definition of lambda to
> yield syntax more in line with the runtime version of boost::bind, but
> that would apparently break other code.

Yes, Daniel's suggestion affects not only protect's syntax, but also its
semantics.

> What about instead adding a specialization to
> protect for placeholder expressions? Indeed, the source in protect.hpp
> seems to indicate such a thing might've been in the works at one point,

Nope :), please see below.

> but I didn't see any more references to it:
>
> template<
> typename BOOST_MPL_AUX_NA_PARAM(T)
> * , int not_le_ = 0 // not lambda expression?*
> >
> struct protect : T
> {
> ...
> typedef protect type;
> };

That parameter's name is somewhat misleading; what it actually means is
something like "this is an non-type template parameter that prevents
'protect' from being treated as a placeholder expression".

>
> What about changing it to something like this?
>
> template<
> typename BOOST_MPL_AUX_NA_PARAM(T)
> * , typename not_le_ = typename is_lambda_expression< T >::type*
> >
> struct protect : T
> {
> ...
> typedef protect type;
> };
>
> *template< typename T >*
> *struct protect< T, mpl::true_ > : lambda<T>*
> *{*
> *...*
> *typedef protect type;*
> *};*
>

If we are try this, it would need to be more along the lines of

     template<
           typename BOOST_MPL_AUX_NA_PARAM(T)
         , bool not_le_ = is_lambda_expression<T>::value
>
     struct protect : T
     {
         typedef protect type;
     };

     template< typename T >
     struct protect<T,true> : lambda<T>::type
     {
         typedef protect type;
     };

.. but I'm pretty sure it's still going to break at least some code. Hmm,
let me try this quick... yep, breaks the library itself; check out the
following code in "equal.hpp":

     template<
           typename Predicate
         , typename LastIterator1
         , typename LastIterator2
>
     struct equal_pred // Metafunction class!
     {
         template<
               typename Iterator2
             , typename Iterator1
>
         struct apply { ... };
     };

     template<
           typename Sequence1
         , typename Sequence2
         , typename Predicate
>
     struct equal_impl
     {
         typedef typename begin<Sequence1>::type first1_;
         typedef typename begin<Sequence2>::type first2_;
         typedef typename end<Sequence1>::type last1_;
         typedef typename end<Sequence2>::type last2_;

         typedef aux::iter_fold_if_impl<
               first1_
             , first2_
             , next<>
             , protect< aux::equal_pred<Predicate,last1_,last2_> > // <----
here
             , void_
             , always<false_>
> fold_;

See how the suggested specialization changes the meaning of the
highlighted line? For it to work with the new 'protect' definition, the
line needs to be changed to

             , aux::equal_pred< protect<Predicate>,last1_,last2_> >

and the 'equal_pred' rewritten to take this into account. Doable, but the
change is definitely not backward compatible.

The thing is, 'protect' was really conceived w/ bind and Metafunction
classes in mind, and it actually works as you'd expect in that context
(see Example in
http://www.boost.org/doc/libs/1_48_0/libs/mpl/doc/refmanual/protect.html).
It might be possible to retrofit it to lambda expressions, but it'll
require quite a bit of work that I'm not sure is worth it. Personally, I'd
rather work on a more general scoping mechanism along the lines of
http://article.gmane.org/gmane.comp.lib.boost.devel/116000

HTH,

-- 
Aleksey Gurtovoy
MetaCommunications Engineering

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk