Case study: Boost.Local versus Boost.Phoenix

Hey everyone, This e-mail is going to be a case study of my personal experience in converting code using Boost.Local to use Boost.Phoenix instead in order to get insight into the similarities and differences in what the libraries have to offer. I do not claim to have perfect understanding of Boost.Phoenix so I acknowledge that it is entirely possible that any negative experience I may have are due to my ignorance rather than a fault in the library itself. To give you some background of where I am coming from, I am a computational scientist working on a code that will let me prove some properties of a space of objects by exhaustive search. Fortunately it turns out that there are redundancies within this space so that I only need to search a subset of it. Thus, what I am working on now is a code which uses the Gecode library to generate solutions within this space that conform to constraints, where the constraints are chosen to only filter out redundant elements of the space. A significant part of this code is a suite of tests to ensure that my constraints work correctly, and it is this portion of the code that makes extensive use of Boost.Local and which I have attempted to convert to use Boost.Phoenix. First, it would have been helpful if I had known that Boost.Phoenix requires version 1.46 of Boost.Proto and Boost.Fusion, since I naively downloaded the sources to Boost.Phoenix into my local repository and discovered (after wading through pages of error messages) that it was missing a header. Since I have version 1.45 installed I concluded it must rely on sources from the trunk so I downloaded Boost.Proto, and then pages of error message later Boost.Fusion. (Incidentally, this contrasts with Boost.Local which had no such problems with installation.) Still, even after doing all of this I ran into a completely incomprehensible error message on my very first attempt, which was to convert the following snippet of code BOOST_LOCAL_FUNCTION( (void) (checkSolution)( (const StandardFormParameters&)(parameters) (const OperatorSpace&)(space) ) ) { checkRegion(Z,space.getOMatrix().slice( parameters.z_bit_diagonal_size ,space.number_of_qubits ,0u ,space.number_of_operators )); } BOOST_LOCAL_FUNCTION_END(checkSolution) forEachStandardFormSolution( number_of_qubits ,number_of_operators ,list_of(EveryColumnHasZ) ,checkSolution ); to forEachStandardFormSolution( number_of_qubits ,number_of_operators ,list_of(EveryColumnHasZ) ,phoenix::bind(checkRegion ,Z ,phoenix::bind(&IntMatrix::slice,phoenix::bind(&OperatorSpace::getOMatrix,arg2) ,phoenix::bind(&StandardFormParameters::z_bit_diagonal_size,arg1) ,phoenix::bind(&OperatorSpace::number_of_qubits,arg1) ,0u ,phoenix::bind(&OperatorSpace::number_of_operators,arg1) ) ) ); (The error message is attached in the file error-1.txt.) I was so overwhelmed that I almost gave up on the whole project, until it suddenly occurred to me that the last line error: no matching function for call to 'get_pointer(const CodeSearch::StandardFormParameters&)' was probably indicating a problem with the member function accessors. I then remembered that I could equivalently replace the above code with forEachStandardFormSolution( number_of_qubits ,number_of_operators ,list_of(EveryColumnHasZ) ,phoenix::bind(checkRegion ,Z ,phoenix::bind(&IntMatrix::slice,phoenix::bind(&OperatorSpace::getOMatrix,arg2) ,phoenix::bind(&StandardFormParameters::z_bit_diagonal_size,arg1) ,number_of_qubits ,0u ,number_of_operators ) ) ); (due to the context in which it appears) and when I did so it compiled just fine. Scratching my head for a moment, I finally realized that the problem was that in my original code I used "arg1" for the last two binds rather than "arg2". For such a simple, easy mistake, the error message is incredibly intimidating. In this example it is hard to say which of the two above versions I prefer. The last version is definitely more compact and feels more "functional" (which is a big plus for me), but it also has a lot of noise. Also, while someone unfamiliar with Boost.Local could look at the first snippet of code and see immediately what it does, I think that even someone who was familiar with Boost.Phoenix would probably need to stare at the second snippet of code for a few moments in order to figure out exactly what it is doing. So while converting to Boost.Phoenix makes the code more compact and more functional, it does also add some obfuscation. I converted a few more snippets of code similar to the above, and then attempted to convert the following from BOOST_LOCAL_FUNCTION( (void) (checkSolution)( (const StandardFormParameters&)(parameters) (const OperatorSpace&)(space) (const bind)((number_of_qubits)(number_of_operators)) ) ) { const unsigned int x_bit_diagonal_size = parameters.x_bit_diagonal_size , z_bit_diagonal_size = parameters.z_bit_diagonal_size ; checkCorrectOrdering( concatenateBoolMatricesVertically( list_of(space.getZMatrix().slice(x_bit_diagonal_size,number_of_qubits,0u,number_of_operators)) (space.getXMatrix().slice(x_bit_diagonal_size,number_of_qubits,0u,x_bit_diagonal_size)) ) ); checkCorrectOrdering( concatenateBoolMatricesVertically( list_of(space.getZMatrix().slice(x_bit_diagonal_size,z_bit_diagonal_size,0u,z_bit_diagonal_size)) (space.getZMatrix().slice(x_bit_diagonal_size,z_bit_diagonal_size,x_bit_diagonal_size,number_of_operators)) ) ); } BOOST_LOCAL_FUNCTION_END(checkSolution) forEachStandardFormSolution( number_of_qubits ,number_of_operators ,column_ordering_only_constraints ,checkSolution ); to forEachStandardFormSolution( number_of_qubits ,number_of_operators ,column_ordering_only_constraints ,phoenix::let (x_bit_diagonal_size = phoenix::bind(&StandardFormParameters::x_bit_diagonal_size,arg1) ,z_bit_diagonal_size = phoenix::bind(&StandardFormParameters::z_bit_diagonal_size,arg1) ,X_matrix = phoenix::bind(&OperatorSpace::getXMatrix,arg2) ,Z_matrix = phoenix::bind(&OperatorSpace::getZMatrix,arg2) ) [phoenix::bind(checkCorrectOrdering ,phoenix::bind(concatenateBoolMatricesVertically ,phoenix::bind( phoenix::bind(list_of ,phoenix::bind(&BoolMatrix::slice,Z_matrix,x_bit_diagonal_size,number_of_qubits,0u,number_of_operators) ) ,phoenix::bind(&BoolMatrix::slice,X_matrix,x_bit_diagonal_size,number_of_qubits,0u,x_bit_diagonal_size) ) ) ) ,phoenix::bind(checkCorrectOrdering ,phoenix::bind(concatenateBoolMatricesVertically ,phoenix::bind( phoenix::bind(list_of ,phoenix::bind(&BoolMatrix::slice,Z_matrix,x_bit_diagonal_size,z_bit_diagonal_size,0u,z_bit_diagonal_size) ) ,phoenix::bind(&BoolMatrix::slice,x_bit_diagonal_size,z_bit_diagonal_size,x_bit_diagonal_size,number_of_operators) ) ) ) ] ); Again I got pages of error messages (included in error-2.txt), and this time I gave up. Even if I could make this compile, the new version is definitely much more obfuscated than the old version. The main reason for this is that the phoenix::bind syntax requires lots of boilerplace when you have chained function calls, i.e. for expressions like A.B().C().D() or f()()()(). In fact, you can see how in the old version I called "space.getZMatrix()" several times (construction is cheap, and this is only a test) rather than creating a local variable Z_matrix to cache the result because I thought it looked nicer, whereas in the second version I cached it in a local variable because to do otherwise would have introduced lots of extra line noise. Also, it is worth mentioning in the documentation that you can't use just any variable names in a "let" block, and that to use the names _a-_z you need to import the namespace boost::phoenix::local_names. I know that this is mentioned in the previous section in the manual, but I had been expecting that all of the information I would need to use "let" would be in the "let" section so I jumped directly there. Having said that, I really do appreciate that one can declare one's own local variable names because otherwise the code above would have been *really* obfuscated. The documentation could be made more clear, though. My first attempt was to put the following lines inside the function just before I needed them: struct x_bit_diagonal_size_key; phoenix::expression::local_variable<x_bit_diagonal_size_key>::type x_bit_diagonal_size; struct z_bit_diagonal_size_key; phoenix::expression::local_variable<z_bit_diagonal_size_key>::type z_bit_diagonal_size; struct X_matrix_key; phoenix::expression::local_variable<X_matrix_key>::type X_matrix; struct Z_matrix_key; phoenix::expression::local_variable<Z_matrix_key>::type Z_matrix; This produced the cryptic error message in error-3.txt. At first I thought that the problem was that I needed curly brackets, but this didn't fix it, so I moved them outside the function, which worked. This was better than nothing, but it was a shame since I had wanted everything that was local to the function (that is, the outer function containing these nested functions) to be inside the (outer) function itself. Also, when I removed the curly brackets (since they were absent in the documentation) I got the even more horrific error message shown in error-4.txt. I then moved on to the following snippet of code: BOOST_LOCAL_FUNCTION( (void) (checkOMatrix)( (const IntMatrix&)(matrix) ) ) { vector<unsigned int> weights; BOOST_FOREACH(const unsigned int row, irange(0u,(unsigned int)matrix.height())) { unsigned int weight = 0; BOOST_FOREACH(const unsigned int col, irange(0u,(unsigned int)matrix.width())) { if(matrix(col,row).val() > 0) ++weight; } weights.push_back(weight); } if(!is_sorted(weights | reversed)) { ostringstream message; message << "Bad weight order:"; for_each(weights,lambda::var(message) << " " << lambda::_1); FATALLY_FAIL(message.str()); } } BOOST_LOCAL_FUNCTION_END(checkOMatrix) forEachOMatrixSolution( number_of_qubits ,number_of_operators ,bind(postWeightRowOrderingConstraintOnRegion,_1,4,_2,BoolVarArgs()) ,checkOMatrix ); I endeavored to translate this into Boost.Phoenix, but it was a bit painful and when I was almost done I realized that there was a macro (FATALLY_FAIL) at the end that probably would not expand into anything that Boost.Phoenix would recognize, so I gave up. At this point I was getting frustrated, and it looked like most of the code would likewise be so painful to translate that I couldn't bring myself to do it for the sake of science. I did, however, stumble on the following code: BOOST_LOCAL_FUNCTION( (void) (checkAllSolutions)( (auto_ptr<OperatorSpace>)(initial_space) (const unsigned int)(start_column) (const unsigned int)(end_column) (const unsigned int)(start_row) (const unsigned int)(end_row) (const bind)((checkSolution)) ) ) { for_each( generateSolutionsFor(initial_space) , bind(checkSolution , _1 , start_column , end_column , start_row , end_row ) ); } BOOST_LOCAL_FUNCTION_END(checkAllSolutions) forEachConstrainedRegion( number_of_qubits , number_of_operators , postConstraint , checkAllSolutions ); If there were ever a case where I wanted something like Phoenix, this was surely it! It was a serious waste to have to write all of that boilerplate code just because I couldn't figure out how to accomplish the same goal using Boost.Bind or Boost.Lambda. So I rewrote it as the following: forEachConstrainedRegion( number_of_qubits , number_of_operators , postConstraint , phoenix::for_each( phoenix::bind(generateSolutionsFor,arg1) ,phoenix::lambda(_a=arg2,_b=arg3,_c=arg4,_d=arg5) [phoenix::bind(checkSolution,arg1,_a,_b,_c,_d)] ) ); Wow, look at how beautiful that is! This is *exactly* the kind of code that I wanted to write, and Phoenix has finally let me write it that way. There's just a little problem... compiling it produces a 7,061 line error message (attached in error-5.txt). Perhaps I just made a simple mistake, but since there was no indication about what I got wrong I simply gave up. Feel free to let me know if you spot my mistake; for your information, my prelude included the lines #include "boost/phoenix/bind.hpp" #include "boost/phoenix/core.hpp" #include "boost/phoenix/scope.hpp" #include "boost/phoenix/stl/algorithm/iteration.hpp" namespace arg_names = boost::phoenix::arg_names; namespace local_names = boost::phoenix::local_names; using local_names::_a; using local_names::_b; using local_names::_c; using local_names::_d; using arg_names::arg1; using arg_names::arg2; using arg_names::arg3; using arg_names::arg4; using arg_names::arg5; so it could be that I made a mistake somewhere in there. In conclusion, I have found that Boost.Phoenix is simply too painful to use in practice for most cases where I have been using Boost.Local. Although it could potentially allow for very elegant code in many cases, it is so hard to figure out what you are doing wrong that it seems to be more trouble than it is worth. I am actually a little sad at having arrived at this conclusion, because the library looked incredibly cool and I was very excited about trying it out, and now I am just walking away from the whole experience feeling incredibly frustrated. Furthermore, even if I were an expert in it I have trouble seeing how in most of the places in my code it would result in code that was either more clear or easier to write. The Boost.Local code has extra noise at the beginning, but when the main body of the nested function contains lots of calls it is far more expressive to write the C++ code directly than to use lots of pheonix::bind functions to accomplish the same thing. This doesn't mean that I think that Boost.Phoenix is a bad library. Reading through the documentation I am absolutely amazed at how it can be used to create very expressive functions; the authors have clearly worked very hard on it and should be proud of their work. However, it simply cannot be treated as invaliding the need for something like Boost.Local, because for one to accomplish many of the same tasks in Boost.Phoenix as one can accomplish in Boost.Local one has to deal with a whole lot of extra mental effort and frustration, and the result at the end is often less expressive and clear (and potentially less maintainable) as it would have been if one had used Boost.Local since the body is no longer expressed in standard C++. I hope that you all find this informative! Cheers, Greg PS: The error-*.txt files are zipped up in error-messages.zip, since unzipped they were ~ 900k which caused this message to be rejected.

On 2/3/2011 6:36 PM, Gregory Crosswhite wrote:
Hey everyone,
This e-mail is going to be a case study of my personal experience in converting code using Boost.Local to use Boost.Phoenix instead in order to get insight into the similarities and differences in what the libraries have to offer. I do not claim to have perfect understanding of Boost.Phoenix so I acknowledge that it is entirely possible that any negative experience I may have are due to my ignorance rather than a fault in the library itself. [...many attempted examples...] This doesn't mean that I think that Boost.Phoenix is a bad library. Reading through the documentation I am absolutely amazed at how it can be used to create very expressive functions; the authors have clearly worked very hard on it and should be proud of their work. However, it simply cannot be treated as invaliding the need for something like Boost.Local, because for one to accomplish many of the same tasks in Boost.Phoenix as one can accomplish in Boost.Local one has to deal with a whole lot of extra mental effort and frustration, and the result at the end is often less expressive and clear (and potentially less maintainable) as it would have been if one had used Boost.Local since the body is no longer expressed in standard C++.
I hope that you all find this informative!
Very informative, yes. - Jeff

On 2/4/2011 10:36 AM, Gregory Crosswhite wrote:
In conclusion, I have found that Boost.Phoenix is simply too painful to use in practice for most cases where I have been using Boost.Local. Although it could potentially allow for very elegant code in many cases, it is so hard to figure out what you are doing wrong that it seems to be more trouble than it is worth. I am actually a little sad at having arrived at this conclusion, because the library looked incredibly cool and I was very excited about trying it out, and now I am just walking away from the whole experience feeling incredibly frustrated. Furthermore, even if I were an expert in it I have trouble seeing how in most of the places in my code it would result in code that was either more clear or easier to write. The Boost.Local code has extra noise at the beginning, but when the main body of the nested function contains lots of calls it is far more expressive to write the C++ code directly than to use lots of pheonix::bind functions to accomplish the same thing.
This doesn't mean that I think that Boost.Phoenix is a bad library. Reading through the documentation I am absolutely amazed at how it can be used to create very expressive functions; the authors have clearly worked very hard on it and should be proud of their work. However, it simply cannot be treated as invaliding the need for something like Boost.Local, because for one to accomplish many of the same tasks in Boost.Phoenix as one can accomplish in Boost.Local one has to deal with a whole lot of extra mental effort and frustration, and the result at the end is often less expressive and clear (and potentially less maintainable) as it would have been if one had used Boost.Local since the body is no longer expressed in standard C++.
You've just scratched the surface, and IMO, learned about the library in a way that I would not advice. To be honest, I never liked bind -- not a bit. If I did it my way, I'd write small modular phoenix functions just like you would in C++ or any FP language, but are fully curryable and lazy. It's very similar to what Local provides, albeit in C++ syntax instead of macros. By doing so, you avoid overly complex phoenix expressions and work closer to C++ but at the same time get all the benefits of FP. As I hinted, it is also possible to provide Local like macros to make the code even more concise. Right now, admittedly, phoenix function is more verbose, but it does a lot more and is more flexible. Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com

On 2/3/11 8:04 PM, Joel de Guzman wrote:
You've just scratched the surface, and IMO, learned about the library in a way that I would not advice. To be honest, I never liked bind -- not a bit. If I did it my way, I'd write small modular phoenix functions just like you would in C++ or any FP language, but are fully curryable and lazy. It's very similar to what Local provides, albeit in C++ syntax instead of macros. By doing so, you avoid overly complex phoenix expressions and work closer to C++ but at the same time get all the benefits of FP.
As I said in my e-mail, I am sure the Phoenix is a wonderful tool after you have taken a lot of time to learn how to use it, and hopefully I will get the chance to become proficient at it myself one day. However, not all of us want to go through a lot of learning effort just to be able to conveniently create local functions. What is nice about Boost.Local is that you only have to learn about a couple of relatively simple concepts that have no dependence at all on the code that you want to embed in the function, whereas for Phoenix you are limited in what code you can include in the function until you have a great deal of mastery over the library, and the error message will give you no help at all in figuring out where you made a mistake so you need to have a lot of patience. Let me put this another way. Imagine that a new user comes along and thinks, "Gee, I have this snippet of code that I'd like to pass to another function inside a closure; does Boost have a library that makes this easier for me than writing lots of boilerplate to define a function object class?" If you say, "Sure! Check out the Phoenix library!" then they will likely have an experience similar to mine, conclude that the whole thing is more trouble than its worth, and go back to writing function object classes. However, if you say, "Sure! Check out the Local library!" then they will be much more likely to immediately see how to use it to solve their problem and will adopt it after a relatively shallow learning curve. So to repeat my thesis: Phoenix is a great library, but unless something significantly changes then I would strongly not recommend that it be viewed as making the existence of Local redundant. If Local is not included in Boost because theoretically Phoenix allows you to accomplish the same thing then this won't cause the users who need it to all learn how to use the theoretically nicer tool so much as it will cause many of them to use no tool from Boost at all. Local should be viewed as being complimentary to Phoenix rather competing with it, and it should be evaluated on its own merits. Cheers, Greg

On 2/4/2011 12:29 PM, Gregory Crosswhite wrote:
Local should be viewed as being complimentary to Phoenix rather competing with it, and it should be evaluated on its own merits.
I think I have to agree with this. In as much as we have Boost-FOREACH in addition to std::for_each, Boost.Local is a good and useful library that is simple, practical and effective enough as it is. Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com

On Thu, Feb 3, 2011 at 11:48 PM, Joel de Guzman <joel@boost-consulting.com> wrote:
On 2/4/2011 12:29 PM, Gregory Crosswhite wrote:
Local should be viewed as being complimentary to Phoenix rather competing with it, and it should be evaluated on its own merits.
I think I have to agree with this. In as much as we have Boost-FOREACH in addition to std::for_each, Boost.Local is a good and useful library that is simple, practical and effective enough as it is. http://lists.boost.org/mailman/listinfo.cgi/boost
I also honestly think that Boost.Local and Boost.Phoenix cover different application domains. I can spell that out in the library docs if Boosters come to the same conclusion. I compared Boost.Local with Boost.Phoenix just because they can both be used to write functor objects (like many other libraries can) but I would chose to use one over the other depending on the my specific application needs (e.g., can I accept a non normal C++ syntax for the body? do I need the extra functional programming power offered by Phoenix? etc). -- Lorenzo

Joel de Guzman <joel <at> boost-consulting.com> writes:
On 2/4/2011 10:36 AM, Gregory Crosswhite wrote:
In conclusion, I have found that Boost.Phoenix is simply too painful to use in practice for most cases where I have been using Boost.Local. Although it could potentially allow for very elegant code in many cases, it is so hard to figure out what you are doing wrong that it seems to be more trouble than it is worth. I am actually a little sad at having arrived at this conclusion, because the library looked incredibly cool and I was very excited about trying it out, and now I am just walking away from the whole experience feeling incredibly frustrated. Furthermore, even if I were an expert in it I have trouble seeing how in most of the places in my code it would result in code that was either more clear or easier to write. The Boost.Local code has extra noise at the beginning, but when the main body of the nested function contains lots of calls it is far more expressive to write the C++ code directly than to use lots of pheonix::bind functions to accomplish the same thing.
This doesn't mean that I think that Boost.Phoenix is a bad library. Reading through the documentation I am absolutely amazed at how it can be used to create very expressive functions; the authors have clearly worked very hard on it and should be proud of their work. However, it simply cannot be treated as invaliding the need for something like Boost.Local, because for one to accomplish many of the same tasks in Boost.Phoenix as one can accomplish in Boost.Local one has to deal with a whole lot of extra mental effort and frustration, and the result at the end is often less expressive and clear (and potentially less maintainable) as it would have been if one had used Boost.Local since the body is no longer expressed in standard C++.
I think what you have shown is a prime example of the clash of two programming paradigms. It became very clear that the examples you've shown just didn't fit in well in the Phoenix programming model. However, Phoenix provides an interface for that as well, phoenix::function ... see Joel's comment. Additionally, I have to agree with you that the error message you experienced lead to a lot frustration. They probably dominated the overall impression you got of the library. These (overly verbose) error messages can be seen as a bug of the library! There are techniques to improve them, however none of these are implemented yet. Ironically, while developing the library, I needed the full exposure of the error messages to debug all the template code ;)
You've just scratched the surface, and IMO, learned about the library in a way that I would not advice. To be honest, I never liked bind -- not a bit. If I did it my way, I'd write small modular phoenix functions just like you would in C++ or any FP language, but are fully curryable and lazy. It's very similar to what Local provides, albeit in C++ syntax instead of macros. By doing so, you avoid overly complex phoenix expressions and work closer to C++ but at the same time get all the benefits of FP.
As I hinted, it is also possible to provide Local like macros to make the code even more concise. Right now, admittedly, phoenix function is more verbose, but it does a lot more and is more flexible.
George, thank you for the effort you put into trying out Phoenix! If it doesn't lead to too much trouble I would like to have a (selfcontained) testcase I can work on to analyze the problems you had and improve either the documentation or fix some bugs you might have run into. I think your experience shows that there is a need for a Troubleshooting section in the docs. Thanks, Thomas

On 04/02/2011 05:04, Joel de Guzman wrote:
You've just scratched the surface, and IMO, learned about the library in a way that I would not advice. To be honest, I never liked bind -- not a bit. If I did it my way, I'd write small modular phoenix functions just like you would in C++ or any FP language, but are fully curryable and lazy.
While Phoenix integrates very well with PFOs and operators, not so much with functions and structures. Forcing all C++ users to exclusively use PFOs is unrealistic; it also adds significant compile-time overhead compared to the use of template or overloaded functions. That notwithstanding, I'm still writing a source-to-source compiler where the target is C++ and all functions are PFOs, since that's required for functional programming in C++.

From: Gregory Crosswhite <gcross@phys.washington.edu>
Hey everyone,
This e-mail is going to be a case study of my personal experience in converting code using Boost.Local to use Boost.Phoenix instead in order to get insight into the similarities and differences in what the libraries have to offer.
[snip]
BOOST_LOCAL_FUNCTION( (void) (checkSolution)( (const StandardFormParameters&)(parameters) (const OperatorSpace&)(space) ) ) { checkRegion(Z,space.getOMatrix().slice( parameters.z_bit_diagonal_size ,space.number_of_qubits ,0u ,space.number_of_operators )); } BOOST_LOCAL_FUNCTION_END(checkSolution) forEachStandardFormSolution( number_of_qubits ,number_of_operators ,list_of(EveryColumnHasZ) ,checkSolution );
I'm sorry is it only me or it would be much more readable and maintainable to write: namespace { struct my_lambda { foo_type &foo; bar_type &bar my_lambda(foo_type &local_foo,bar_type &local_bar) : foo(local_foo), bar(local_bar) { } void operator()(a_type a) const { /// Your body goes there } }; } void my_function() { foo_type foo; bar_type bar; my_lambda lambda(foo,bar); for_each(as.begin(),as.end(),lambda); // or something else } I understand that Pheonix and boost::lambda are very useful in certain situations but am I only person who thinks that if you need some little bit more complicated lambda function you just create a small class with operator() which would be 1. Much More readable 2. Clear for source code maintainers who not familiar with some boost toolkits 3. Easier to debug and see compiler's error messages 4. Be friendlier to less experienced C++ programmers who would have to maintain this horrible code? Am I the only person there who thinks that such constructions are "write-only" and don't provide real benifit? I do like and use boost::bind, boost::lambda and some other simple construction that can be written in a single like and what is most important improve programmer's productivity but I really fail to see it in this case. I think that for complex cases it is much better to write a class with operator() (till we have C++0x) and make: - compiler happy and handle it fast - maintainer happy to be able to actually understand what does the code do without reading Boost.Pheonix or Boost.Local manuals for 5 hours. Artyom

On 02/04/2011 05:35 AM, Artyom wrote:
I'm sorry is it only me or it would be much more readable and maintainable to write:
<snip> No, you are certainly not the only one. Your code is much easier to understand. As someone who spends a good portion of time every week doing what amounts to "software archeology" (maintenance programming) on code written by very smart programmers with varying degrees of expertise and experience over the course of 20+ years, I completely agree.
Am I the only person there who thinks that such constructions are "write-only" and don't provide real benifit? Again, no. The points you make are spot on.
Rob

I'm sorry is it only me or it would be much more readable and maintainable to write:
namespace { struct my_lambda { foo_type &foo; bar_type &bar my_lambda(foo_type &local_foo,bar_type &local_bar) : foo(local_foo), bar(local_bar) { }
void operator()(a_type a) const { /// Your body goes there }
}; }
void my_function() { foo_type foo; bar_type bar;
my_lambda lambda(foo,bar); for_each(as.begin(),as.end(),lambda); // or something else }
It's not just you. I've made a habit (which have been widely adapted by others at my workplace) to make local lambdas like this: void my_function() { foo_type foo; bar_type bar; struct { foo_type &foo_; bar_type &bar_; void operator()(a_type a) const { foo_(a); bar_(a); } } lambda = { foo, bar} ; for_each(as.begin(),as.end(),lambda); } I don't know if that's standard compliant, but works perfectly fine in MSVC. I like the {} initializer, so I don't need to write a boilerplate constructor. - Christian

It's not just you. I've made a habit (which have been widely adapted by others at my workplace) to make local lambdas like this:
void my_function() { foo_type foo; bar_type bar;
struct { foo_type &foo_; bar_type &bar_; void operator()(a_type a) const { foo_(a); bar_(a); } } lambda = { foo, bar} ;
for_each(as.begin(),as.end(),lambda);
}
I don't know if that's standard compliant, but works perfectly fine in MSVC. I like the {} initializer, so I don't need to write a boilerplate constructor.
AFAIK it is not standard compliant and gcc does not accept this (with combination of for_each of other situations); But struct lambda_type { foo_type &foo_; bar_type &bar_; void operator()(a_type a) const { foo_(a); bar_(a); } }; void my_function() { foo_type foo; bar_type bar; lambda_type lambda = { foo, bar} ; for_each(as.begin(),as.end(),lambda); } Is more then fine and this is what I usually use.

Christian Holmquist <c.holmquist@gmail.com> writes:
I've made a habit (which have been widely adapted by others at my workplace) to make local lambdas like this:
void my_function() { foo_type foo; bar_type bar;
struct { foo_type &foo_; bar_type &bar_; void operator()(a_type a) const { foo_(a); bar_(a); } } lambda = { foo, bar} ;
for_each(as.begin(),as.end(),lambda);
}
I don't know if that's standard compliant, but works perfectly fine in MSVC.
It's not legal C++03, but is legal C++0x. Anthony -- Author of C++ Concurrency in Action http://www.stdthread.co.uk/book/ just::thread C++0x thread library http://www.stdthread.co.uk Just Software Solutions Ltd http://www.justsoftwaresolutions.co.uk 15 Carrallack Mews, St Just, Cornwall, TR19 7UL, UK. Company No. 5478976

On 04/02/2011 13:35, Artyom wrote:
I'm sorry is it only me or it would be much more readable and maintainable to write:
namespace { struct my_lambda { foo_type&foo; bar_type&bar my_lambda(foo_type&local_foo,bar_type&local_bar) : foo(local_foo), bar(local_bar) { }
void operator()(a_type a) const { /// Your body goes there }
}; }
void my_function() { foo_type foo; bar_type bar;
my_lambda lambda(foo,bar); for_each(as.begin(),as.end(),lambda); // or something else }
That's exactly the kind of thing Boost.Local does, it generates that structure but avoids you the boilerplate of declaring foo and bar members and forwarding them in the constructor. Also, it uses Boost.Typeof, so you don't have to write their type. Also, it is much more valuable to declare this as void my_function() { foo_type foo; bar_type bar; struct { foo_type &foo; bar_type &bar my_lambda(foo_type &local_foo,bar_type &local_bar) : foo(local_foo), bar(local_bar) { } void operator()(a_type a) const { /// Your body goes there } } lambda(foo, bar); for_each(as.begin(),as.end(),lambda); // or something else } since the lambda is right next to where it is used (which is the whole point of the exercise). It is true however that the syntax of the Boost.Local macro could greatly be simplified. I think LOCAL_FUNCTION(R, f, (a, b, c)(T0 a0, T1 a1), body) is a better syntax. (a, b, c) would catch a, b, and c in the scope and would be optional. (Of course, it requires the use of the C99 preprocessor)

On Fri, Feb 4, 2011 at 10:55 AM, Mathias Gaunard <mathias.gaunard@ens-lyon.org> wrote:
On 04/02/2011 13:35, Artyom wrote:
I'm sorry is it only me or it would be much more readable and maintainable to write:
namespace { struct my_lambda { foo_type&foo; bar_type&bar my_lambda(foo_type&local_foo,bar_type&local_bar) : foo(local_foo), bar(local_bar) { }
void operator()(a_type a) const { /// Your body goes there }
}; }
void my_function() { foo_type foo; bar_type bar;
my_lambda lambda(foo,bar); for_each(as.begin(),as.end(),lambda); // or something else }
That's exactly the kind of thing Boost.Local does, it generates that structure but avoids you the boilerplate of declaring foo and bar members and forwarding them in the constructor. Also, it uses Boost.Typeof, so you don't have to write their type.
Also, it is much more valuable to declare this as
void my_function() { foo_type foo; bar_type bar;
struct { foo_type &foo; bar_type &bar my_lambda(foo_type &local_foo,bar_type &local_bar) : foo(local_foo), bar(local_bar) { }
void operator()(a_type a) const { /// Your body goes there }
} lambda(foo, bar); for_each(as.begin(),as.end(),lambda); // or something else }
since the lambda is right next to where it is used (which is the whole point of the exercise).
It is true however that the syntax of the Boost.Local macro could greatly be simplified.
I think
LOCAL_FUNCTION(R, f, (a, b, c)(T0 a0, T1 a1), body)
is a better syntax. (a, b, c) would catch a, b, and c in the scope and would be optional.
(Of course, it requires the use of the C99 preprocessor)
Sorry, C++ preprocessor only -- that was a requirement for me... (Can you imagine how much the syntax can be simplified with varidiac macros... I can!! Too bad I can't use them...). I still think Boost.Local syntax is better that your macro above but that's just because I am used to it, I know. However, I would like people to try the parenthesized syntax for a couple of days before saying it's horrible because in my experience it looks horrible at first but then it's not too bad... I guess I would just be much more useful for me to know if people have _used_ the parenthesized syntax and they _concluded_ it's horrible (instead of knowing the obvious that the parenthesis _look_ confusing at first). -- Lorenzo

On 2/5/2011 4:04 PM, Lorenzo Caminiti wrote:
On Fri, Feb 4, 2011 at 10:55 AM, Mathias Gaunard <mathias.gaunard@ens-lyon.org> wrote:
On 04/02/2011 13:35, Artyom wrote:
I'm sorry is it only me or it would be much more readable and maintainable to write:
namespace { struct my_lambda { foo_type&foo; bar_type&bar my_lambda(foo_type&local_foo,bar_type&local_bar) : foo(local_foo), bar(local_bar) { }
void operator()(a_type a) const { /// Your body goes there }
}; }
void my_function() { foo_type foo; bar_type bar;
my_lambda lambda(foo,bar); for_each(as.begin(),as.end(),lambda); // or something else }
That's exactly the kind of thing Boost.Local does, it generates that structure but avoids you the boilerplate of declaring foo and bar members and forwarding them in the constructor. Also, it uses Boost.Typeof, so you don't have to write their type.
Also, it is much more valuable to declare this as
void my_function() { foo_type foo; bar_type bar;
struct { foo_type&foo; bar_type&bar my_lambda(foo_type&local_foo,bar_type&local_bar) : foo(local_foo), bar(local_bar) { }
void operator()(a_type a) const { /// Your body goes there }
} lambda(foo, bar); for_each(as.begin(),as.end(),lambda); // or something else }
since the lambda is right next to where it is used (which is the whole point of the exercise).
It is true however that the syntax of the Boost.Local macro could greatly be simplified.
I think
LOCAL_FUNCTION(R, f, (a, b, c)(T0 a0, T1 a1), body)
is a better syntax. (a, b, c) would catch a, b, and c in the scope and would be optional.
(Of course, it requires the use of the C99 preprocessor)
Sorry, C++ preprocessor only -- that was a requirement for me... (Can you imagine how much the syntax can be simplified with varidiac macros... I can!! Too bad I can't use them...).
For the purposes of Boost compatibility you can use variadic macros as long as BOOST_NO_VARIADIC_MACROS is not defined. You can also use my variadic_macro_data library in the sandbox to interoperate variadic macros with Boost PP and to simplify some Boost PP regarding tuples when variadic macros is supported. So you might be able to offer a clearer syntax for your macros for those compilers which do support variadic macros ( there are quite a few of them ) if you wanted to do so, even though it does not fit your own needs. But this is of course totally up to you. The downside to variadic macros, which Boost PP data types do not share, is that variadic macro notation can only be used as the last parameter of any given macro.

On Sun, Feb 6, 2011 at 8:24 AM, Edward Diener <eldiener@tropicsoft.com> wrote:
On 2/5/2011 4:04 PM, Lorenzo Caminiti wrote:
On Fri, Feb 4, 2011 at 10:55 AM, Mathias Gaunard <mathias.gaunard@ens-lyon.org> wrote:
On 04/02/2011 13:35, Artyom wrote:
I'm sorry is it only me or it would be much more readable and maintainable to write:
namespace { struct my_lambda { foo_type&foo; bar_type&bar my_lambda(foo_type&local_foo,bar_type&local_bar) : foo(local_foo), bar(local_bar) { }
void operator()(a_type a) const { /// Your body goes there }
}; }
void my_function() { foo_type foo; bar_type bar;
my_lambda lambda(foo,bar); for_each(as.begin(),as.end(),lambda); // or something else }
That's exactly the kind of thing Boost.Local does, it generates that structure but avoids you the boilerplate of declaring foo and bar members and forwarding them in the constructor. Also, it uses Boost.Typeof, so you don't have to write their type.
Also, it is much more valuable to declare this as
void my_function() { foo_type foo; bar_type bar;
struct { foo_type&foo; bar_type&bar my_lambda(foo_type&local_foo,bar_type&local_bar) : foo(local_foo), bar(local_bar) { }
void operator()(a_type a) const { /// Your body goes there }
} lambda(foo, bar); for_each(as.begin(),as.end(),lambda); // or something else }
since the lambda is right next to where it is used (which is the whole point of the exercise).
It is true however that the syntax of the Boost.Local macro could greatly be simplified.
I think
LOCAL_FUNCTION(R, f, (a, b, c)(T0 a0, T1 a1), body)
is a better syntax. (a, b, c) would catch a, b, and c in the scope and would be optional.
(Of course, it requires the use of the C99 preprocessor)
Sorry, C++ preprocessor only -- that was a requirement for me... (Can you imagine how much the syntax can be simplified with varidiac macros... I can!! Too bad I can't use them...).
For the purposes of Boost compatibility you can use variadic macros as long as BOOST_NO_VARIADIC_MACROS is not defined.
You can also use my variadic_macro_data library in the sandbox to interoperate variadic macros with Boost PP and to simplify some Boost PP regarding tuples when variadic macros is supported.
So you might be able to offer a clearer syntax for your macros for those compilers which do support variadic macros ( there are quite a few of them ) if you wanted to do so, even though it does not fit your own needs. But this is of course totally up to you.
The downside to variadic macros, which Boost PP data types do not share, is that variadic macro notation can only be used as the last parameter of any given macro.
On Sat, Feb 5, 2011 at 12:42 PM, Steven Watanabe <watanabesj@gmail.com> wrote:
AMDG
On 2/5/2011 9:26 AM, Lorenzo Caminiti wrote:
On Fri, Feb 4, 2011 at 5:03 PM, Alexander Nasonov<alnsn@yandex.ru> wrote:
Steven and I were playing with different syntaxes few years ago
We came up with something like this:
void BOOST_LOCAL_FUNCTION ( BOOST_BIND((factor)(&sum)), double num ) { sum += factor * num; std::clog<< "Summed: "<< sum<< std::endl; } BOOST_LOCAL_FUNCTION_DECL(add)
http://thread.gmane.org/gmane.comp.lib.boost.devel/168612/focus=168694
Yes, Alex that is exactly where I started and I cannot thank you enough for your Boost.ScopeExit work which has served as a solid basis for my Boost.Local development.
However: 1) How is this syntax is preferable to Boost.Local parenthesized syntax? To me, there is not much difference.*
The argument list is more readable, because it's closer to normal C++ syntax.
2) How would you pass multiple local function parameters (not just the one parameter `num`) using this syntax?
Black magic. I don't remember how, but I know we did figure this out.
3) Can you generate local function that are passed as template parameters using this syntax?
I think so. It wouldn't be very useful otherwise would it?
OK, I can take as a homework to look into how variadic macros and the Alex-Steven* macro syntax could be used to replace or improve the parenthesized syntax to declare local functions. However, I would be much more motivated to look into improving/changing the parenthesized syntax if people that have actually used the syntax report a bad experience with it -- instead than just because the parenthesis don't generate love at first sight :) (*) A _possible_ issue with Alex-Steven syntax is that it does not separate the parameter types from the parameter names. I cannot program the preprocessor to retrieve type and name separately from `double num` -- while I can from `(double)(num)` or `double, num` -- but I think I need the type to pass it as a template parameter to create the functor type `function<int (double)>`... the same issue _could_ be present for the eventual macros to wrap Phoenix's function. Maybe there are ways to do this at compile-time instead that a preprocessing-time using template metaprogramming... -- Lorenzo

On 06/02/2011 16:29, Lorenzo Caminiti wrote:
(*) A _possible_ issue with Alex-Steven syntax is that it does not separate the parameter types from the parameter names. I cannot program the preprocessor to retrieve type and name separately from `double num` -- while I can from `(double)(num)` or `double, num` -- but I think I need the type to pass it as a template parameter to create the functor type `function<int (double)>`...
You can pass the name as well to function<int (double num)>. But why do you need to reference function in the first place?

On Mon, Feb 7, 2011 at 4:42 AM, Mathias Gaunard <mathias.gaunard@ens-lyon.org> wrote:
On 06/02/2011 16:29, Lorenzo Caminiti wrote:
(*) A _possible_ issue with Alex-Steven syntax is that it does not separate the parameter types from the parameter names. I cannot program the preprocessor to retrieve type and name separately from `double num` -- while I can from `(double)(num)` or `double, num` -- but I think I need the type to pass it as a template parameter to create the functor type `function<int (double)>`...
You can pass the name as well to function<int (double num)>.
Cool. I have incorporated this suggestion in the new email thread "Simplifying the parenthesized syntax", thanks.
But why do you need to reference function in the first place?
To pass local functions as template parameters using this trick: http://lists.boost.org/Archives/boost/2010/09/170888.php -- Lorenzo

On 05/02/2011 22:04, Lorenzo Caminiti wrote:
Sorry, C++ preprocessor only -- that was a requirement for me... (Can you imagine how much the syntax can be simplified with varidiac macros... I can!! Too bad I can't use them...).
Can you give me one modern C++ compiler that doesn't support variadic macros?

On Sun, Feb 6, 2011 at 10:56 AM, Mathias Gaunard <mathias.gaunard@ens-lyon.org> wrote:
On 05/02/2011 22:04, Lorenzo Caminiti wrote:
Sorry, C++ preprocessor only -- that was a requirement for me... (Can you imagine how much the syntax can be simplified with varidiac macros... I can!! Too bad I can't use them...).
Can you give me one modern C++ compiler that doesn't support variadic macros?
Mathias: No, I can't. Variadic macros are not certified on the compiler I use so *I personally* cannot use them even if they are there (I am really using a gcc C99 preprocessor). *I personally* can only use the C++ standard features, I am not sure about others... As I said, I will take a look to what variadic macros and Alex-Steven syntax can do for Boost.Local. Is there still value for Boost in writing pure C++ libraries? I guess, I am asking if pure C++ ISO standard compliance is and/or should remain a requirement for Boost libraries. As I understand it, that is how for example Boost.Preprocessor was _originally_ written: No C99 preprocessor (no empty sequences), no variadic macros (which are now being added as optional to this library), etc. Of course, a library could work with pure C++ and then offer more features (or a simpler syntax) if C++ extensions can be detected to be available (for example the parenthesized syntax already accepts the empty sequence `(void) (f)()` instead of `(void) (f)( (void) )` on C99 preprocessors). -- Lorenzo

AMDG On 2/6/2011 8:13 AM, Lorenzo Caminiti wrote:
On Sun, Feb 6, 2011 at 10:56 AM, Mathias Gaunard <mathias.gaunard@ens-lyon.org> wrote:
On 05/02/2011 22:04, Lorenzo Caminiti wrote:
Sorry, C++ preprocessor only -- that was a requirement for me... (Can you imagine how much the syntax can be simplified with varidiac macros... I can!! Too bad I can't use them...).
Can you give me one modern C++ compiler that doesn't support variadic macros?
Mathias: No, I can't. Variadic macros are not certified on the compiler I use so *I personally* cannot use them even if they are there (I am really using a gcc C99 preprocessor). *I personally* can only use the C++ standard features, I am not sure about others... As I said, I will take a look to what variadic macros and Alex-Steven syntax can do for Boost.Local.
FWIW, we didn't use variadic macros to implement it. To achieve this, the actual expansion of the macros is rather weird. BOOST_LOCAL_FUNCTION is an object-like macro and the parameter list is the parameter list of the implementation function (it isn't processed by any macros at all). BOOST_BIND closes the local class created by BOOST_LOCAL_FUNCTION and starts a new one. In Christ, Steven Watanabe

On 2/4/11 4:35 AM, Artyom wrote:
I'm sorry is it only me or it would be much more readable and maintainable to write:
It isn't clear to me how writing all of that extra boilerplate makes things easier to understand. BOOST_LOCAL_FUNCTION is not that hard to understand; the name of the macro itself tells you very clearly what it is doing: declaring a local function. Furthermore, if anything I would argue that using structs to define functions makes things harder to understand by adding lots of extra noise that you have to go through before you figure out what the function actually does.
I understand that Pheonix and boost::lambda are very useful in certain situations but am I only person who thinks that if you need some little bit more complicated lambda function you just create a small class with operator() which would be
1. Much More readable
Again, I have to strongly disagree with this. If you have never seen BOOST_LOCAL_FUNCTION then it might take a small amount of time to get used to it, but after that it is incredibly clear what is going on at a glance, especially since it contains less boilerplate noise getting in the way.
2. Clear for source code maintainers who not familiar with some boost toolkits
True, but on the other hand this will always be true of any library that one chooses to use that is not standard C++.
3. Easier to debug and see compiler's error messages
The error messages usually aren't that bad for Boost.Local, and the syntax is simple enough that I found it relatively easy to figure out what went wrong compared to many other libraries.
4. Be friendlier to less experienced C++ programmers who would have to maintain this horrible code?
I strongly object to you repeatedly calling my code horrible just because it does not follow your personal aesthetics.
I do like and use boost::bind, boost::lambda and some other simple construction that can be written in a single like and what is most important improve programmer's productivity but I really fail to see it in this case.
Yes, but all of the arguments you have just made could be applied equally well against boost::bind and boost::lambda, so this isn't a matter of keeping things simple so much as a matter of personal preference. In particular, I think that BOOST_LOCAL_FUNCTION is much clearer than boost::bind because there isn't actual line noise getting in the way of you seeing what is making the function call.
- maintainer happy to be able to actually understand what does the code do without reading Boost.Pheonix or Boost.Local manuals for 5 hours.
If you saw BOOST_LOCAL_FUNCTION for the first time and had to look at the manual, there is no way that it would take you over even one hour to understand what it does unless you are very slow. In fact, it would take much closer to 5 minutes than 5 hours. In conclusion, while I agree that over-use of alien looking constructs can cause more trouble than it is worth I think that you are greatly exaggerating how hard it is to understand BOOST_LOCAL_FUNCTION. Cheers, Greg

On 2/4/11 4:35 AM, Artyom wrote:
I'm sorry is it only me or it would be much more readable and maintainable to write:
It isn't clear to me how writing all of that extra boilerplate makes things easier to understand. BOOST_LOCAL_FUNCTION is not that hard to understand; the name of the macro itself tells you very clearly what it is doing: declaring a local function. Furthermore, if anything I would argue that using structs to define functions makes things harder to understand by adding lots of extra noise that you have to go through before you figure out what the function actually does.
That's purely a choice based on personal taste.
I understand that Pheonix and boost::lambda are very useful in certain situations but am I only person who thinks that if you need some little bit more complicated lambda function you just create a small class with operator() which would be
1. Much More readable
Again, I have to strongly disagree with this. If you have never seen BOOST_LOCAL_FUNCTION then it might take a small amount of time to get used to it, but after that it is incredibly clear what is going on at a glance, especially since it contains less boilerplate noise getting in the way.
Just my 2c: I find the proposed macro syntax to be ugly, unreadable and very difficult to associate with the corresponding function definition.
3. Easier to debug and see compiler's error messages
The error messages usually aren't that bad for Boost.Local, and the syntax is simple enough that I found it relatively easy to figure out what went wrong compared to many other libraries.
Again, that's a matter of taste. I find macros to be more difficult to debug than normal code. You normally can't step through macro code. FWIW, I prefer using lambda/phoenix for simple expressions and I tend to write phoenix functions for more complex things. Working with lazy PFO's has the general advantage to allow for easy function composition, which is a major advantage over plain adhoc 'local functions' as defined by your library. Regards Hartmut --------------- http://boost-spirit.com

On 04/02/2011 19:33, Hartmut Kaiser wrote:
FWIW, I prefer using lambda/phoenix for simple expressions and I tend to write phoenix functions for more complex things. Working with lazy PFO's has the general advantage to allow for easy function composition, which is a major advantage over plain adhoc 'local functions' as defined by your library.
The local function in question is a PFO.

On 2/4/11 10:33 AM, Hartmut Kaiser wrote:
That's purely a choice based on personal taste.
I am perfectly fine with that conclusion, as long as people aren't calling my code horrible. :-)
Just my 2c: I find the proposed macro syntax to be ugly, unreadable and very difficult to associate with the corresponding function definition.
I thought so at first, but it didn't take very long to get used to it.
3. Easier to debug and see compiler's error messages The error messages usually aren't that bad for Boost.Local, and the syntax is simple enough that I found it relatively easy to figure out what went wrong compared to many other libraries. Again, that's a matter of taste. I find macros to be more difficult to debug than normal code. You normally can't step through macro code.
In this case it is really not a matter of taste, but rather it is a matter of having an error message that is just a few lines long and points you in the direction of what you did wrong versus an error message that is thousands of lines long and gives you no useful information at all. :-) If you have never used Boost.Local then you will just have to trust me that the error messages are not as cryptic as you (understandably) seem to expect they should be given that the implementation uses macros.
FWIW, I prefer using lambda/phoenix for simple expressions and I tend to write phoenix functions for more complex things. Working with lazy PFO's has the general advantage to allow for easy function composition, which is a major advantage over plain adhoc 'local functions' as defined by your library.
First, you might already know this but just to clarify: this library is not mine and I have contributed nothing to it except support. This library is one that I picked up after it was advertised a couple of months ago and grew to really like because it scratched a major itch. Second, your arguments sounds a bit to me like saying that one should not use the cryptic macro BOOST_FOREACH when one can use the Boost.Range functions with Phoenix functions. Yes, it is true that there are many times where Phoenix functions may be the better solution, but sometimes you just want to write a for loop over a range using a standard C++ block for the body and it is awfully nice to have a macro that makes that easy. :-) Cheers, Greg

On Fri, Feb 4, 2011 at 12:57 PM, Gregory Crosswhite <gcross@phys.washington.edu> wrote:
The error messages usually aren't that bad for Boost.Local, and the syntax is simple enough that I found it relatively easy to figure out what went wrong compared to many other libraries.
That is also my _personal_ experience. Anyone else that has _used_ Boost.Local and can comment on the complexity of both (1) error messages and (2) parenthesized syntax? I am VERY curious :) Thanks a lot! -- Lorenzo

Artyom wrote:
I'm sorry is it only me or it would be much more readable and maintainable to write:
namespace { struct my_lambda { foo_type &foo; bar_type &bar my_lambda(foo_type &local_foo,bar_type &local_bar) : foo(local_foo), bar(local_bar) { }
void operator()(a_type a) const { /// Your body goes there }
}; }
void my_function() { foo_type foo; bar_type bar;
my_lambda lambda(foo,bar); for_each(as.begin(),as.end(),lambda); // or something else }
No, it's not only you. I'd probably write something like that if a simple bind(&fn,_1) were insufficient. The main complaint about that from the advocates of both Pheonix and Local seems to be that it's too far from the point of use. Well, I have to ask how big your functions are, and if they're huge, why? Also do you dislike code like this: void f1(args) { ........ } void my_big_fn() { .... .... .... f1(a); // using a function only to avoid duplicating the f1(b); // same code for a and b. .... } because the definition of f1 is far from where it's used? Would you countenance using a macro instead:? #define f1(arg) .... f1(a); f1(b); Perhaps it comes down to this: we're used to functions being relatively far from where they're used because it has always been like that, but we think of these lambda expressions as taking the place of the body of a loop, and we're not used to that being out-of-line. Anyway, I'm just going to wait for C++0x lambdas. Regards, Phil.

On 2/4/2011 3:39 PM, Phil Endecott wrote:
No, it's not only you. I'd probably write something like that if a simple bind(&fn,_1) were insufficient.
The main complaint about that from the advocates of both Pheonix and Local seems to be that it's too far from the point of use. Well, I have to ask how big your functions are, and if they're huge, why? Also do you dislike code like this:
void f1(args) { ........ }
void my_big_fn() { .... .... .... f1(a); // using a function only to avoid duplicating the f1(b); // same code for a and b. .... }
because the definition of f1 is far from where it's used? Would you countenance using a macro instead:?
#define f1(arg) .... f1(a); f1(b);
Perhaps it comes down to this: we're used to functions being relatively far from where they're used because it has always been like that, but we think of these lambda expressions as taking the place of the body of a loop, and we're not used to that being out-of-line.
Anyway, I'm just going to wait for C++0x lambdas.
+10000

On Fri, Feb 4, 2011 at 3:43 PM, Kenny Riddile <kfriddile@gmail.com> wrote:
On 2/4/2011 3:39 PM, Phil Endecott wrote:
Anyway, I'm just going to wait for C++0x lambdas.
+10000
Sorry, there is not point in me waiting for C++0x because I can only use standard C++ in my application domain :(( Consider the following case: 1) I program an embedded platform. 2) The supplier of the embedded platform distributes also a certified C++ tool chain (essentially gcc and gdb compiled, tested, and certified for such a platform). 3) I cannot use anything but the certified compiler because if the compiler generates the wrong code the embedded platform might use actuators to make a big mess in the real world (this application is not a website -- even if a few of my friends will be messed up mentally if facebook were to shutdown for a week :) ). 4) The supplier has no intention to certify any C++0x or C99 or anything else (that's a huge effort as they have to "guarantee" the compiler will generate proper code). I am sure in 10+ years a new embedded platform will come along with C++0x... well, I am not sure but I hope so. However, for now, I am "stock" with C++ (even pure C++ compliant preprocessor and template metaprogramming tricks are seen suspiciously in this domain because it is not clear if suppliers really test for them even if they are part of the C++ standard...). -- Lorenzo

Lorenzo Caminiti wrote:
On Fri, Feb 4, 2011 at 3:43 PM, Kenny Riddile <kfriddile@gmail.com> wrote:
On 2/4/2011 3:39 PM, Phil Endecott wrote:
Anyway, I'm just going to wait for C++0x lambdas.
+10000
I came to the same conclusion 3 years ago and stopped working on BOOST_LOCAL_FUNCTION. This was a wrong decision. I'm glad that Lorenzo is working on the library.
Sorry, there is not point in me waiting for C++0x because I can only use standard C++ in my application domain :((
...
I am sure in 10+ years a new embedded platform will come along with C++0x... well, I am not sure but I hope so. However, for now, I am "stock" with C++ (even pure C++ compliant preprocessor and template metaprogramming tricks are seen suspiciously in this domain because it is not clear if suppliers really test for them even if they are part of the C++ standard...).
Lorenzo, can you use typeof (sorry, if you already use it in the implementation, I've not looked at it yet) and variadic macros? BTW, how widely variadic macros are supported nowadays? Alex

On 2/6/2011 7:20 AM, Alexander Nasonov wrote:
Lorenzo Caminiti wrote:
On Fri, Feb 4, 2011 at 3:43 PM, Kenny Riddile<kfriddile@gmail.com> wrote:
On 2/4/2011 3:39 PM, Phil Endecott wrote:
Anyway, I'm just going to wait for C++0x lambdas.
+10000
I came to the same conclusion 3 years ago and stopped working on BOOST_LOCAL_FUNCTION. This was a wrong decision. I'm glad that Lorenzo is working on the library.
Sorry, there is not point in me waiting for C++0x because I can only use standard C++ in my application domain :((
...
I am sure in 10+ years a new embedded platform will come along with C++0x... well, I am not sure but I hope so. However, for now, I am "stock" with C++ (even pure C++ compliant preprocessor and template metaprogramming tricks are seen suspiciously in this domain because it is not clear if suppliers really test for them even if they are part of the C++ standard...).
Lorenzo, can you use typeof (sorry, if you already use it in the implementation, I've not looked at it yet) and variadic macros?
BTW, how widely variadic macros are supported nowadays?
Variadic macro support is defined in Boost for a given compiler when BOOST_NO_VARIADIC_MACROS is not defined. Gcc has had it since version 3.0, VC++ has had it since 8.0, even Borland has had it since BCB6. I suspect Clang supports variadic macros also although I did not try to add it to the clang config files because of lack of knowledge about clang.

On Sun, Feb 6, 2011 at 7:20 AM, Alexander Nasonov <alnsn@yandex.ru> wrote:
Lorenzo Caminiti wrote:
On Fri, Feb 4, 2011 at 3:43 PM, Kenny Riddile <kfriddile@gmail.com> wrote:
On 2/4/2011 3:39 PM, Phil Endecott wrote:
Anyway, I'm just going to wait for C++0x lambdas.
+10000
I came to the same conclusion 3 years ago and stopped working on BOOST_LOCAL_FUNCTION. This was a wrong decision. I'm glad that Lorenzo is working on the library.
Sorry, there is not point in me waiting for C++0x because I can only use standard C++ in my application domain :((
Also, why don't C++0x lambdas support constant binding? (You can only by & or value but not by const& or const value... why?)
I am sure in 10+ years a new embedded platform will come along with C++0x... well, I am not sure but I hope so. However, for now, I am "stock" with C++ (even pure C++ compliant preprocessor and template metaprogramming tricks are seen suspiciously in this domain because it is not clear if suppliers really test for them even if they are part of the C++ standard...).
Lorenzo, can you use typeof (sorry, if you already use it in the implementation, I've not looked at it yet) and variadic macros?
Yes, Boost.Local uses Boost.Typeof to automatically determine the type of the bound variables. I am actually using *exactly* the same mechanism that you Alex programmed for Boost.ScopeExit (with the global variable `extern boost::scope_exit::aux::undeclared ...`, typeof, etc) plus a patch from Adam Butcher to handle `typeof(*this)` on MSVC (see http://lists.boost.org/Archives/boost/2010/08/170253.php). I think you are also asking if I can use typeof and variadic macros on my embedded platform compiler: 1) Yes, I can use typeof because __typeof__ is actually part of the compiler certification process (strangely enough given that is not pure C++... it must be trivial to test for its correctness... I don't know). Also, as I understand it, I can rely on Boost.Typeof (using sizeof() + SFINAE template metaprogramming tricks) to emulate this functionality on pure C++ compilers. 2) No, I cannot use variadic macros because they are not part of the compiler certification process (even if they are actually there because the preprocessor is really a gcc C99 perprocessor). Also a general guideline in for my application domain is to stick with 100% standard to increase portability -- if a new platform comes along the only reasonable assumption is for it to have a pure C++ standard certified compiler (even if the current certified compiler has a C99 preprocessor, the next certified compiler might not -- if they change the hardware a different base compiler than gcc could be used...). (That said, if the embedded platform were to change there will be quite a bit of low level code that will need to be re-written and everything will need to be throughly re-tested so portability has a restricted meaning in this context and I essentially hope nothing changes with my platform, compiler, debugger, etc ;) .) -- Lorenzo

Lorenzo Caminiti wrote:
On Sun, Feb 6, 2011 at 7:20 AM, Alexander Nasonov <alnsn@yandex.ru> wrote:
Lorenzo, can you use typeof (sorry, if you already use it in the implementation, I've not looked at it yet) and variadic macros?
2) No, I cannot use variadic macros because they are not part of the compiler certification process (even if they are actually there because the preprocessor is really a gcc C99 perprocessor). Also a general guideline in for my application domain is to stick with 100% standard to increase portability -- if a new platform comes along the only reasonable assumption is for it to have a pure C++ standard certified compiler (even if the current certified compiler has a C99 preprocessor, the next certified compiler might not -- if they change the hardware a different base compiler than gcc could be used...). (That said, if the embedded platform were to change there will be quite a bit of low level code that will need to be re-written and everything will need to be throughly re-tested so portability has a restricted meaning in this context and I essentially hope nothing changes with my platform, compiler, debugger, etc ;) .)
Hi Lorenzo, The fact that you can not use variadic macros at work doesn't means that your library can not provide in addition variadic macros on compilers supporting them. I have no idea the work that this suppose, but if the interface could be more appealing your library will have much more people interested in. Have you an idea of how the interface could be simplified if variadic macros were used? Best, Vicente -- View this message in context: http://boost.2283326.n4.nabble.com/Case-study-Boost-Local-versus-Boost-Phoen... Sent from the Boost - Dev mailing list archive at Nabble.com.

On Sun, Feb 6, 2011 at 10:26 AM, Vicente Botet <vicente.botet@wanadoo.fr> wrote:
Lorenzo Caminiti wrote:
On Sun, Feb 6, 2011 at 7:20 AM, Alexander Nasonov <alnsn@yandex.ru> wrote:
Lorenzo, can you use typeof (sorry, if you already use it in the implementation, I've not looked at it yet) and variadic macros?
2) No, I cannot use variadic macros because they are not part of the compiler certification process (even if they are actually there because the preprocessor is really a gcc C99 perprocessor). Also a general guideline in for my application domain is to stick with 100% standard to increase portability -- if a new platform comes along the only reasonable assumption is for it to have a pure C++ standard certified compiler (even if the current certified compiler has a C99 preprocessor, the next certified compiler might not -- if they change the hardware a different base compiler than gcc could be used...). (That said, if the embedded platform were to change there will be quite a bit of low level code that will need to be re-written and everything will need to be throughly re-tested so portability has a restricted meaning in this context and I essentially hope nothing changes with my platform, compiler, debugger, etc ;) .)
Hi Lorenzo,
Hi Vicente!
The fact that you can not use variadic macros at work doesn't means that your library can not provide in addition variadic macros on compilers supporting them. I have no idea the work that this suppose, but if the interface could be more appealing your library will have much more people interested in.
Agree.
Have you an idea of how the interface could be simplified if variadic macros were used?
Nope, not yet :( I can (and should) experiment with this but it'd be nice to get some more feedback on the usability (not just the look) of the current parenthesized syntax (so I also know of specific stuff that should be improved based on actual experience from people that have used the parenthesized syntax). -- Lorenzo

On 2/4/11 12:39 PM, Phil Endecott wrote:
The main complaint about that from the advocates of both Pheonix and Local seems to be that it's too far from the point of use.
True, but we're not saying that it's the end of the world for the function to be so far away, just that we think in many situations that the code is clearer when it is right next to the point of use. Also, the other main complaint is that function objects require writing extra boilerplate.
Well, I have to ask how big your functions are, and if they're huge, why?
It doesn't matter whether the function is big or small; it still helps, in my opinion, to have everything that the function needs contained within the function and declared as close to the point of use as possible. And this is not a new fancy foreign idea but one that we already use with variables; that is, rather than writing code like this: f(...) { int a, b, c; ... a = value; ... b = value; ... c = value; } the idiomatic style in C++ is to declare variables right at the point of first use like this: f(...) { ... int a = value; ... int b = value; ... int c = value; } It doesn't matter whether f() is big or small, it is still idiomatic to put the declarations right next to where the variable is first used. BOOST_LOCAL_FUNCTION is nothing more than an extension of this idea to nested functions.
Also do you dislike code like this:
void f1(args) { ........ }
void my_big_fn() { .... .... .... f1(a); // using a function only to avoid duplicating the f1(b); // same code for a and b. .... }
because the definition of f1 is far from where it's used?
It depends on the situation. If the best solution really is to write a function "f1" in this case and f1 were relatively small and not being used outside of my_big_fn then I would prefer that it be declared right where it is being used for the same reasons that I have said before --- which again are the same reasons that we declare variables close to their point of use --- even if my_big_fn is really my_small_fn. Of course, if f1 is much bigger than my_fn then there might be times when it makes sense to separate it out anyway because its presence breaks up the logic of my_fn too much and so decreases readability. Or if f1 were sufficiently non-trivial then I would be likely to separate it out so that I could more easily run test cases against it. There isn't a one-size-fits-all rule, but in general I would claim that there are many cases where it is at least not unreasonable to want to put the function right before it is used.
Would you countenance using a macro instead:?
#define f1(arg) .... f1(a); f1(b);
Again, it depends. If we're talking about just a couple of lines that I want to refactor then I would be more likely to use a macro. Keep in mind though that the use case for which I have mainly been using this library is not the case where I wrote a function for the sake of refactoring code I had copied and pasted but rather the case where I wanted to pass a local function to another function.
Perhaps it comes down to this: we're used to functions being relatively far from where they're used because it has always been like that, but we think of these lambda expressions as taking the place of the body of a loop, and we're not used to that being out-of-line.
Interesting point. Along these lines, I will repeat my remark that there is some similarity in this situation to how we like having BOOST_FOREACH available to us even though we could theoretically accomplish the same thing using a function object and boost::for_each or std::for_each.
Anyway, I'm just going to wait for C++0x lambdas.
Well lucky for you, the Boost.Local manual actually has a clause in it that says that you don't have to use it if you don't want to. :-) But on a more serious note, nobody here is claiming that BOOST_LOCAL_FUNCTION is the best tool to use in all situations or that everyone has to use it. All that we are claiming (and again, I say this not as a developer but as a user who was made very happy by this library) is that it is a good general tool to have available and that many people and situations would benefit from it. Finally, it is worth mentioning that although you might be lucky enough to be able to use C++0x lambdas in the near future, many of us won't be able to rely on them being available on all of the platforms for which we want to develop for a long time, just as we won't be able to rely on rvalues being available for a long time. There is plenty of precedent for Boost libraries (e.g. Boost.Move, which is *awesome* by-the-way!) that allow us to use features similar to those in C++0x in C++03 environments. Cheers, Greg

Phil Endecott wrote:
The main complaint about that from the advocates of both Pheonix and Local seems to be that it's too far from the point of use. Well, I have to ask how big your functions are, and if they're huge, why? Also do you dislike code like this:
void f1(args) { ........ }
void my_big_fn() { .... .... .... f1(a); // using a function only to avoid duplicating the f1(b); // same code for a and b. .... }
because the definition of f1 is far from where it's used? Would you countenance using a macro instead:?
#define f1(arg) .... f1(a); f1(b);
Perhaps it comes down to this: we're used to functions being relatively far from where they're used because it has always been like that, but we think of these lambda expressions as taking the place of the body of a loop, and we're not used to that being out-of-line.
It isn't a matter of distance or familiarity, it is a matter of scope. Let's look at the use case for local functions passed as arguments to other functions. If the interace we are passing the function argument into isn't sufficiently generic we may not have access to the data we need through the arguments of the function we pass. One way to work around this is to make the data global. Now the function has acces to the data and can use it when it is called. The problem with this is that we don't want a mess of global data. If we instead declare the function as a local function it has access to the local scope data of the function in which it was declared when it is called from within the function to which it was passed. Moving the function out of the local scope to the namespace scope means we have to move that data out of the local scope and into the namespace scope also. That is objectionable and has serious implications for writing re-entrant code. Regards, Luke

Simonson, Lucanus J wrote:
Phil Endecott wrote:
The main complaint about that from the advocates of both Pheonix and Local seems to be that it's too far from the point of use.
It isn't a matter of distance or familiarity, it is a matter of scope.
Right, typically you have to pass-by-reference any local variables that you need to the functors' constructor. It would often be better to have automatic access to the content of the local scope. C++0x lambdas can have that. With Boost.Local I believe that you still have to explicitly capture the local variables that you need: (approximate syntax here:) void my_function() { foo_type foo; bar_type bar; BOOST_LOCAL_FUNCTION( (void) (my_lambda) ( (arg_type)(a) (bind)(&foo) (bind)(&bar) ) ) { ....... Note the (bind)(&foo) bit.
If we instead declare the function as a local function it has access to the local scope data of the function in which it was declared
If Boost.Local has some way to automatically access variables in the scope in which it is declared without having to explicitly list them, that is something that I have missed. Regards, Phil.

Phil Endecott wrote:
Simonson, Lucanus J wrote:
Phil Endecott wrote:
The main complaint about that from the advocates of both Pheonix and Local seems to be that it's too far from the point of use.
It isn't a matter of distance or familiarity, it is a matter of scope.
Right, typically you have to pass-by-reference any local variables that you need to the functors' constructor. It would often be better to have automatic access to the content of the local scope. C++0x lambdas can have that.
With Boost.Local I believe that you still have to explicitly capture the local variables that you need: (approximate syntax here:)
void my_function() { foo_type foo; bar_type bar;
BOOST_LOCAL_FUNCTION( (void) (my_lambda) ( (arg_type)(a) (bind)(&foo) (bind)(&bar) ) ) { .......
Note the (bind)(&foo) bit.
If we instead declare the function as a local function it has access to the local scope data of the function in which it was declared
If Boost.Local has some way to automatically access variables in the scope in which it is declared without having to explicitly list them, that is something that I have missed.
I never looked at the library. I didn't plan on using it or participating in the review and I'm not sure what problem it is solving with these macros. I was talking about local functions in general. What is wrong with: void my_function() { foo_type foo; bar_type bar; void my_lambda( arg_type a) { ....... Is it that my_lambda isn't allowed to be a template parameter or some other strange restriction on local functions or local functions aren't allowed in template functions? I know there was a linker problem with local functions in header files, which sort of interferes with their use in templates. If the library doesn't allow access to a local variable without passing them to a macro what's the point? I can easily declare a struct with members that reference the local varaibles and construct one then pass it as a functor. Did this guy just do that and wrap it with a macro? Regards, Luke

On Fri, Feb 4, 2011 at 7:35 AM, Artyom <artyomtnk@yahoo.com> wrote:
From: Gregory Crosswhite <gcross@phys.washington.edu>
Hey everyone,
This e-mail is going to be a case study of my personal experience in converting code using Boost.Local to use Boost.Phoenix instead in order to get insight into the similarities and differences in what the libraries have to offer.
[snip]
BOOST_LOCAL_FUNCTION( (void) (checkSolution)( (const StandardFormParameters&)(parameters) (const OperatorSpace&)(space) ) ) { checkRegion(Z,space.getOMatrix().slice( parameters.z_bit_diagonal_size ,space.number_of_qubits ,0u ,space.number_of_operators )); } BOOST_LOCAL_FUNCTION_END(checkSolution) forEachStandardFormSolution( number_of_qubits ,number_of_operators ,list_of(EveryColumnHasZ) ,checkSolution );
I'm sorry is it only me or it would be much more readable and maintainable to write:
namespace { struct my_lambda { foo_type &foo; bar_type &bar my_lambda(foo_type &local_foo,bar_type &local_bar) : foo(local_foo), bar(local_bar) { }
void operator()(a_type a) const { /// Your body goes there }
}; }
void my_function() { foo_type foo; bar_type bar;
my_lambda lambda(foo,bar); for_each(as.begin(),as.end(),lambda); // or something else }
1) No, it's not only you. From Stroustrup's Glossary http://www2.research.att.com/~bs/glossary.html : local function - function defined within a function. Not supported by C++. Most often, the use of a local function is a sign that a function is too large. So the creator of C++ would probably argue that you should pull out the code into a separate functor (exactly as you did above) and make your enclosing function `my_function` smaller. What you propose is DEFINITELY one approach.* (I tried to make this point in the library docs.) (*) Note that one (small?) issue with your code if that you have to repeat the bound types `foo_type` and `bar_type` in different places. That might complicate maintainability (if you change the types within the enclosing function `my_function` you need to change them in the `my_lambda` function as well but syntactically they are not linked and they could be defined in two completely different places in the code -- you could have a global typedef, but that's even less "local" than your code... then what if everyone else starts using the same typedef...). Semantically instead these types must always be the same and it would be nice if you could capture such information. Boost.Local uses typeof so you don't have to worry about this (maybe you could rework your example to also use typeof if this was really important but then you have even more noise code...). (I tried to make this point in the library docs.) 2) However, local functions really are a form of *information hiding* (http://en.wikipedia.org/wiki/Nested_function#Purpose). This is not achieved at all by your example because the information about `my_lambda` declaration and definition is not hidden within the scope of its user `my_function`. If you don't need this form of information hiding, it's fine, you really don't have to use it. I found it useful myself (other Pascal and Ada programmers _might_ argue the same given that they are accustomed to using local functions as they have language support for it; I personally used them when I programmed in Turbo Pascal back in the days ;) ). 3) I also needed local functions because I needed a macro to expand locally: void f(int& zero) { BLOCK_INVARIANT( (const (zero) (zero == 0)) ) // Compiler error if programmers mistake `==` with `=`. ... } I can do this with Boost.Local: void f(int& zero) { BOOST_LOCAL_BLOCK( (const bind)((&zero)) ) { assert(zero == 0); } BOOST_LOCAL_BLOCK_END ... } There's no way I can program `BLOCK_INVARIANT` to expand to code outside `f` (like in the code you proposed) because macros are always expanded right where they are invoked. So for this one use case, your code just doesn't do the trick... -- Lorenzo

On Sat, Feb 5, 2011 at 3:52 PM, Lorenzo Caminiti <lorcaminiti@gmail.com> wrote:
On Fri, Feb 4, 2011 at 7:35 AM, Artyom <artyomtnk@yahoo.com> wrote:
From: Gregory Crosswhite <gcross@phys.washington.edu>
Hey everyone,
This e-mail is going to be a case study of my personal experience in converting code using Boost.Local to use Boost.Phoenix instead in order to get insight into the similarities and differences in what the libraries have to offer.
[snip]
BOOST_LOCAL_FUNCTION( (void) (checkSolution)( (const StandardFormParameters&)(parameters) (const OperatorSpace&)(space) ) ) { checkRegion(Z,space.getOMatrix().slice( parameters.z_bit_diagonal_size ,space.number_of_qubits ,0u ,space.number_of_operators )); } BOOST_LOCAL_FUNCTION_END(checkSolution) forEachStandardFormSolution( number_of_qubits ,number_of_operators ,list_of(EveryColumnHasZ) ,checkSolution );
I'm sorry is it only me or it would be much more readable and maintainable to write:
namespace { struct my_lambda { foo_type &foo; bar_type &bar my_lambda(foo_type &local_foo,bar_type &local_bar) : foo(local_foo), bar(local_bar) { }
void operator()(a_type a) const { /// Your body goes there }
}; }
void my_function() { foo_type foo; bar_type bar;
my_lambda lambda(foo,bar); for_each(as.begin(),as.end(),lambda); // or something else }
1) No, it's not only you. From Stroustrup's Glossary http://www2.research.att.com/~bs/glossary.html :
local function - function defined within a function. Not supported by C++. Most often, the use of a local function is a sign that a function is too large.
So the creator of C++ would probably argue that you should pull out the code into a separate functor (exactly as you did above) and make your enclosing function `my_function` smaller. What you propose is DEFINITELY one approach.* (I tried to make this point in the library docs.)
(*) Note that one (small?) issue with your code if that you have to repeat the bound types `foo_type` and `bar_type` in different places. That might complicate maintainability (if you change the types within the enclosing function `my_function` you need to change them in the `my_lambda` function as well but syntactically they are not linked and they could be defined in two completely different places in the code -- you could have a global typedef, but that's even less "local" than your code... then what if everyone else starts using the same typedef...). Semantically instead these types must always be the same and it would be nice if you could capture such information. Boost.Local uses typeof so you don't have to worry about this (maybe you could rework your example to also use typeof if this was really important but then you have even more noise code...). (I tried to make this point in the library docs.)
2) However, local functions really are a form of *information hiding* (http://en.wikipedia.org/wiki/Nested_function#Purpose). This is not achieved at all by your example because the information about `my_lambda` declaration and definition is not hidden within the scope of its user `my_function`. If you don't need this form of information hiding, it's fine, you really don't have to use it. I found it useful myself (other Pascal and Ada programmers _might_ argue the same given that they are accustomed to using local functions as they have language support for it; I personally used them when I programmed in Turbo Pascal back in the days ;) ).
3) I also needed local functions because I needed a macro to expand locally:
void f(int& zero) { BLOCK_INVARIANT( (const (zero) (zero == 0)) ) // Compiler error if programmers mistake `==` with `=`. ... }
I can do this with Boost.Local:
void f(int& zero) { BOOST_LOCAL_BLOCK( (const bind)((&zero)) ) { assert(zero == 0); } BOOST_LOCAL_BLOCK_END ... }
There's no way I can program `BLOCK_INVARIANT` to expand to code outside `f` (like in the code you proposed) because macros are always expanded right where they are invoked. So for this one use case, your code just doesn't do the trick...
2a) Also I forgot to reference N2511 (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2511.html) for a discussion on why named lambdas / local functions are useful and should be added to standard. (Again, this is mentioned in Boost.Local docs.) -- Lorenzo

On Thu, Feb 3, 2011 at 9:36 PM, Gregory Crosswhite <gcross@phys.washington.edu> wrote:
In conclusion, I have found that Boost.Phoenix is simply too painful to use in practice for most cases where I have been using Boost.Local. Although it could potentially allow for very elegant code in many cases, it is so hard to figure out what you are doing wrong that it seems to be more trouble than it is worth. I am actually a little sad at having arrived at this conclusion, because the library looked incredibly cool and I was very excited about trying it out, and now I am just walking away from the whole experience feeling incredibly frustrated. Furthermore, even if I were an expert in it I have trouble seeing how in most of the places in my code it would result in code that was either more clear or easier to write. The Boost.Local code has extra noise at the beginning, but when the main body of the nested function contains lots of calls it is far more expressive to write the C++ code directly than to use lots of pheonix::bind functions to accomplish the same thing.
This doesn't mean that I think that Boost.Phoenix is a bad library. Reading through the documentation I am absolutely amazed at how it can be used to create very expressive functions; the authors have clearly worked very hard on it and should be proud of their work. However, it simply cannot be treated as invaliding the need for something like Boost.Local, because for one to accomplish many of the same tasks in Boost.Phoenix as one can accomplish in Boost.Local one has to deal with a whole lot of extra mental effort and frustration, and the result at the end is often less expressive and clear (and potentially less maintainable) as it would have been if one had used Boost.Local since the body is no longer expressed in standard C++.
I hope that you all find this informative!
Indeed, very informative for me. (I only hope my library is helping you enough to justify the time you spent on this comparison :) .) -- Lorenzo
participants (18)
-
Alexander Nasonov
-
Anthony Williams
-
Artyom
-
Christian Holmquist
-
Edward Diener
-
Gregory Crosswhite
-
Hartmut Kaiser
-
Jeffrey Lee Hellrung, Jr.
-
Joel de Guzman
-
Kenny Riddile
-
Lorenzo Caminiti
-
Mathias Gaunard
-
Phil Endecott
-
Rob Riggs
-
Simonson, Lucanus J
-
Steven Watanabe
-
Thomas Heller
-
Vicente Botet