Boost logo

Boost Users :

Subject: Re: [Boost-users] [lambda][phoenix] make lambda/phoenix play well with std::complex, imag, real, abs, etc.
From: Alfredo Correa (alfredo.correa_at_[hidden])
Date: 2010-06-18 17:21:21


On Mon, Jun 7, 2010 at 12:52 AM, Joel de Guzman
<joel_at_[hidden]> wrote:
> On 6/7/10 3:42 PM, Alfredo Correa wrote:
>>
>> On Sun, Jun 6, 2010 at 4:41 PM, Joel de Guzman
>> <joel_at_[hidden]>  wrote:
>>>
>>> Is there a reason why you are not using plain phoenix::function(s)
>>> for this?
>>
>> good point, I am still phoenix-challenged I guess. I got driven by the
>> "Composite" example in the manual.
>> This is the alternative implementation using phoenix::function:
>>
>> namespace boost{namespace phoenix{
>> struct real_impl{
>>     template<typename Arg>  struct result{
>>         typedef double type; //can be generalized
>>     };
>>     template<typename Arg>
>>     typename result<Arg>::type
>>     operator()(Arg z) const{
>>         return real(z);
>>     }
>> };
>> function<real_impl>  real_; // "real" as name doesn't work
>> }}
>> int main(){
>>        std::complex<double>  z(1.,2.);
>>        cout<<  real_(arg1)(z)<<endl;
>>         return 0;
>> }
>>
>> which works.
>> ...But there is a problem it seem that I can not call "real_" as
>> "real", so the resulting syntax is degraded.
>> (Either I have to call the phoenix::function real_ or put std::real in
>> the operator())
>> It seems that the overload get confused. Is there a way to go around this?
>> (my poorly written first version --with no phoenix::function-- at
>> least didn't have this limitation)
>
> Good point. Well, you can make real_ and friends use the extension
> mechanism as before, then use functions for all else.

I understand the underscore is necessary for reserved keywords, but
using it for everything is a pity.
So I stick with the solution that allows the same phoenix and (rest
of) C++ the same name.

In fact I realized that if I need any function (from example in std
cmath) it is good to define the following macro to "register" existing
functions, to be used in phoenix expressions.

#include<boost/spirit/home/phoenix.hpp>
#include <boost/utility/enable_if.hpp>
/// Extends any endomorphic function as a phoenix function,
endomorphic in the sense that the input and output are of the same
type
#define BOOST_PHOENIX_REGISTER_ENDOMORPHISM(FN) \
namespace boost{ \
namespace phoenix{ \
        struct FN##_eval{ \
                template <typename Env, typename Arg_> struct result{typedef
typename boost::mpl::at_c<typename Env::args_type, 0>::type type;}; \
                template <typename RT, typename Env, typename Arg_> static RT
eval(Env const& env, Arg_ const& arg_){ \
                        return FN(arg_.eval(env)); \
                } \
        }; \
        template <typename Arg_> \
        typename boost::enable_if_c<is_actor<Arg_>::value, \
                actor<typename as_composite<FN##_eval, Arg_>::type> \
>::type \
        FN(Arg_ const& arg_){ \
                return compose<FN##_eval>(arg_); \
        } \
}}

and then register any arbitrary function (this solution work if the
input/output type are the same. maybe with upcoming decltype we can do
better).

BOOST_PHOENIX_REGISTER_ENDOMORPHISM(cos);
BOOST_PHOENIX_REGISTER_ENDOMORPHISM(cosh);
BOOST_PHOENIX_REGISTER_ENDOMORPHISM(exp);
BOOST_PHOENIX_REGISTER_ENDOMORPHISM(log);
BOOST_PHOENIX_REGISTER_ENDOMORPHISM(log10);
...
This could be useful to make phoenix support a large portion of STL
(in this case cmath), I don't know if it is planned. Also there is no
necessity of the underscore.

Note two things:
1) I had to put an "enable_if_c" in order to avoid huge error messages
when the function is not really defined outside Phoenix. When the
function is not defined then the 'return FN(...)' line get confused
with the phoenix function with the same name.
2) I have a confusion with the namespaces, the phoenix function (e.g.
"cos") is in the namespace boost::phoenix which is good because the
function is resolved correctly most of the time. but the real function
(e.g. "std::cos") has no information about the namespace, which is
good but I guess sometimes we need need to be specific of the
namespace of the function we are trying to register. But adding the
namespace will break the macro. One alternative is to add a second
parameter to the macro so
BOOST_PHOENIX_REGISTER_ENDOMORPHISM(std::cos, cos); //first the fully
qualified name, second the boost::phoenix
the other possibility is to try to define the phoenix function in the
same namespace as the original function.
The fact that a function with the same name can appear in different
namespaces and the fact that there no concept of namespace in phoenix
then it seems the last possibility is the only general one.

Thanks,
Alfredo


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net