2

I would like to use instances of a non-proto class as proto terminals for all purposes. To enable this functionality, I use is_terminal metafunction and pass it to BOOST_PROTO_DEFINE_OPERATORS().

This actually defines the operators, so the following expression makes an expression tree, as expected:

non_proto_obj * proto_obj; // creates proto expression tree

However, I cannot do this:

non_proto_obj = proto_obj; // no operator=
non_proto_obj[proto_obj]; // no operator[]

While the opposite does compile:

proto_obj = non_proto_obj;
proto_obj[non_proto_obj];

It seems that my object is not convertible to proto expression. Is there any workaround for this issue?

(On Coliru)

#include <iostream>
#include <boost/proto/proto.hpp>
#include <boost/mpl/int.hpp>

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

template<typename Expr>
struct my_expression;

struct my_domain : proto::domain<proto::generator<my_expression> >
{};

template<int I> struct placeholder {};

template<typename Expr>
struct my_expression : proto::extends<Expr, my_expression<Expr>, my_domain>
{
    explicit my_expression(Expr const &expr = Expr()) :
my_expression::proto_extends(expr)
    {}

    BOOST_PROTO_EXTENDS_USING_ASSIGN(my_expression<Expr>)
};

const my_expression<proto::terminal<placeholder<1>>::type> _1;

namespace app
{
  struct non_proto_type
  {};

  template <typename T>
  struct is_terminal : mpl::false_
  {};
  template<>
  struct is_terminal<non_proto_type> : mpl::true_
  {};
  BOOST_PROTO_DEFINE_OPERATORS(is_terminal, my_domain)

  non_proto_type non_proto;
}

int main()
{
    _1 = app::non_proto; // ok, builds temporary proto expression
    //app::non_proto = _1; // does not compile! no operator=
    _1[app::non_proto]; // ok
    //app::non_proto[_1]; // does not compile! no operator[]
    (+app::non_proto)[_1]; // ok! applying unary + to make a proto expression first, then operator[] is available
}
Igor R.
  • 14,716
  • 2
  • 49
  • 83
  • Maybe those operators are the ones that can't be defined "externally" to the struct/class (besides `operator()`). It's possible that adding the tags [tag:boost] and/or [tag:c++] could give your question more visibility, and a higher chance to get help. – llonesmiz May 31 '16 at 15:10
  • @jv_ Right, but I hoped proto would have some workaround for this :). – Igor R. May 31 '16 at 17:31
  • 1
    I'm not confident at all about [this approach](http://coliru.stacked-crooked.com/a/5f14be8aeafffe11) so I'll just leave it as a comment in case it helps until (hopefully) you get a better answer. I've done something similar to what [Boost.Phoenix does with its `actors`](https://github.com/boostorg/phoenix/blob/develop/include/boost/phoenix/core/actor.hpp#L212). – llonesmiz May 31 '16 at 18:28
  • @jv_ thanks, defining the operators manually is an option, although somewhat intrusive... – Igor R. May 31 '16 at 19:42

1 Answers1

1

There is no work around.

The C++ language places restrictions on some operators. Notably, the indexing (operator[]), assignment and function call operators must be defined as a non-static member function.

This means that there can never be an implicit conversion on the "left-hand-side" operand in an expression of the type lhs[rhs]. End of story.

All EDSL frameworks I know have helper functions to decorate your "literal" expression as a domain expression, e.g. boost::phoenix::val(x) or boost::spirit::x3::as_parser(x).

sehe
  • 374,641
  • 47
  • 450
  • 633