
Hi list, Can we please put an end to this insane idiosyncrasy going on here lately? It doesn't lead to anywhere! Ok, we get it ... people want to write local functions in the usual way. Here is what I have to propose: Why not combine Boost.Local and Boost.Phoenix and keep everybody happy? If the macros provided by Boost.Local would emit some instance of a phoenix::function. It would be great if this could be done completely polymorphic, maybe some tricks with boost::any? That way we gain these advantages: 1) We can get rid of the need to capture variables in scope by the macro. 2) People who don't want to put their functions in global/namespace/class scope and are afraid of writing a phoenix expression don't have to 3) The resulting lazy function will be composable in the usual phoenix way and will be far more useful! 4) We can put an end to this useless Phoenix vs. Local discussion In peace, Thomas

On Nov 24, 2011, at 1:14 PM, Thomas Heller wrote:
Here is what I have to propose: Why not combine Boost.Local and Boost.Phoenix and keep everybody happy? If the macros provided by Boost.Local would emit some instance of a phoenix::function. It would be great if this could be done completely polymorphic, maybe some tricks with boost::any?
How would you do this without putting a member template into a local class?
In peace
+1 Cheers, Gordon

Can we please put an end to this insane idiosyncrasy going on here lately? It doesn't lead to anywhere!
I agree, why can't people simply agree to disagree? There's apparently two way to solve this: with TMP or without. One camp just refuses to use TMP-heavy code because they don't want to deal with the burden of the compiler errors. For them, Boost.Local would be great. The other camp think TMP-heavy code is fine, that learning how to deal with the errors isn't that hard. For them, Boost.Local just sounds like a bad idea (for all the reasons already mentionned). When you put yourself in the schoes of each of the camps, they're both "right". Please stop being so stubburn about refusing to admit people might have different needs/requirements. C++ being so flexible means people are free to use it however they want. Now, I think way enough points were made in both camps for the moderators/review manager to be able to decide something. The real question is more like "do we want overlapping libraries?" or "do we want to support people that dislike TMP-heavy code when it's possible to provide an alternative?". My 0.02$, Philippe

On 11/24/2011 07:14 PM, Thomas Heller wrote:
Hi list,
Can we please put an end to this insane idiosyncrasy going on here lately? It doesn't lead to anywhere!
Ok, we get it ... people want to write local functions in the usual way.
Here is what I have to propose: Why not combine Boost.Local and Boost.Phoenix and keep everybody happy? If the macros provided by Boost.Local would emit some instance of a phoenix::function.
The macros provided by Boost.Local already generate PFOs. The goal of Phoenix should be to integrate seamlessly with PFOs, which are ubiquitous in C++. It shouldn't be required to make every type be a phoenix type to use it with phoenix. That is, currently, the main problem with phoenix: you need to adapt all functions before you can use them. I've already proposed the addition of a function named "lazy" (I think I suggested it during both the v2 and v3 reviews), that would be trivial to write, and that simply turns a PFO into a phoenix actor. That little thing would make Phoenix much more usable. Instead of BOOST_PHOENIX_ADAPT_FUNCTION(R, f_, f, N) /* ... */ void g() { /* ... */ my_higher_order_algorithm( f_(arg2, arg1) + arg3 ) } We could just write my_higher_order_algorithm( lazy(f)(arg2, arg1) + arg3 )

On 11/25/2011 01:36 PM, Mathias Gaunard wrote:
On 11/24/2011 07:14 PM, Thomas Heller wrote:
Hi list,
Can we please put an end to this insane idiosyncrasy going on here lately? It doesn't lead to anywhere!
Ok, we get it ... people want to write local functions in the usual way.
Here is what I have to propose: Why not combine Boost.Local and Boost.Phoenix and keep everybody happy? If the macros provided by Boost.Local would emit some instance of a phoenix::function.
The macros provided by Boost.Local already generate PFOs. The goal of Phoenix should be to integrate seamlessly with PFOs, which are ubiquitous in C++.
It shouldn't be required to make every type be a phoenix type to use it with phoenix. That is, currently, the main problem with phoenix: you need to adapt all functions before you can use them.
I've already proposed the addition of a function named "lazy" (I think I suggested it during both the v2 and v3 reviews), that would be trivial to write, and that simply turns a PFO into a phoenix actor. That little thing would make Phoenix much more usable.
Instead of
BOOST_PHOENIX_ADAPT_FUNCTION(R, f_, f, N)
/* ... */ void g() { /* ... */ my_higher_order_algorithm( f_(arg2, arg1) + arg3 ) }
We could just write
my_higher_order_algorithm( lazy(f)(arg2, arg1) + arg3 )
This functionality already exists. It's called bind. What is bind missing?

On Fri, Nov 25, 2011 at 11:37 AM, Thomas Heller <thom.heller@googlemail.com>wrote:
On 11/25/2011 01:36 PM, Mathias Gaunard wrote:
On 11/24/2011 07:14 PM, Thomas Heller wrote:
Hi list,
Can we please put an end to this insane idiosyncrasy going on here lately? It doesn't lead to anywhere!
Ok, we get it ... people want to write local functions in the usual way.
Here is what I have to propose: Why not combine Boost.Local and Boost.Phoenix and keep everybody happy? If the macros provided by Boost.Local would emit some instance of a phoenix::function.
The macros provided by Boost.Local already generate PFOs. The goal of Phoenix should be to integrate seamlessly with PFOs, which are ubiquitous in C++.
It shouldn't be required to make every type be a phoenix type to use it with phoenix. That is, currently, the main problem with phoenix: you need to adapt all functions before you can use them.
I've already proposed the addition of a function named "lazy" (I think I suggested it during both the v2 and v3 reviews), that would be trivial to write, and that simply turns a PFO into a phoenix actor. That little thing would make Phoenix much more usable.
Instead of
BOOST_PHOENIX_ADAPT_FUNCTION(**R, f_, f, N)
/* ... */ void g() { /* ... */ my_higher_order_algorithm( f_(arg2, arg1) + arg3 ) }
We could just write
my_higher_order_algorithm( lazy(f)(arg2, arg1) + arg3 )
This functionality already exists. It's called bind. What is bind missing?
I would think lazy(f) would be polyary (I'm probably making up this word), while bind(f, _1, _2 /*, etc.*/) would have a fixed arity. Hence, I would consider lazy(f) to be more flexible. - Jeff

On 11/25/2011 08:37 PM, Thomas Heller wrote:
On 11/25/2011 01:36 PM, Mathias Gaunard wrote:
Instead of
BOOST_PHOENIX_ADAPT_FUNCTION(R, f_, f, N)
/* ... */ void g() { /* ... */ my_higher_order_algorithm( f_(arg2, arg1) + arg3 ) }
We could just write
my_higher_order_algorithm( lazy(f)(arg2, arg1) + arg3 )
This functionality already exists. It's called bind. What is bind missing?
You cannot write bind(f) like this. You must write bind(f, arg1, arg2) Also there is no complex machinery to involve here. It's just a function that constructors a phoenix::function initialized from a PFO and returns it.

On 11/25/2011 01:56 PM, Mathias Gaunard wrote:
On 11/25/2011 08:37 PM, Thomas Heller wrote:
On 11/25/2011 01:36 PM, Mathias Gaunard wrote:
Instead of
BOOST_PHOENIX_ADAPT_FUNCTION(R, f_, f, N)
/* ... */ void g() { /* ... */ my_higher_order_algorithm( f_(arg2, arg1) + arg3 ) }
We could just write
my_higher_order_algorithm( lazy(f)(arg2, arg1) + arg3 )
This functionality already exists. It's called bind. What is bind missing?
You cannot write bind(f) like this. You must write bind(f, arg1, arg2)
Correct.
Also there is no complex machinery to involve here. It's just a function that constructors a phoenix::function initialized from a PFO and returns it.
Ok, so why would I want to do it? What's wrong with binding the PFO? If you want to lazily call the PFO directly: bind(f, ...); If you want to capture the PFO for reuse: let(_f = lambda[bind(f, ...)])[_f, ..., _f] or: let(_f = f)[bind(_f, ...), ..., bind(_f, ...)] So, what do i miss here?

Thomas Heller wrote:
Can we please put an end to this insane idiosyncrasy going on here lately? It doesn't lead to anywhere!
Ok, we get it ... people want to write local functions in the usual way.
Here is what I have to propose: Why not combine Boost.Local and Boost.Phoenix and keep everybody happy? [...] 3) The resulting lazy function will be composable in the usual phoenix way and will be far more useful!
+1 for as much compatibility as reasonably possible between Boost.Local and Boost.Phoenix. I don't know what this exactly means in detail. Perhaps the functor generated by Boost.Local must just follow the result_of protocol. However, it's probably more complicated than that. Regards, Thomas

On Thu, Nov 24, 2011 at 1:14 PM, Thomas Heller <thom.heller@googlemail.com> wrote:
Hi list,
Can we please put an end to this insane idiosyncrasy going on here lately? It doesn't lead to anywhere!
Sorry, I didn't reply sooner to this thread but I needed to study Phoenix more before I could reply (and I've been pretty busy with the review replies in the last week :) ). Please note that there's still a lot I need to learn about Phoenix so help me here (if I knew that Phoenix/Local was going to be such a big topic, I would have studied Phoenix _before_ the review but from the discussion we had ~1 year ago I incorrectly concluded this issue was settled).
Ok, we get it ... people want to write local functions in the usual way.
Here is what I have to propose: Why not combine Boost.Local and Boost.Phoenix and keep everybody happy? If the macros provided by Boost.Local would emit some instance of a phoenix::function.
With the following, you can create a Phoenix function from a local function simply by doing: // localfunc defined at local scope... boost::phoenix::function<BOOST_LOCAL_TYPEOF(localfunc)> phoenixfunc = localfunc; // (1) For example: #include <vector> #include <algorithm> #include <iostream> #include <boost/local/function.hpp> #include <boost/phoenix/core.hpp> #include <boost/phoenix/function.hpp> // TODO: Is there a better way to do this? Ideally, I'd want the result type to // be the X template parameter without introducing the extra R... template<typename R> struct global_add { typedef R result_type; template<typename X, typename Offset> R operator()(X x, Offset offset) const { X total = x + offset; std::cout << total << std::endl; return total; } }; int main() { using boost::phoenix::arg_names::arg1; std::vector<int> v(3); v[0] = 1; v[1] = 2; v[2] = 3; int offset = 10; // Phoenix function defined globally: boost::phoenix::function< global_add<int> > pg_add; std::for_each(v.begin(), v.end(), pg_add(arg1, offset)); // Phoenix function defined locally: int BOOST_LOCAL_FUNCTION_PARAMS(int x, bind offset) { int total = x + offset; std::cout << total << std::endl; return total; } BOOST_LOCAL_FUNCTION_NAME(local_add) boost::phoenix::function<BOOST_LOCAL_TYPEOF(local_add)> pl_add = local_add; std::for_each(v.begin(), v.end(), pl_add(arg1)); return 0; } Implementation: This only required me to typedef result_type into the functor used by local (expanded by PARAMS) and to do another typdef at local scope to expose the type for BOOST_LOCAL_TYPE(local_function_name) (expanded by NAME). If users need Phoenix function benefits (lazy evaluation, etc) but they want to define the Phoenix function locally and using statement syntax, then they create the Phoenix function from the local function. If they don't need the Phoenix function benefits, then they just use the local function. This way, the relationship between Local and Phoenix is very clear, I add the needed typedefs to the Local implementation, line (1) is added to the Phoenix docs, and that's all :)) Would this be useful?
It would be great if this could be done completely polymorphic, maybe some tricks with boost::any?
Note that in the example above the Phoenix global function is polymorphic (uses the template parameter X, even if I couldn't get around also using R (I didn't try hard)...) while the Phoenix local function is not (uses int). Of course a local function can use boost::any: #include <boost/local/function.hpp> #include <boost/any.hpp> #include <vector> #include <string> int main () { std::vector<boost::any> values; void BOOST_LOCAL_FUNCTION_PARAMS(boost::any const& value, bind& values) { values.push_back(value); } BOOST_LOCAL_FUNCTION_NAME(append) std::vector<int> i(3); i[0] = 1; i[1] = 2; i[2] = 3; std::for_each(i.begin(), i.end(), append); std::vector<std::string> s(3); s[0] = "abc"; s[1] = "uvw"; s[2] = "xyz"; std::for_each(s.begin(), s.end(), append); return 0; } I don't see a way to improve this (i.e., going from the generic boost::any type to a real polymorphic type) without template parameters within local types :( What do you think?
That way we gain these advantages: 1) We can get rid of the need to capture variables in scope by the macro. 2) People who don't want to put their functions in global/namespace/class scope and are afraid of writing a phoenix expression don't have to 3) The resulting lazy function will be composable in the usual phoenix way and will be far more useful! 4) We can put an end to this useless Phoenix vs. Local discussion
Thanks in advance. --Lorenzo

On Sat, Nov 26, 2011 at 7:28 AM, Lorenzo Caminiti <lorcaminiti@gmail.com> wrote:
// TODO: Is there a better way to do this? Ideally, I'd want the result type to // be the X template parameter without introducing the extra R... template<typename R> struct global_add { typedef R result_type;
template<typename X, typename Offset> R operator()(X x, Offset offset) const { X total = x + offset; std::cout << total << std::endl; return total; } };
I found out that the Phoenix docs show a better way to do this without the extra template parameter R: http://www.boost.org/doc/libs/1_48_0/libs/phoenix/example/factorial.cpp struct factorial_impl { template <typename Sig> struct result; template<typename This, typename Arg> struct result<This(Arg)> : result<This(Arg const &)> {}; template<typename This, typename Arg> struct result<This(Arg &)> { typedef Arg type; }; template <typename Arg> Arg operator()(Arg const& n) const { return (n <= 0) ? 1 : n * (*this)(n-1); } }; BTW, I have experimented a bit and I _think_ macros like the ones below can be written to automatically generate the code above: // some global scope template<typename Arg1> Arg1 BOOST_PHOENIX_FUNCTION_TPL(Arg1 const& n) { return (n <= 0) ? 1 : n * (*this)(n - 1); } BOOST_PHOENIX_FUNCTION_END(factorial_impl); These macros can only be used at non-local scope and the resulting functor is of course polymorphic. Would this be useful? I think Thomas tried something similar before: On Sun, Nov 20, 2011 at 11:16 PM, Thomas Heller <thom.heller@googlemail.com> wrote:
TBH, I considered writing macros to reduce the boilerplate of creating such construct for Phoenix. I decided to stop investigating that because at the end of the day, the macros grew in complexity and actually writing the boilerplate yourself turned out to be clearer, shorter and more flexible.
(The non-macro approach will always remain more flexible because users are in full control-- they can add state to the struct (being careful about expensive copies), etc.) For the moment the BOOST_PHOENIX_FUNCTION_TPL macro I was able to draft requires the template parameters to use the predefined names Arg1, Arg2, ..., ArgN plus it assumes that the number of template parameters equals the arity of the tuple specified to the macro unless you do BOOST_PHOENIX_FUNCTION_TPL(1, Arg1 x, Arg1 y) which specifies there's only 1 template parameter Arg1 even if there are 2 arguments x and y. If there was interest in these macros, I could try to relax these assumptions. --Lorenzo

On Sat, Nov 26, 2011 at 7:28 AM, Lorenzo Caminiti <lorcaminiti@gmail.com> wrote:
On Thu, Nov 24, 2011 at 1:14 PM, Thomas Heller <thom.heller@googlemail.com> wrote:
Here is what I have to propose: Why not combine Boost.Local and Boost.Phoenix and keep everybody happy?
Hello Thomas, Joel, Hartmut, et alt. Sorry for asking again... I know we are all trying to get disintoxicated from the last week's discussions :)
int main() { using boost::phoenix::arg_names::arg1;
std::vector<int> v(3); v[0] = 1; v[1] = 2; v[2] = 3; int offset = 10;
// Phoenix function defined locally: int BOOST_LOCAL_FUNCTION_PARAMS(int x, bind offset) { int total = x + offset; std::cout << total << std::endl; return total; } BOOST_LOCAL_FUNCTION_NAME(local_add)
boost::phoenix::function<BOOST_LOCAL_TYPEOF(local_add)> pl_add = local_add; std::for_each(v.begin(), v.end(), pl_add(arg1));
return 0; }
Would this ability to plug local functions into Phoenix be useful?
// some global scope template<typename Arg1> Arg1 BOOST_PHOENIX_FUNCTION_TPL(Arg1 const& n) { return (n <= 0) ? 1 : n * (*this)(n - 1); } BOOST_PHOENIX_FUNCTION_END(factorial_impl);
boost::phoenix::function<factorial_impl> f;
Is there any interest in the above macros to define Phoenix functions at global scope? Thanks a lot. --Lorenzo

On Thu, Dec 1, 2011 at 3:46 AM, Lorenzo Caminiti <lorcaminiti@gmail.com>wrote:
On Thu, Nov 24, 2011 at 1:14 PM, Thomas Heller <
On Sat, Nov 26, 2011 at 7:28 AM, Lorenzo Caminiti <lorcaminiti@gmail.com> wrote: thom.heller@googlemail.com> wrote:
Here is what I have to propose: Why not combine Boost.Local and Boost.Phoenix and keep everybody happy?
Hello Thomas, Joel, Hartmut, et alt. Sorry for asking again... I know we are all trying to get disintoxicated from the last week's discussions :)
int main() { using boost::phoenix::arg_names::arg1;
std::vector<int> v(3); v[0] = 1; v[1] = 2; v[2] = 3; int offset = 10;
// Phoenix function defined locally: int BOOST_LOCAL_FUNCTION_PARAMS(int x, bind offset) { int total = x + offset; std::cout << total << std::endl; return total; } BOOST_LOCAL_FUNCTION_NAME(local_add)
boost::phoenix::function<BOOST_LOCAL_TYPEOF(local_add)> pl_add = local_add; std::for_each(v.begin(), v.end(), pl_add(arg1));
return 0; }
Would this ability to plug local functions into Phoenix be useful?
// some global scope template<typename Arg1> Arg1 BOOST_PHOENIX_FUNCTION_TPL(Arg1 const& n) { return (n <= 0) ? 1 : n * (*this)(n - 1); } BOOST_PHOENIX_FUNCTION_END(factorial_impl);
boost::phoenix::function<factorial_impl> f;
Is there any interest in the above macros to define Phoenix functions at global scope?
I don't think this would be all that useful. If you're doing doing something simple enough with Phoenix that it could just be rolled into the local function, you should probably just do that (e.g., the "pl_add(arg1)" above might as well just be "local_add"). If it's more complicated than that, maybe you should just "suck it up" and define it out-of-line. I wouldn't think you would get much extra mileage from mixing local functions with Phoenix. - Jeff
participants (7)
-
Gordon Woodhull
-
Jeffrey Lee Hellrung, Jr.
-
Lorenzo Caminiti
-
Mathias Gaunard
-
Philippe Vaucher
-
Thomas Heller
-
Thomas Klimpel