Boost logo

Boost Users :

Subject: Re: [Boost-users] [proto] a question on assignment operators
From: Eric Niebler (eric_at_[hidden])
Date: 2009-08-24 15:55:41


(Public response to Maurizio's private follow-up. Hope Maurizio doesn't
mind.)

Maurizio Vitale wrote:
>>>>>> "Eric" == Eric Niebler <eric_at_[hidden]> writes:
>
> Eric> Maurizio Vitale wrote:
> >> Hi Eric, while you were on vacation I posted the following on the
> >> boost.users
> Eric> <snip>
>
> Eric> Sorry for the delay; I missed your original message. I've
> Eric> followed up on the boost-users list. In short: I don't
> Eric> understand enough of what you're trying to do to help. Please
> Eric> clarify and I'll help as best I can.
>
> Let me try to rephrase what I was trying to ask and see if I'm more
> successful.
>
> The gist of the question is what happens when you have to add an
> operator= to a proto extended expression. You normally wouldn't because
> in general it doesn't make much sense to do things like:
>
> a+b = c-d;
>
> (although it could make sense in a DSL for linear algebra)
> In my case something like:
>
> (a,b,c) = expression;
>
> would require to do (inside my_expr::operator=):
> a = f (expression);
> b = g (expression);
> c = h (expression);
>
> where f,g,h depend on the semantics. In my case they would carve a
> certain number of bits from expression. In a DSL dealing with tuples
> they would select the appropriate component from expression.

OK sure, that makes sense.

> The problem I'm having is that expressions built by proto seem to be
> const, forcing operator= to be const (or at least that's what happens in
> my example).

It's true, Proto's objects have a top-level const qualification. They
are, after all, rvalues (temporaries) so they should not be modified.

HOWEVER, the const-ness only applies at the top-level. The const-ness of
child nodes is preserved. In particular, non-const lvalue terminals are
held by reference and remain non-const. Consider:

   typedef proto::terminal<int>::type I;
   I i = {42};
   I & refi = proto::left(i+i);

The temporary object created by i+i is const, but each i is held by
non-const reference, which is what is returned by proto::left.

> If that's true, then my followup question was whether it is
> ok to perform the assignment the way I was doing it, casting away const.

In short, it shouldn't be necessary. You're using proto::flatten and
fusion::fold. Somewhere in there the constness is getting messed up. You
should file a bug.

Proto's fold_tree transform gets the constness correct. See the attached
code which is equivalent to yours, but doesn't require any ugly
const_cast's.

> The other question I had was on what was the reason for explicitely
> disable assignment operators in grammars. This is something you do in
> many of your examples

I do? Which examples? I don't see it.

> , but doesn't seem to make a difference in my code
> (e.g. having an operator= in my expression seems to stop proto from
> building a larger tree including the '=' anyhow, without needing to
> disable it explicitly)

I'm afraid you've lost me again.

> The last question, which was the first, it is not very important. What
> I'm really interesting in is how to use proto when you need to assign to
> a proto expression, rather than a proto terminal.

And I hope at least that much has been cleared up.

HTH,

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

#include <iostream>
#include <boost/mpl/plus.hpp>
#include <boost/mpl/sizeof.hpp>
#include <boost/proto/core.hpp>
#include <boost/proto/transform.hpp>

namespace mpl = boost::mpl;
namespace proto = boost::proto;
namespace fusion = boost::fusion;
using proto::_;

struct my_grammar : proto::or_<
  proto::terminal< int >,
  proto::binary_expr<_, my_grammar, my_grammar>
>
{};

template<typename Expr>
struct my_expr;

struct my_domain : proto::domain<proto::generator<my_expr>, my_grammar>
{};

struct AssignTerminal : proto::callable
{
  template<typename Sig>
  struct result;
  
  template<typename This, typename Terminal, typename Offset>
  struct result<This(Terminal &, Offset)>
  {
    typedef mpl::plus<mpl::sizeof_<Terminal>, Offset> type;
  };

  template<typename Terminal, typename Offset>
  mpl::plus<mpl::sizeof_<Terminal>, Offset>
  operator()(Terminal & t, Offset) const
  {
    proto::value(t) = Offset::value;
    return mpl::plus<mpl::sizeof_<Terminal>, Offset>();
  }
};

struct AssignGroup
  : proto::fold_tree<
      proto::_,
      mpl::size_t<0>(),
      AssignTerminal(proto::_, proto::_state)
>
{};

template<typename Expr>
struct my_expr
  : proto::extends<Expr, my_expr<Expr>, my_domain>
{
  typedef proto::extends<Expr, my_expr<Expr>, my_domain> base_type;

  my_expr( Expr const & expr = Expr() )
    : base_type( expr )
  {}

  template<typename E>
  const my_expr& operator=(const E&) const
  {
    AssignGroup()(*this);
    return *this;
  }
};

struct my_int
  : my_expr< proto::terminal< int >::type >
{
  typedef my_expr< proto::terminal< int >::type > base_type;
  explicit my_int (int i = 0) : base_type (base_type::proto_base_expr::make (i)) {}
  operator int () { return proto::value (*this); }
};

int main()
{
  my_int i (1);
  my_int j (2);
  my_int k (3);

  (k,j) = (j,k);
  
  std::cout << j << std::endl << k << std::endl;
}

/// Local Variables:
/// mode:c++
/// comment-column:60
/// fill-column:150
/// compile-command:"g++ -I. -I./boost -o testcase testcase.cpp"
/// c-macro-cppflags:"-C -I. -I./boost"
/// c-backslash-column:120
/// c-backslash-max-column:149
/// End:


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net