|
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