[phoenix] v2 to v3 transition of extension mechanism
Hi, I am updating some of my code to use Phoenix3 instead of Phoenix2. For example in Phoenix2 I used to have this extension to support 'cos(...)' expressions: namespace boost{ namespace phoenix{ struct cos_eval{ template<class Env, typename Arg_> struct result{typedef double type;}; template<class RT, typename Env, typename Arg_> static RT eval(Env const& env, Arg_ const& arg_){ return cos(arg_.eval(env)); } }; template<typename Arg_> void cos(Arg_ const& arg_){ return compose<cos_eval>(arg_); } }} In Phoenix3, if I followed things correctly (from here https://svn.boost.org/svn/boost/sandbox/SOC/2010/phoenix3/libs/phoenix/doc/h...) the new framework ask for the following, which doesn't work. The error (see at end) is difficult to decipher. What am I missing to implement this simple expression extension? -- Thanks. #include<boost/phoenix/phoenix.hpp> //v3 BOOST_PHOENIX_DEFINE_EXPRESSION( (boost)(phoenix)(cos_) , (meta_grammar) // Arg ) namespace boost { namespace phoenix { struct cos_eval{ typedef double result_type; template <typename Context, typename Arg> result_type operator()(Context const& ctx, Arg const& arg) const{ return std::cos(eval(arg, ctx)); } }; template <typename Dummy> struct default_actions::when<rule::cos_, Dummy> : call<cos_eval, Dummy> {}; template <typename Arg> typename expression::cos_<Arg>::type const cos_(Arg const& arg){ return expression::cos_<Arg>::make(arg); } }} usr/include/boost/proto/matches.hpp:846:13: error: no type named ‘proto_tag’ in ‘const struct boost::phoenix::vector2<boost::phoenix::vector2<const boost::phoenix::actor<boost::proto::exprns_::basic_expr<boost::phoenix::tag::cos_, boost::proto::argsns_::list1<boost::phoenix::actor<boost::proto::exprns_::expr<boost::proto::tag::terminal, boost::proto::argsns_::term<boost::phoenix::argument<1> >, 0l> > >, 1l> >*, double&>&, const boost::phoenix::default_actions&>’ compilation terminated due to -Wfatal-errors.
jeje, I just found boost/phoenix/stdlib/cmath.hpp which exactly implements a lazy 'cos' function. Anyway it would be good to understand what I was missing in my implementation since the boost/phoenix/stdlib/cmath.hpp (by Steven Watanabe) seems to have some complicated MACRO magic in it. Thanks, Alfredo On Apr 11, 10:59 pm, alfC <alfredo.cor...@gmail.com> wrote:
Hi, I am updating some of my code to use Phoenix3 instead of Phoenix2.
For example in Phoenix2 I used to have this extension to support 'cos(...)' expressions:
namespace boost{ namespace phoenix{ struct cos_eval{ template<class Env, typename Arg_> struct result{typedef double type;}; template<class RT, typename Env, typename Arg_> static RT eval(Env const& env, Arg_ const& arg_){ return cos(arg_.eval(env)); } }; template<typename Arg_> void cos(Arg_ const& arg_){ return compose<cos_eval>(arg_); }
}}
In Phoenix3, if I followed things correctly (from herehttps://svn.boost.org/svn/boost/sandbox/SOC/2010/phoenix3/libs/phoeni...) the new framework ask for the following, which doesn't work. The error (see at end) is difficult to decipher. What am I missing to implement this simple expression extension? -- Thanks.
#include<boost/phoenix/phoenix.hpp> //v3 BOOST_PHOENIX_DEFINE_EXPRESSION( (boost)(phoenix)(cos_) , (meta_grammar) // Arg )
namespace boost { namespace phoenix { struct cos_eval{ typedef double result_type;
template <typename Context, typename Arg> result_type operator()(Context const& ctx, Arg const& arg) const{ return std::cos(eval(arg, ctx)); } };
template <typename Dummy> struct default_actions::when<rule::cos_, Dummy> : call<cos_eval, Dummy> {};
template <typename Arg> typename expression::cos_<Arg>::type const cos_(Arg const& arg){ return expression::cos_<Arg>::make(arg); }
}}
usr/include/boost/proto/matches.hpp:846:13: error: no type named ‘proto_tag’ in ‘const struct boost::phoenix::vector2<boost::phoenix::vector2<const boost::phoenix::actor<boost::proto::exprns_::basic_expr<boost::phoenix::tag::cos_, boost::proto::argsns_::list1<boost::phoenix::actor<boost::proto::exprns_::expr<boost::proto::tag::terminal, boost::proto::argsns_::term<boost::phoenix::argument<1> >, 0l> > >, 1l> >*, double&>&, const boost::phoenix::default_actions&>’ compilation terminated due to -Wfatal-errors. _______________________________________________ Boost-users mailing list Boost-us...@lists.boost.orghttp://lists.boost.org/mailman/listinfo.cgi/boost-users
On Tuesday, April 12, 2011 08:14:34 AM alfC wrote:
jeje, I just found boost/phoenix/stdlib/cmath.hpp which exactly implements a lazy 'cos' function.
Anyway it would be good to understand what I was missing in my implementation since the boost/phoenix/stdlib/cmath.hpp (by Steven Watanabe) seems to have some complicated MACRO magic in it.
Stay tuned ... :) I have a bunch of commits and already compiled a reply explaining what went wrong ... boost/phoenix/stdlib/cmath.hpp will be moved to boost/phoenix/stl/cmath.hpp Docs explaining the macro "magic" will be following shortly too.
Thanks, Alfredo
On Apr 11, 11:47 pm, Thomas Heller <thom.hel...@googlemail.com> wrote:
Stay tuned ... :)
Thank you, I'll wait for the good stuff. By the way, since I was implementing my own I have been thinking about where to put these functions. They are strictly implemented with functions in std::, so, does it makes sense to make parallel namespaces? like "boost::phoenix::std::cos" is it a good idea?, or it creates other problems related to parallel namespaces? What about leaving the namespace (e.g. std) out of the implementation of cos_impl to allow for other versions (e.g. from other libraries) of eager "cos" to be called, perhaps from context? Thanks, Alfredo
On Tuesday, April 12, 2011 07:59:13 AM alfC wrote:
Hi, I am updating some of my code to use Phoenix3 instead of Phoenix2.
For example in Phoenix2 I used to have this extension to support 'cos(...)' expressions:
namespace boost{ namespace phoenix{ struct cos_eval{ template<class Env, typename Arg_> struct result{typedef double type;}; template<class RT, typename Env, typename Arg_> static RT eval(Env const& env, Arg_ const& arg_){ return cos(arg_.eval(env)); } }; template<typename Arg_> void cos(Arg_ const& arg_){ return compose<cos_eval>(arg_); } }}
In Phoenix3, if I followed things correctly (from here
https://svn.boost.org/svn/boost/sandbox/SOC/2010/phoenix3/libs/phoenix/doc/h...) Your browser is pointing to the wrong URL, use that one: https://svn.boost.org/svn/boost/trunk/libs/phoenix/doc/html/phoenix/examples...
the new framework ask for the following, which doesn't work. The error (see at end) is difficult to decipher. What am I missing to implement this simple expression extension? -- Thanks.
You did everything right ... conforming to the old docs ... however I changed one little detail. Explanation and fix inlined in the code below.
namespace boost { namespace phoenix { struct cos_eval{ typedef double result_type;
Here is the change:
template <typename Context, typename Arg> result_type operator()(Context const& ctx, Arg const& arg) const{ return std::cos(eval(arg, ctx)); }
Change it to: template <typename Arg, typename Context> result_type operator()(Arg const& arg, Context const& ctx) const{ return std::cos(eval(arg, ctx)); } I moved the Context argument to the end of the argument list so it gets more consistent to the eval function. eval takes the context as the last argument because its the proto state, thus its the second parameter. The rest of your code looks good. Sorry for that little inconvenience.
usr/include/boost/proto/matches.hpp:846:13: error: no type named ‘proto_tag’ in ‘const struct boost::phoenix::vector2<boost::phoenix::vector2<const
boost::phoenix::actor<boost::proto::exprns_::basic_expr<boost::phoenix::tag::cos_,
boost::proto::argsns_::list1<boost::phoenix::actor<boost::proto::exprns_::expr<boost::proto::tag::terminal,
boost::proto::argsns_::term<boost::phoenix::argument<1> >, 0l> > >, 1l> >*, double&>&, const boost::phoenix::default_actions&>’ compilation terminated due to -Wfatal-errors.
The error you got here is basically saying: The context is not a proto expression. Since it was passed as first argument to your function it was assumed to be one, which lead to the error. FWIW, phoenix no has a cmath module (note: this file moved): #include <boost/phoenix/stl/cmath.hpp> and you get all functions from cmath in the lazy version.
Hi Tomas, On Apr 13, 12:13 pm, Thomas Heller <thom.hel...@googlemail.com> wrote:
I moved the Context argument to the end of the argument list so it gets more consistent to the eval function. eval takes the context as the last argument because its the proto state, thus its the second parameter.
With your changes now it works. I post the complete code below, to document this for others. (be advises that this is an example only, the lazy cos is implemented in boost/phoenix/stl/cmath.hpp) The code has a couple of comments on obscure points that I still don't understand. For example, 1) I tried to use decltype so to have the generic return type of the function but it didn't work. 2) Can the std (for std::cos) specification be omitted so a context dependent 'cos' in an arbitrary namesapce is called? (depending on the return type of the nested eval()? In such case the lazy cos can be used for other exotic implementations of eager cos, even in different namespace. 3) Does it worth to use enable_if to construct the lazy function to avoid name clashes and horrible errors?, i.e. such that lazy functions are called only on phoenix expressions see the numbers below, in the following working code: #include<boost/phoenix/core.hpp> #include<cmath> BOOST_PHOENIX_DEFINE_EXPRESSION( (boost)(phoenix)(cos) , (meta_grammar) // Arg ) namespace boost { namespace phoenix { struct cos_eval{ template<class Sig> struct result; template<class This, class Arg, class Cont> struct result<This(Arg, Cont)>{ typedef double //works, but not generic //decltype(std::cos(eval(Arg(), Cont()))) // (1) doesn't work, why? type; }; template <typename Arg, typename Context> typename result<cos_eval(Arg const&, Context const&)>::type operator()(Arg const& arg, Context const& ctx) const{ using namespace std; // (2) can this be avoided in order to call a context-namespace-dependent eager 'cos' return cos(eval(arg, ctx)); } }; template <typename Dummy> struct default_actions::when<rule::cos, Dummy> : call<cos_eval, Dummy> {}; template <typename Arg> typename expression::cos<Arg>::type const cos( Arg const& arg, typename enable_if_c<is_actor<Arg>::value, void*>::type = 0 //worth it? ){ return expression::cos<Arg>::make(arg); } }} #include <boost/phoenix/operator.hpp> #include<iostream> using namespace boost::phoenix::arg_names; int main(){ double r; r = cos(arg1 + 1.)(2.); std::cout << r << std::endl; //prints -0.989992 return 0; }
On Thu, Apr 14, 2011 at 8:26 AM, alfC <alfredo.correa@gmail.com> wrote:
The code has a couple of comments on obscure points that I still don't understand. For example, 1) I tried to use decltype so to have the generic return type of the function but it didn't work. See below. 2) Can the std (for std::cos) specification be omitted so a context dependent 'cos' in an arbitrary namesapce is called? (depending on the return type of the nested eval()? In such case the lazy cos can be used for other exotic implementations of eager cos, even in different namespace.
Maybe a good idea, maybe leading to tons of ambiguity errors. Any opinions about that?
3) Does it worth to use enable_if to construct the lazy function to avoid name clashes and horrible errors?, i.e. such that lazy functions are called only on phoenix expressions
I don't think so ... the expression::cos<...>::make function will turn non phoenix actors into phoenix actors. The intention is that the cos expression generator function is truly generic. SFINAEing out certain types might be impossible even more when you want the cos function to be picked up by ADL.
see the numbers below, in the following working code:
#include<boost/phoenix/core.hpp> #include<cmath>
BOOST_PHOENIX_DEFINE_EXPRESSION( (boost)(phoenix)(cos) , (meta_grammar) // Arg )
namespace boost { namespace phoenix { struct cos_eval{ template<class Sig> struct result; template<class This, class Arg, class Cont> struct result<This(Arg, Cont)>{ typedef double //works, but not generic //decltype(std::cos(eval(Arg(), Cont()))) // (1) doesn't work, why? type; }; <snip> Ok, this doesn't work because you are trying to value initialize a reference. A small workaround:
namespace detail { template <typename T> T make(); } // ... template<class Sig> struct result; template<class This, class Arg, class Cont> struct result<This(Arg, Cont)>{ typedef decltype(cos(eval(detail::make<Arg>(), detail::make<Cont>()))) type; }; // ... Regards, Thomas
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 AMDG On 04/14/2011 12:45 AM, Thomas Heller wrote:
Ok, this doesn't work because you are trying to value initialize a reference. A small workaround:
namespace detail { template <typename T> T make(); }
// ... template<class Sig> struct result; template<class This, class Arg, class Cont> struct result<This(Arg, Cont)>{ typedef decltype(cos(eval(detail::make<Arg>(), detail::make<Cont>()))) type; }; // ...
Or use boost::declval (http://www.boost.org/libs/utility/doc/html/declval.html) In Christ, Steven Watanabe -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org/ iQEcBAEBAgAGBQJNpv5xAAoJEDTBQuhymLHSQugH+gJwn+QuRPdI09nITGzK5Ndw zoPin8uWTNw6824pocKHw7wS21i4vs3IiXNoVRR5AN9EuREWFYeqRxjVz7bQKknR JsWxw4Hok+45IEmsCY4iMv7s33PQEpKr6jgkqZtVAI2WMsMnjnQJqrO3USKTkUMs yatQZHDFxHKLrySLifvwk/NV8dfGKhAHew4Iw/XjlZhIw2ndV8OLb0+PgMewUcLq 8JLsGUJjzqxwIw4CBTyy/lthrwsaZxZQTsmuuYEvAykdBN6jGsl7mFyrki18PGM+ y3Vnt6ukIWFihBLvd08HpFKFrBBUuvlxfjvHS2UlL3puOJjsOwdOy29WHpbbwKI= =8whX -----END PGP SIGNATURE-----
participants (3)
-
alfC
-
Steven Watanabe
-
Thomas Heller