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 <boost/proto/proto.hpp> #include <boost/proto/proto_typeof.hpp> #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<typename This, typename Expr> struct result<This(Expr &)> { typedef Expr& type; }; template<typename This, typename Expr> struct result<This(Expr)> { typedef Expr& type; }; template<typename Expr> typename result<by_ref_generator(Expr &)>::type operator ()(Expr &e) const { return e; } }; struct assign : proto::or_< proto::when<proto::terminal<Var<proto::_> >, _assign(proto::_value)>, proto::when<proto::assign<proto::terminal<Var<proto::_> >, proto::literal<proto::_> >, _assign(proto::_value(proto::_left), proto::_value(proto::_right))>, proto::when<proto::function<proto::terminal<Var<proto::_> >, proto::literal<proto::_> >, _assign(proto::_value(proto::_left), proto::_value(proto::_right))>
{};
template<class E> struct program_expr; struct program_generator : proto::or_< proto::when<proto::function<proto::terminal<program_>, proto::terminal<Var<proto::_> > >, proto::pod_generator<program_expr>(proto::_expr)>, proto::when<proto::terminal<Var<proto::_> >, by_ref_generator(proto::_expr)>, proto::otherwise<proto::_expr>
{};
struct program_domain : proto::domain<program_generator> {}; template<typename A0> typename proto::result_of::make_expr<proto::tag::function , program_domain , program_ , A0 &>::type Program_(A0 &a0) { return proto::make_expr<proto::tag::function, program_domain>( 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<Var<int> >::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; }