Well, your domain's generator is a little unusual in that not all the
expressions it generates are, in fact, within your domain. That's not a
stated post-condition of the Generator parameter, but it probably should be.
In short, make_expr is assuming that the generator is returning a new
object, and so it is storing it by value.
In my case, I am treating the domain mechanism as a means to add a new
property to my expression. I want that property to hold only for the
expression of the form Program_(...), and that's why I have the
generator the way it is. To be honest, I assumed that you provide the
domain mechanism just for this purpose (i.e., add new properties to
expressions).
I could hack Proto to make this case work, but doing so makes me
uncomfortable. I don't think I want to support generators that don't return
expressions in the right domain.
Well, I did get my case working by creating a new generator,
by_ref_generator, and taking the terminals passed to the Program_
function as references (NOT const refs). Here is the code :
#include
#include
#include <iostream>
#include <sstream>
#include <string>
namespace proto=boost::proto;
namespace mpl=boost::mpl;
template<typename VT>
struct Var {
VT value;
void assign(const VT v) {
value = v;
}
};
struct program_ {};
struct _assign
: proto::callable {
typedef void result_type;
template<typename VT>
result_type operator()(Var<VT> &var) const {
var.assign(20);
}
template<typename VT>
result_type operator()(Var<VT> &var, const VT val) const {
var.assign(val);
}
};
struct by_ref_generator
{
BOOST_PROTO_CALLABLE()
template<typename Sig>
struct result;
template
struct result
{
typedef Expr& type;
};
template
struct result
{
typedef Expr& type;
};
template<typename Expr>
typename result::type operator ()(Expr &e) const
{
return e;
}
};
struct assign
: proto::or_<
proto::whenproto::_ >,
_assign(proto::_value)>,
proto::whenproto::_ >,
proto::literalproto::_ >,
_assign(proto::_value(proto::_left), proto::_value(proto::_right))>,
proto::whenproto::_ >,
proto::literalproto::_ >,
_assign(proto::_value(proto::_left), proto::_value(proto::_right))>
{};
template<class E> struct program_expr;
struct program_generator
: proto::or_<
proto::when,
proto::terminalproto::_ > >,
proto::pod_generator(proto::_expr)>,
proto::whenproto::_ >,
by_ref_generator(proto::_expr)>,
proto::otherwiseproto::_expr
{};
struct program_domain
: proto::domain
{};
template<typename A0>
typename proto::result_of::make_expr::type
Program_(A0 &a0)
{
return proto::make_expr(
program_(),
boost::ref(a0)
);
}
template<typename Expr>
struct program_expr {
BOOST_PROTO_BASIC_EXTENDS(Expr, program_expr<Expr>, program_domain);
BOOST_PROTO_EXTENDS_SUBSCRIPT();
typedef void result_type;
result_type operator()(int a) {
std::cout << "program with one arg\n";
(proto::value(proto::child_c<1>(*this))).assign(a);
}
};
typedef proto::terminal::type int32_;
int main()
{
int32_ a,b,c;
assign()(a=20);
assign()(a(35));
Program_(a)(40);
std::cout << proto::value(a).value << std::endl;
Program_(a)(50);
std::cout << proto::value(a).value << std::endl;
}