[proto] defining function call operators on expressions

I am trying to define operator()(...) for my expressions. I want to be able to form expression of the form Program_(a,b) and call Program_(a, b)(10,20). Here, 'a' and 'b' are terminals and I have a mechanism shown in the code below that somehow gets the type suitable for the parameter position. Suppose 'a' is terminal<Var<int> >, I want the first parameter position in Program_(a,b) (pos1, pos2) to be of the type 'int'. typename boost::result_of<call_param_type<M>(const Expr &)>::type gives me that type for the Mth argument in the expression of the form Program_(a1,a2,...). Now, I am trying to use this to define operator()(...) in my program_expr class. That doesn't seem to work. However, if I define a separate function callfunc, that takes the expression and the arguments, that seems to work. In the following code, enabling the if 0'ed out code results in compilation errors. How do I go about defining operator()(...) in my program_expr class with the above property? Thanks, Manjunath #include <boost/proto/proto.hpp> #include <boost/proto/proto_typeof.hpp> #include <boost/mpl/bool.hpp> #include <boost/mpl/assert.hpp> #include <iostream> #include <sstream> #include <string> namespace proto=boost::proto; namespace mpl=boost::mpl; unsigned int ids; template<typename VT> struct Var { unsigned int id; Var() { id = ++ids; } }; typedef proto::terminal<Var<int> >::type int32_; typedef proto::terminal<Var<float> >::type float_; struct program_ {}; template<class E> struct program_expr; struct program_domain : proto::domain<proto::pod_generator<program_expr> > { }; struct _var_type : proto::callable { template<typename Sig> struct result; template<typename This, typename T> struct result<This(const Var<T> &)> { typedef T type; }; }; struct var_type : proto::or_< proto::when<proto::terminal<Var<proto::_> >, _var_type(proto::_value)>
{};
template<int N> struct call_param_type : proto::or_< proto::when< proto::function< proto::terminal<program_>, proto::vararg<proto::terminal<Var<proto::_> > > >, var_type(proto::_child_c<N+1>)>
{};
template <typename Expr> struct program_expr { BOOST_PROTO_BASIC_EXTENDS(Expr, program_expr<Expr>, program_domain); BOOST_PROTO_EXTENDS_SUBSCRIPT(); typedef void result_type; #if 0 result_type operator()(typename boost::result_of<call_param_type<0>(const Expr &)>::type x) { std::cout << "program with one arg\n"; } result_type operator()(typename boost::result_of<call_param_type<0>(const Expr &)>::type x, typename boost::result_of<call_param_type<1>(const Expr &)>::type y) { std::cout << "program with two args\n"; } #endif }; template<typename Expr> void callfunc(const Expr &e, typename boost::result_of<call_param_type<0>(const Expr &)>::type x) { std::cout << "program with one arg\n"; } template<typename Expr> void callfunc(const Expr &e, typename boost::result_of<call_param_type<0>(const Expr &)>::type x, typename boost::result_of<call_param_type<1>(const Expr &)>::type y) { std::cout << "program with two args\n"; } template<typename A0> typename proto::result_of::make_expr<proto::tag::function , program_domain , program_ , A0 const &>::type const Program_(A0 const &a0) { return proto::make_expr<proto::tag::function, program_domain>( program_(), boost::ref(a0)); } template<typename A0, typename A1> typename proto::result_of::make_expr<proto::tag::function , program_domain , program_ , A0 const & , A1 const &>::type const Program_(A0 const &a0, A1 const &a1) { return proto::make_expr<proto::tag::function, program_domain>( program_(), boost::ref(a0), boost::ref(a1)); } int main() { int32_ a,b,c; callfunc(Program_(a, b), 10, 20); #if 0 Program_(a, b)(10, 20); #endif }

On 2/28/2010 7:17 PM, Manjunath Kudlur wrote:
I am trying to define operator()(...) for my expressions. I want to be able to form expression of the form Program_(a,b) and call Program_(a, b)(10,20). Here, 'a' and 'b' are terminals and I have a mechanism shown in the code below that somehow gets the type suitable for the parameter position. Suppose 'a' is terminal<Var<int> >, I want the first parameter position in Program_(a,b) (pos1, pos2) to be of the type 'int'. <snip>
OK.
template<int N> struct call_param_type : proto::or_< proto::when< proto::function< proto::terminal<program_>, proto::vararg<proto::terminal<Var<proto::_> > > >, var_type(proto::_child_c<N+1>)>
{};
OK, the call_param_type grammar only matches expressions of the type Program_(a, b).
template<typename Expr> struct program_expr { BOOST_PROTO_BASIC_EXTENDS(Expr, program_expr<Expr>, program_domain); BOOST_PROTO_EXTENDS_SUBSCRIPT();
typedef void result_type;
#if 0 result_type operator()(typename boost::result_of<call_param_type<0>(const Expr&)>::type x) { std::cout<< "program with one arg\n"; } <snip>
This function definition eagerly evaluates the call_param_type grammar's transform against Expr regardless of whether Expr matches call_param_type's grammar or not. Trouble. Look at how you define the Program_ function:
template<typename A0> typename proto::result_of::make_expr<proto::tag::function , program_domain , program_ , A0 const &>::type const Program_(A0 const &a0)
This creates an expression node with tag type "tag::function" and two children: a program_ terminal and A0. When make_expr turns program_ into a Proto terminal, it uses program_domain to wrap the terminal in a program_expr. That's bad news because a program_ terminal doesn't match the call_param_type grammar. You're violating the transform's precondition. Suggestion: when you are having problems with evaluating a transform, try adding an MPL assertion that the expression you're passing to it actually matches the grammar. HTH, -- Eric Niebler BoostPro Computing http://www.boostpro.com

On Sun, Feb 28, 2010 at 1:28 AM, Eric Niebler <eric@boostpro.com> wrote:
On 2/28/2010 7:17 PM, Manjunath Kudlur wrote:
I am trying to define operator()(...) for my expressions. I want to be able to form expression of the form Program_(a,b) and call Program_(a, b)(10,20). Here, 'a' and 'b' are terminals and I have a mechanism shown in the code below that somehow gets the type suitable for the parameter position. Suppose 'a' is terminal<Var<int> >, I want the first parameter position in Program_(a,b) (pos1, pos2) to be of the type 'int'.
<snip>
OK.
template<int N> struct call_param_type : proto::or_< proto::when< proto::function< proto::terminal<program_>, proto::vararg<proto::terminal<Var<proto::_> > > >, var_type(proto::_child_c<N+1>)>
{};
OK, the call_param_type grammar only matches expressions of the type Program_(a, b).
template<typename Expr> struct program_expr { BOOST_PROTO_BASIC_EXTENDS(Expr, program_expr<Expr>, program_domain); BOOST_PROTO_EXTENDS_SUBSCRIPT();
typedef void result_type;
#if 0 result_type operator()(typename boost::result_of<call_param_type<0>(const Expr&)>::type x) { std::cout<< "program with one arg\n"; }
<snip>
This function definition eagerly evaluates the call_param_type grammar's transform against Expr regardless of whether Expr matches call_param_type's grammar or not. Trouble. Look at how you define the Program_ function:
template<typename A0> typename proto::result_of::make_expr<proto::tag::function , program_domain , program_ , A0 const &>::type const Program_(A0 const &a0)
This creates an expression node with tag type "tag::function" and two children: a program_ terminal and A0. When make_expr turns program_ into a Proto terminal, it uses program_domain to wrap the terminal in a program_expr. That's bad news because a program_ terminal doesn't match the call_param_type grammar. You're violating the transform's precondition.
Yeah, got it. I realise now that the passing a domain to make_expr wraps all the components of the expression also with the corresponding expression class. What I am unable to figure out it how to wrap only the final expression I want with program_expr. In this case I want to wrap expr<tag::function, vararg<terminal<Var> > > with program_expr. Is there an example I can look at that does something similar? Or is this the wrong way to look at the problem? Basically, I want to add the function call operator to my expressions, but only to expressions of certain forms. My thinking is, if I only wrap expressions of that certain form with my expression class, I should be OK, no? Manjunath
Suggestion: when you are having problems with evaluating a transform, try adding an MPL assertion that the expression you're passing to it actually matches the grammar.
HTH,
-- Eric Niebler BoostPro Computing http://www.boostpro.com _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users

On 3/1/2010 2:21 PM, Manjunath Kudlur wrote:
Yeah, got it. I realise now that the passing a domain to make_expr wraps all the components of the expression also with the corresponding expression class. What I am unable to figure out it how to wrap only the final expression I want with program_expr. In this case I want to wrap expr<tag::function, vararg<terminal<Var> > > with program_expr. Is there an example I can look at that does something similar? Or is this the wrong way to look at the problem? Basically, I want to add the function call operator to my expressions, but only to expressions of certain forms. My thinking is, if I only wrap expressions of that certain form with my expression class, I should be OK, no?
Ah, you want a different expression interface based on the structure of the expression being wrapped. There's a straightforward way to do that, but it's not obvious. See the program below. #include <boost/utility/result_of.hpp> #include <boost/utility/enable_if.hpp> #include <boost/proto/proto.hpp> namespace proto = boost::proto; using proto::_; // This wrapper is for function expressions template<typename Expr> struct function_expr; // This wrapper is for all other expressions template<typename Expr> struct program_expr; struct function_call : proto::function< proto::vararg< proto::terminal<_> > > {}; struct program_generator : proto::or_< // Wrap function expressions in function_expr proto::when< function_call // pod_generator is a function object and // so can be used to make this callable // transform , proto::pod_generator<function_expr>(_) > // Wrap all other expressions in program_expr , proto::otherwise< proto::pod_generator<program_expr>(_) > > {}; // A program_generator satisfies the requirement for // a proto generator because it is a unary function // object, albeit a rather complicated one. struct program_domain : proto::domain< program_generator > {}; template<typename Expr> struct function_expr { BOOST_PROTO_BASIC_EXTENDS(Expr, function_expr<Expr>, program_domain) BOOST_PROTO_EXTENDS_FUNCTION() void I_am_a_function() const {} }; template<typename Expr> struct program_expr { BOOST_PROTO_BASIC_EXTENDS(Expr, program_expr<Expr>, program_domain) BOOST_PROTO_EXTENDS_FUNCTION() void I_am_not_a_function() const {} }; program_expr<proto::terminal<int>::type> i = {{1}}; program_expr<proto::terminal<int>::type> j = {{2}}; int main() { i.I_am_not_a_function(); (i + j).I_am_not_a_function(); i(j).I_am_a_function(); (i(j) + j(i)).I_am_not_a_function(); } -- Eric Niebler BoostPro Computing http://www.boostpro.com

On Sun, Feb 28, 2010 at 8:18 PM, Eric Niebler <eric@boostpro.com> wrote:
On 3/1/2010 2:21 PM, Manjunath Kudlur wrote:
Yeah, got it. I realise now that the passing a domain to make_expr wraps all the components of the expression also with the corresponding expression class. What I am unable to figure out it how to wrap only the final expression I want with program_expr. In this case I want to wrap expr<tag::function, vararg<terminal<Var> > > with program_expr. Is there an example I can look at that does something similar? Or is this the wrong way to look at the problem? Basically, I want to add the function call operator to my expressions, but only to expressions of certain forms. My thinking is, if I only wrap expressions of that certain form with my expression class, I should be OK, no?
Ah, you want a different expression interface based on the structure of the expression being wrapped. There's a straightforward way to do that, but it's not obvious. See the program below.
Thanks, it seems obvious now :). I am including my code too, so that there is one more example for people to look at. I began wondering about a related problem when writing this code. Because of the way program_generator is defined below, any expression of the form Program_(a0, a1... an) gets wrapped in program_expr. Suppose I want to restrict the number of parameters to the function call operator to the number of arguments to Program_(). In other words, I want to make Program_(a0, a1)(10, 20, 30), Program_(a0, a1)(10) etc. illegal. I can invent one expression wrapper for every possible Program_(..) and in that wrapper class, define the function call operator with the correct number of arguments, i.e., wrap Program_(a0) in program1_expr, wrap Program_(a0, a1) in program2_expr etc., and define these classes. That would work of course. Is there a way to define member functions in the wrapper class that are valid only for certain kinds of expressions? I experimented a little with boost::enable_if, but that didn't seem to work. Any thoughts? Manjunath #include <boost/proto/proto.hpp> #include <boost/proto/proto_typeof.hpp> #include <boost/mpl/bool.hpp> #include <boost/mpl/assert.hpp> #include <boost/utility.hpp> #include <iostream> #include <sstream> #include <string> namespace proto=boost::proto; namespace mpl=boost::mpl; unsigned int ids; template<typename VT> struct Var { unsigned int id; Var() { id = ++ids; } }; typedef proto::terminal<Var<int> >::type int32_; typedef proto::terminal<Var<float> >::type float_; struct program_ {}; template<class E> struct program_expr; struct call_grammar : proto::or_< proto::function< proto::terminal<program_>, proto::vararg<proto::terminal<Var<proto::_> > > >
{};
struct program_generator : proto::or_< proto::when<call_grammar, proto::pod_generator<program_expr>(proto::_expr) >, proto::otherwise<proto::_expr>
{};
struct program_domain : proto::domain<program_generator> {}; struct _var_type : proto::callable { template<typename Sig> struct result; template<typename This, typename T> struct result<This(const Var<T> &)> { typedef T type; }; }; struct var_type : proto::or_< proto::when<proto::terminal<Var<proto::_> >, _var_type(proto::_value)> > {}; template<int N> struct call_param_type : proto::or_< proto::when< proto::function< proto::terminal<program_>, proto::vararg<proto::terminal<Var<proto::_> > > >, var_type(proto::_child_c<N+1>)> > {}; 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()(typename boost::result_of<call_param_type<0>(const Expr &)>::type x) const { std::cout << "program with one arg\n"; } result_type operator()(typename boost::result_of<call_param_type<0>(const Expr &)>::type x, typename boost::result_of<call_param_type<1>(const Expr &)>::type y) const { std::cout << "program with two args\n"; } }; template<typename A0> typename proto::result_of::make_expr<proto::tag::function , program_domain , program_ , A0 const &>::type const Program_(A0 const &a0) { return proto::make_expr<proto::tag::function, program_domain>( program_(), boost::ref(a0)); } template<typename A0, typename A1> typename proto::result_of::make_expr<proto::tag::function , program_domain , program_ , A0 const & , A1 const &>::type const Program_(A0 const &a0, A1 const &a1) { return proto::make_expr<proto::tag::function, program_domain>( program_(), boost::ref(a0), boost::ref(a1)); } int main() { int32_ a,b,c; //Program_(a)(10); Program_(a, b)(10, 20); Program_(a, b)(10); }

Manjunath Kudlur wrote:
Is there a way to define member functions in the wrapper class that are valid only for certain kinds of expressions? I experimented a little with boost::enable_if, but that didn't seem to work. Any thoughts?
Look at how the proto calculator works. You just need to compute () ariy using a transform adn then juse a static assert in the expression evaluation

On 3/2/2010 6:24 PM, Joel Falcou wrote:
Manjunath Kudlur wrote:
Is there a way to define member functions in the wrapper class that are valid only for certain kinds of expressions? I experimented a little with boost::enable_if, but that didn't seem to work. Any thoughts?
Look at how the proto calculator works. You just need to compute () ariy using a transform adn then juse a static assert in the expression evaluation
It's not actually quite that simple in this case. Manjunath has defined his function call overloads as follows: result_type operator()( typename boost::result_of<call_param_type<0>(const Expr &)>::type x, typename boost::result_of<call_param_type<1>(const Expr &)>::type y) This function is *not* a template, so the types mentioned in its signature are evaluated eagerly. The call_param_type transform extracts the Nth child from Expr. If Expr doesn't have enough child nodes, it violates the transform's preconditions. Compiler errors are sure to result. One solution would be to rewrite the overloads as: template<typename A0, typename A1> result_type operator()(A0 const &a0, A1 const &a0) const and then within the function add compile-time assertions that (a) the number of arguments is currect, and (b) that each argument type is implicitly convertible to the expected types. The other option would be to move the function call overloads into a CRTP base that is partially specialized on the number of arguments, like: template<class Expr, long Arity = Expr::proto_arity_c> struct program_expr_base; template<class Expr> struct program_expr_base<Expr, 2> { void operator()( typename boost::result_of< call_param_type<0>(Expr const &) >::type x ) const { ... } }; // more specializations. See docs for BOOST_PROTO_LOCAL_ITERATE // and friends Then your program_expr inherits from program_expr_base. At this point, I would also give up on making program_expr a POD and just use proto::extends. Finally, there'll be an ambiguity because both program_expr_base and boost::extends will define operator(). To resolve that, you'll need "using program_expr_base<Expr>::operator();" in program_expr. HTH, -- Eric Niebler BoostPro Computing http://www.boostpro.com

Eric Niebler wrote:
It's not actually quite that simple in this case. Manjunath has defined his function call overloads as follows:
result_type operator()( typename boost::result_of<call_param_type<0>(const Expr &)>::type x, typename boost::result_of<call_param_type<1>(const Expr &)>::type y)
This function is *not* a template, so the types mentioned in its signature are evaluated eagerly. The call_param_type transform extracts the Nth child from Expr. If Expr doesn't have enough child nodes, it violates the transform's preconditions. Compiler errors are sure to result.
Oh good catch, I missed that.

One solution would be to rewrite the overloads as:
template<typename A0, typename A1> result_type operator()(A0 const &a0, A1 const &a0) const
and then within the function add compile-time assertions that (a) the number of arguments is currect, and (b) that each argument type is implicitly convertible to the expected types.
Here is the code as per the above suggestion, preserved for posterity : #include <boost/proto/proto.hpp> #include <boost/proto/proto_typeof.hpp> #include <boost/mpl/bool.hpp> #include <boost/mpl/assert.hpp> #include <boost/mpl/not.hpp> #include <boost/mpl/identity.hpp> #include <boost/utility.hpp> #include <boost/type_traits/is_same.hpp> #include <iostream> #include <sstream> #include <string> namespace proto=boost::proto; namespace mpl=boost::mpl; unsigned int ids; template<typename VT> struct Var { unsigned int id; Var() { id = ++ids; } }; typedef proto::terminal<Var<int> >::type int32_; typedef proto::terminal<Var<float> >::type float_; struct program_ {}; template<class E> struct program_expr; struct call_grammar : proto::or_< proto::function< proto::terminal<program_>, proto::vararg<proto::terminal<Var<proto::_> > > >
{};
struct _var_arity : proto::or_< proto::when<proto::terminal<Var<proto::_> >, proto::make<mpl::int_<1> > >
{};
struct _prog_arity : proto::or_< proto::when< proto::function<proto::terminal<program_>, proto::vararg<proto::terminal<Var<proto::_> > > >, proto::fold<proto::functional::pop_front(proto::_expr), proto::make<mpl::int_<0> >, proto::make<mpl::plus<proto::_state, _var_arity> > > >
{};
template<typename Expr> struct prog_arity : boost::result_of<_prog_arity(Expr)> {}; struct program_generator : proto::or_< proto::when<call_grammar, proto::pod_generator<program_expr>(proto::_expr) >, proto::otherwise<proto::_expr>
{};
struct program_domain : proto::domain<program_generator> {}; struct _var_type : proto::callable { template<typename Sig> struct result; template<typename This, typename T> struct result<This(const Var<T> &)> { typedef T type; }; }; struct var_type : proto::or_< proto::when<proto::terminal<Var<proto::_> >, _var_type(proto::_value)> > {}; template<int N> struct call_param_type : proto::or_< proto::when< proto::function< proto::terminal<program_>, proto::vararg<proto::terminal<Var<proto::_> > > >, var_type(proto::_child_c<N+1>)> > {}; template <typename Expr> struct program_expr { BOOST_PROTO_BASIC_EXTENDS(Expr, program_expr<Expr>, program_domain); BOOST_PROTO_EXTENDS_SUBSCRIPT(); typedef void result_type; template<typename A0> result_type operator()(A0 a0) const { BOOST_MPL_ASSERT_RELATION(1, ==, prog_arity<Expr>::type::value); BOOST_MPL_ASSERT((boost::is_same<A0, typename boost::result_of<call_param_type<0>(const Expr &)>::type>)); std::cout << "program with one arg\n"; } template<typename A0, typename A1> result_type operator()(A0 a0, A1 a1) const { BOOST_MPL_ASSERT_RELATION(2, ==, prog_arity<Expr>::type::value); BOOST_MPL_ASSERT((boost::is_same<A0, typename boost::result_of<call_param_type<0>(const Expr &)>::type>)); BOOST_MPL_ASSERT((boost::is_same<A1, typename boost::result_of<call_param_type<1>(const Expr &)>::type>)); std::cout << "program with two args\n"; } template<typename A0, typename A1, typename A2> result_type operator()(A0 a0, A1 a1, A2 a2) const { BOOST_MPL_ASSERT_RELATION(3, ==, prog_arity<Expr>::type::value); BOOST_MPL_ASSERT((boost::is_same<A0, typename boost::result_of<call_param_type<0>(const Expr &)>::type>)); BOOST_MPL_ASSERT((boost::is_same<A1, typename boost::result_of<call_param_type<1>(const Expr &)>::type>)); BOOST_MPL_ASSERT((boost::is_same<A2, typename boost::result_of<call_param_type<2>(const Expr &)>::type>)); std::cout << "program with three args\n"; } }; template<typename A0> typename proto::result_of::make_expr<proto::tag::function , program_domain , program_ , A0 const &>::type const Program_(A0 const &a0) { return proto::make_expr<proto::tag::function, program_domain>( program_(), boost::ref(a0)); } template<typename A0, typename A1> typename proto::result_of::make_expr<proto::tag::function , program_domain , program_ , A0 const & , A1 const &>::type const Program_(A0 const &a0, A1 const &a1) { return proto::make_expr<proto::tag::function, program_domain>( program_(), boost::ref(a0), boost::ref(a1)); } template<typename A0, typename A1, typename A2> typename proto::result_of::make_expr<proto::tag::function , program_domain , program_ , A0 const & , A1 const & , A2 const &>::type const Program_(A0 const &a0, A1 const &a1, A2 const &a3) { return proto::make_expr<proto::tag::function, program_domain>( program_(), boost::ref(a0), boost::ref(a1), boost::ref(a3)); } int main() { int32_ a,b,c; int p1 = 10, p2 = 20, p3 = 30; Program_(a)(p1); Program_(a,b)(10, p2); Program_(a,b,c)(10, 20, p3); }
participants (3)
-
Eric Niebler
-
Joel Falcou
-
Manjunath Kudlur