|
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