[Home]CPPTM Answers - Exercise 3-4

BOOST WIKI | RecentChanges | Preferences | Page List | Links List

 // (by Ariel Badichi)
 #include <boost/static_assert.hpp>
 #include <boost/type_traits/is_same.hpp>
 #include <boost/type_traits/add_pointer.hpp>
 #include <boost/mpl/apply.hpp>
 #include <boost/mpl/placeholders.hpp>
 #include <boost/mpl/lambda.hpp>

 namespace mpl = boost::mpl;
 using namespace mpl::placeholders;

 template<typename F, typename T>
 struct twice : mpl::apply<F, typename mpl::apply<F, T>::type> {};

 int main()
 {
     typedef mpl::lambda<boost::add_pointer<_> >::type add_pointer_lambda;
     typedef twice<twice<add_pointer_lambda, _>, int>::type p;

     BOOST_STATIC_ASSERT((boost::is_same<int ****, p>::value));

     return 0;
 }


Can anyone explain why the explicit lambda call is necessary? I had thought that the apply embedded in twice would deal with the add_pointer placeholder expression, but GCC 3.3.3 gives a compile error with the following:

 BOOST_STATIC_ASSERT((
   boost::is_same<
     twice<
       twice<
         boost::add_pointer<_1>,  // needs to be wrapped in mpl::lambda<...>::type ???
         _1
       >,
       int
     >::type,
     int****
   >::value
 ));

Any ideas? -- Matt Brecknell

Section 3.3 has a nice explanation for why this won't work. - Ariel

Matt: Section 3.3 does explain why versions of the twice metafunction from earlier sections of the book don't work directly with placeholder expressions. But then section 3.3.2 says the first argument to mpl::apply can be any lambda expression (including those built with placeholders). Since your definition twice uses mpl::apply to invoke the argument metafunction, I expected to be able to use placeholder expressions without needing to use mpl::lambda. For example, I believe the following should work with your definition of twice (though I'm not in front of an MPL-capable compiler at the moment, so I can't check):

 BOOST_STATIC_ASSERT((
   boost::is_same<
     twice<
       boost::add_pointer<_>,  // no mpl::lambda required here
       int
     >::type,
     int**
   >::value
 ));

I'm currently working on the theory that the problem with my version of the nested invocation of twice might be something to do with the way the outer twice is evaluating placeholders in the inner twice, but I haven't figured it out yet. -Matt

In your previous snippet, consider the inner twice first. Consider the second argument. It's just the type _1. It's nothing special in the context of the inner twice. Therefore, the result of the inner twice is _1 **. The inner twice is a metafunction, not a lambda expression. So you can't mpl::apply it. If you use mpl::lambda on boost::add_pointer, then the inner twice will use it to generate a lambda expression, which can be passed to mpl::apply, and therefore to the outer twice. At least I think that's how it goes (I wonder if Dave could clear this up?) - Ariel

I agree that the inner twice<> would evaluate to _1** if I had immediately evaluated the inner twice<> with ::type. But since I didn't, I thought that the outer twice<> would regard the inner twice<> as a placeholder expression (and not just a metafunction, as you then suggest). I therefore expected that the mpl::apply in the inner twice<> would convert the boost::add_pointer<_1> to a metafunction class, while leaving the second argument (bare _1) for the outer twice<> to substitute. That's why I used _1 in both places, even though they are meant to refer to different things in different contexts. In any case, I don't see why your reasoning would apply to my formulation, without also applying to yours. After all, you have a "_" placeholder in the same place in your version. The only material difference is that you have wrapped boost::addpointer<_> in mpl::lambda<>. I don't follow your reasoning about mpl::lambda causing the inner twice<> to "generate a lambda expression" for the outer twice<>: if the inner twice<> is a placeholder expression, then it's already a lambda expression (book section 3.6). Obviously, my reasoning has gone wrong somewhere, but I'm not yet convinced it's for any of the reasons you have suggested. -Matt

I accept that the inner twice is a placeholder expression. The outer twice then substitutes BOTH placeholders for a type (for each mpl::apply). Using mpl::lambda protects the placeholder for boost::add_pointer from substitution. How this works exactly, I'm not sure. - Ariel

The reason twice< twice< add_pointer<_1>, _1 >, int > doesn't work is that, without an explicit scope specification, mpl::apply< twice< add_pointer<_1>, _1 >, int >::type is equivalent to

    template< typename T >
    struct twice_add_pointer
        : twice< typename add_pointer<T>::type, T >
    {
    };

    mpl::apply< twice_add_pointer<_1>, int >::type

which, I hope, is clearly erroneous. With explicit scoping along the lines of http://thread.gmane.org/gmane.comp.lib.boost.devel/115924, a working one-liner would be

   twice< twice< scope< add_pointer<_1> >, _1 >, int >

Hope this clarifies things, People/Aleksey Gurtovoy.


BOOST WIKI | RecentChanges | Preferences | Page List | Links List
Edit text of this page | View other revisions
Last edited April 3, 2005 8:43 pm (diff)
Search:
Disclaimer: This site not officially maintained by Boost Developers