Boost logo

Proto :

Subject: Re: [proto] Nested Transforms
From: Eric Niebler (eric_at_[hidden])
Date: 2011-02-26 06:55:30


On 2/26/2011 8:18 AM, Nate Knight wrote:
> I'm trying to understand why the two commented out lines fail to compile. It seems to me
> that ideally they would compile, but I may be missing some constraint that makes these
> lines incorrect. In the first case, it seems like the return type of the default subscript evaluator
> is being computed incorrectly. The second one is not as interesting, but I curious why it
> doesn't compile.
>
> I'm compiling against 1.45 with gcc 4.5.
>
> Thanks for any help.
>
> #include <boost/proto/proto.hpp>
> #include <tr1/array>
> #include <boost/mpl/bool.hpp>
>
> using namespace boost::proto;
>
> namespace la
> {
> typedef std::tr1::array< double, 2 > state_type;
>
> struct Distribute:
> or_<
> when<terminal<state_type>, _make_subscript(_, _state)>,
> terminal<_>,
> nary_expr<_, vararg<Distribute> >
> >
> {};
>
> struct Optimize:
> or_<
> when<
> subscript<Distribute, terminal<_> >,
> Distribute(_left, _right)
> >,
> nary_expr<_, vararg<Optimize> >,
> terminal<_>
> >
> {};
>
> // optimize and then evaluate
> struct EvalOpt: when<_, _default<>(Optimize)> {};
> }
>
> int main()
> {
> la::state_type x = {1.0,2.0};
> la::state_type const& xc = x;
>
> // this compiles
> la::EvalOpt()( lit(x)[1] );
>
> // this fails to compile
> //la::EvalOpt()( lit(xc)[1] );
>
> // this fails to compile
> //_default<>()( la::Optimize()( boost::proto::lit(x)[1] ) );
>
> // this compiles
> _default<>()( as_expr( la::Optimize()( boost::proto::lit(x)[1] ) ) );
> }

I think I know what's causing this. Can you try compiling with
BOOST_PROTO_STRICT_RESULT_OF?

What's going on: Proto is cutting corners in the interest of
performance. Consider the actual implementation of the _value transform:

    struct _value : transform<_value>
    {
        template<typename Expr, typename State, typename Data>
        struct impl : transform_impl<Expr, State, Data>
        {
            typedef
                typename result_of::value<Expr>::type
            result_type;

            /// Returns the value of the specified terminal expression.
            /// \pre <tt>arity_of\<Expr\>::value == 0</tt>.
            /// \param e The current expression.
            /// \return <tt>proto::value(e)</tt>
            /// \throw nothrow
            #ifdef BOOST_PROTO_STRICT_RESULT_OF
            typename mpl::if_c<is_array<result_type>::value, result_type
&, result_type>::type
            #else
            typename result_of::value<typename impl::expr_param>::type
            #endif
            operator ()(
                typename impl::expr_param e
              , typename impl::state_param
              , typename impl::data_param
            ) const
            {
                return proto::value(e);
            }
        };
    };

Notice the PP grossness. The type that the transform *claims* to return
differs from the type it actually returns. It does that to avoid making
copies. The problem is that the compile-time computation of your
transform does something slightly different than the runtime one. In
most cases, its harmless. Not in yours. You're an innocent bystander who
got cross in the crossfire. Sorry.

So what to do? This is clearly a bug in proto. But I hesitate to fix it
because I worry about the performance implications. It's not so bad for
terminals, but _child_c has the same problem, and if the _child_c
transform suddenly started returning whole sub-trees by value, it could
really hurt.

But maybe it wouldn't hurt. Maybe RVO kicks in. It's worth testing. Joel
F., are you listening? Yours is probably the most performance sensitive
application of Proto. Can you try compiling with
BOOST_PROTO_STRICT_RESULT_OF and let us know if there's a perf hit?

-- 
Eric Niebler
BoostPro Computing
http://www.boostpro.com

Proto list run by eric at boostpro.com