Re: [boost] New libraries implementing C++11 features in C++03

On 23 Nov 2011 10:21:50 Joel de Guzman wrote:
a good approximation of lambda is already existing in Boost. Namely: bind, lambda and phoenix. I posted a Spirit example of lambda in action using Phoenix:
Try as hard as you can, but you cannot ever come close to the clarity of he syntax presented there. Proponents of locals have cited error-messages generated by the compiler as a justification for Locals. Locals is probably a good workaround. But hear me out...
, Skipper = boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, Attr = int]::skipper_type, Attribute = int, Subject = const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > , int(), boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, boost::spirit::unused_type, boost::spirit::unused_type>]': /usr/include/boost/spirit/home/qi/parse.hpp:164: instantiated from 'bool boost::spirit::qi::phrase_parse(Iterator&, Iterator, const Expr&, const Skipper&, boost::spirit::qi::skip_flag::enum_type, Attr&) [with Iterator = __gnu_cxx::__normal_iterator<char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > , Expr = client::calculator<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > , Skipper = boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, Attr = int]' /usr/include/boost/spirit/home/qi/parse.hpp:197: instantiated from 'bool boost::spirit::qi::phrase_parse(Iterator&, Iterator, const Expr&, const Skipper&, Attr&) [with Iterator = __gnu_cxx::__normal_iterator<char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, Expr =
First of all, this "trivial" example didn't even compile on the first machine I tried it on, which had Boost 1.45 (which doesn't expose qi:_val_type or qi:_1_type). This is a pretty good reminder of the fact that Phoenix remains a fast moving target (1.45 is less than a year old) and still appears unnecessarily baroque to new users (why are typenames that need to be cited in even a basic tutorial prefixed with an underscore?) Local/Closure, on the other hand, seems to have a pretty stable interface at this point. On a machine with 1.48, the example compiles just fine. If you were to pass std::string::iterators instead of const_iterators to phrase_parse(), though ( a pretty common class of mistake in the real world), you get this error message (GCC 4.4.4): /usr/include/boost/spirit/home/qi/reference.hpp: In member function 'bool boost::spirit::qi::reference<Subject>::parse(Iterator&, const Iterator&, Context&, const Skipper&, Attribute&) const [with Iterator = __gnu_cxx::__normal_iterator<char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, Context = boost::spirit::context<boost::fusion::cons<int&, boost::fusion::nil>, boost::spirit::locals<mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na> >, Skipper = boost::spirit::qi::phrase_parse(Iterator&, Iterator, const Expr&, const Skipper&, boost::spirit::qi::skip_flag::enum_type, Attr&) [with Iterator = __gnu_cxx::__normal_iterator<char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, Expr = client::calculator<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > main()::calculator, Skipper = boost::spirit::ascii::space_type, Attr = int]' calc3.cpp:101: instantiated from here /usr/include/boost/spirit/home/qi/reference.hpp:43: error: no matching function for call to 'boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> >
, int(), boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, boost::spirit::unused_type, boost::spirit::unused_type>::parse(__gnu_cxx::__normal_iterator<char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > &, const __gnu_cxx::__normal_iterator<char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >&, boost::spirit::context<boost::fusion::cons<int&, boost::fusion::nil>, boost::spirit::locals<mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na> >&, const boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >&, int&) const'
But that's nothing; if you omit the '>>' from the expression or term definition you get the 33 kilochar beauty copied at the bottom of this email. There have been several postings to this list in the past about trivial errors (substituting the wrong type or omitting a forward declaration and so forth) that generate hundreds of kilobytes of compiler barf with no apparent relation to what is actually wrong with the source. Yes, the compiler is also largely to blame (although GCC does not seem any worse here than the other mainstream options) and perhaps someday this will finally get better, but I think you grossly underestimate the deterrent effect this has on new users and on wider adoption of Phoenix and similar libraries. Phoenix is great when you really need it, but there's also quite a lot to be said for alternatives (including Boost.Local) that don't build massive, fragile scaffolds of parametric types and in some sense keep what the compiler 'sees' on a given line of source similar to what the programmer sees in a text editor. Don't get me wrong; I think Phoenix is an amazing piece of work, and for someone who needs late binding and lazy evaluation in an expression (or a small DSEL without depending on an external parser generator) it's a godsend. I just happen to believe that you could say the same things about Boost.Local--- if this is what you need, it's *exactly* what you need. Many Boost users may never need local functions, but you could again say the same thing about the vast majority of the 113 official components of 1.48. If you want to keep Boost limited to the bare essentials, it's already far too late. The bar for entry should be that the library is well designed, well documented, well implemented, serves a need that a nontrivial number of users have, and isn't likely to interfere with the implementation of other future language or Boost features, not that it will be used just as frequently as enable_if<> or optional<>.
Keep in mind that Boost has been at the forefront of C++ library development. It is because of these libraries that push the limits of C++ that we see the advancement in C++ that we enjoy now in C++11. Case in point is Boost Lambda
Hey, that's great. I just think that we all need to recognize that very little C++ software is written with "push the limits of the language" as one of the goals in mind. The participants on this particular list have self-selected for interest in developing state-of-the-art libraries, but most also have day jobs where they're just trying to maintain an existing codebase with minimal hassle for their colleagues and customers, as well as side projects where getting something running quickly and correctly is more important (if not always more interesting) than figuring out just how far we can push overload resolution rules. And probably 99% of Boost users and 99.9% of potential Boost users aren't the kind of programmers on this list and have no interest at all in "pushing the envelope"--- they just want tools with simple, stable syntax and easy debugging, both of which Local offers. Furthermore, no matter how slick your personal environment is, it's a simple fact that there are thousands and thousands of important projects that will never have the benefit of a C++11 compiler for a variety of reasons. I *really* don't understand the criticism from several people about using macros to implement the library. Part of the power of C++ is that its syntax is extensible, and the preprocessor and operator overloading are the two mechanisms that enable that. The preprocessor is part of the language specification, arguably even more so than the standard library since a (nonconforming) implementation that ships with only 10% of the standard library implemented is far more useful in practice than an implementation that ships with only 10% of the preprocessor implemented. Yes, macros can be abused, as can operator overloading (as many would criticize Phoenix), but often they're the most appropriate choice. There should be only two questions here: are there a nontrivial number of users who desire or would benefit from local functions, and is there a better way to provide them than what Lorenzo has chosen? ------------------- Horrific error messages follow --------------------- /usr/include/boost/spirit/home/qi/nonterminal/rule.hpp: In static member function 'static void boost::spirit::qi::rule<Iterator, T1, T2, T3, T4>::define(boost::spirit::qi::rule<Iterator, T1, T2, T3, T4>&, const Expr&, mpl_::false_) [with Auto = mpl_::bool_<false>, Expr = boost::proto::exprns_::expr<boost::proto::tagns_::tag::multiplies, boost::proto::argsns_::list2<const boost::proto::exprns_::expr<boost::proto::tagns_::tag::subscript, boost::proto::argsns_::list2<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, int(), boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, boost::spirit::unused_type, boost::spirit::unused_type>&, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::assign_eval, boost::fusion::vector<boost::spirit::attribute<0>, boost::spirit::argument<0>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > >&>, 0l> >, 2l>&, const boost::proto::exprns_::expr<boost::proto::tagns_::tag::bitwise_or, boost::proto::argsns_::list2<const boost::proto::exprns_::expr<boost::proto::tagns_::tag::shift_right, boost::proto::argsns_::list2<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const char&>, 0l>, const boost::proto::exprns_::expr<boost::proto::tagns_::tag::subscript, boost::proto::argsns_::list2<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, int(), boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, boost::spirit::unused_type, boost::spirit::unused_type>&, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::plus_assign_eval, boost::fusion::vector<boost::spirit::attribute<0>, boost::spirit::argument<0>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > >&>, 0l> >, 2l>&>, 2l>&, const boost::proto::exprns_::expr<boost::proto::tagns_::tag::shift_right, boost::proto::argsns_::list2<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const char&>, 0l>, const boost::proto::exprns_::expr<boost::proto::tagns_::tag::subscript, boost::proto::argsns_::list2<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, int(), boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, boost::spirit::unused_type, boost::spirit::unused_type>&, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::minus_assign_eval, boost::fusion::vector<boost::spirit::attribute<0>, boost::spirit::argument<0>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > >&>, 0l> >, 2l>&>, 2l>&>, 2l>&>, 2l>, Iterator = __gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, T1 = int(), T2 = boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, T3 = boost::spirit::unused_type, T4 = boost::spirit::unused_type]': /usr/include/boost/spirit/home/qi/nonterminal/rule.hpp:220: instantiated from 'boost::spirit::qi::rule<Iterator, T1, T2, T3, T4>& boost::spirit::qi::rule<Iterator, T1, T2, T3, T4>::operator=(const Expr&) [with Expr = boost::proto::exprns_::expr<boost::proto::tagns_::tag::multiplies, boost::proto::argsns_::list2<const boost::proto::exprns_::expr<boost::proto::tagns_::tag::subscript, boost::proto::argsns_::list2<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, int(), boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, boost::spirit::unused_type, boost::spirit::unused_type>&, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::assign_eval, boost::fusion::vector<boost::spirit::attribute<0>, boost::spirit::argument<0>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > >&>, 0l> >, 2l>&, const boost::proto::exprns_::expr<boost::proto::tagns_::tag::bitwise_or, boost::proto::argsns_::list2<const boost::proto::exprns_::expr<boost::proto::tagns_::tag::shift_right, boost::proto::argsns_::list2<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const char&>, 0l>, const boost::proto::exprns_::expr<boost::proto::tagns_::tag::subscript, boost::proto::argsns_::list2<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, int(), boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, boost::spirit::unused_type, boost::spirit::unused_type>&, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::plus_assign_eval, boost::fusion::vector<boost::spirit::attribute<0>, boost::spirit::argument<0>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > >&>, 0l> >, 2l>&>, 2l>&, const boost::proto::exprns_::expr<boost::proto::tagns_::tag::shift_right, boost::proto::argsns_::list2<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const char&>, 0l>, const boost::proto::exprns_::expr<boost::proto::tagns_::tag::subscript, boost::proto::argsns_::list2<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, int(), boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, boost::spirit::unused_type, boost::spirit::unused_type>&, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::minus_assign_eval, boost::fusion::vector<boost::spirit::attribute<0>, boost::spirit::argument<0>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > >&>, 0l> >, 2l>&>, 2l>&>, 2l>&>, 2l>, Iterator = __gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, T1 = int(), T2 = boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, T3 = boost::spirit::unused_type, T4 = boost::spirit::unused_type]' calc3.cpp:49: instantiated from 'client::calculator<Iterator>::calculator() [with Iterator = __gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >]' calc3.cpp:90: instantiated from here /usr/include/boost/spirit/home/qi/nonterminal/rule.hpp:176: error: no matching function for call to 'assertion_failed(mpl_::failed************ (boost::spirit::qi::rule<Iterator, T1, T2, T3, T4>::define(boost::spirit::qi::rule<Iterator, T1, T2, T3, T4>&, const Expr&, mpl_::false_) [with Auto = mpl_::bool_<false>, Expr = boost::proto::exprns_::expr<boost::proto::tagns_::tag::multiplies, boost::proto::argsns_::list2<const boost::proto::exprns_::expr<boost::proto::tagns_::tag::subscript, boost::proto::argsns_::list2<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, int(), boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, boost::spirit::unused_type, boost::spirit::unused_type>&, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::assign_eval, boost::fusion::vector<boost::spirit::attribute<0>, boost::spirit::argument<0>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > >&>, 0l> >, 2l>&, const boost::proto::exprns_::expr<boost::proto::tagns_::tag::bitwise_or, boost::proto::argsns_::list2<const boost::proto::exprns_::expr<boost::proto::tagns_::tag::shift_right, boost::proto::argsns_::list2<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const char&>, 0l>, const boost::proto::exprns_::expr<boost::proto::tagns_::tag::subscript, boost::proto::argsns_::list2<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, int(), boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, boost::spirit::unused_type, boost::spirit::unused_type>&, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::plus_assign_eval, boost::fusion::vector<boost::spirit::attribute<0>, boost::spirit::argument<0>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > >&>, 0l> >, 2l>&>, 2l>&, const boost::proto::exprns_::expr<boost::proto::tagns_::tag::shift_right, boost::proto::argsns_::list2<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const char&>, 0l>, const boost::proto::exprns_::expr<boost::proto::tagns_::tag::subscript, boost::proto::argsns_::list2<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, int(), boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, boost::spirit::unused_type, boost::spirit::unused_type>&, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::minus_assign_eval, boost::fusion::vector<boost::spirit::attribute<0>, boost::spirit::argument<0>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > >&>, 0l> >, 2l>&>, 2l>&>, 2l>&>, 2l>, Iterator = __gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, T1 = int(), T2 = boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, T3 = boost::spirit::unused_type, T4 = boost::spirit::unused_type]::error_invalid_expression::************)(boost::proto::exprns_::expr<boost::proto::tagns_::tag::multiplies, boost::proto::argsns_::list2<const boost::proto::exprns_::expr<boost::proto::tagns_::tag::subscript, boost::proto::argsns_::list2<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, int(), boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, boost::spirit::unused_type, boost::spirit::unused_type>&, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::assign_eval, boost::fusion::vector<boost::spirit::attribute<0>, boost::spirit::argument<0>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > >&>, 0l> >, 2l>&, const boost::proto::exprns_::expr<boost::proto::tagns_::tag::bitwise_or, boost::proto::argsns_::list2<const boost::proto::exprns_::expr<boost::proto::tagns_::tag::shift_right, boost::proto::argsns_::list2<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const char&>, 0l>, const boost::proto::exprns_::expr<boost::proto::tagns_::tag::subscript, boost::proto::argsns_::list2<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, int(), boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, boost::spirit::unused_type, boost::spirit::unused_type>&, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::plus_assign_eval, boost::fusion::vector<boost::spirit::attribute<0>, boost::spirit::argument<0>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > >&>, 0l> >, 2l>&>, 2l>&, const boost::proto::exprns_::expr<boost::proto::tagns_::tag::shift_right, boost::proto::argsns_::list2<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const char&>, 0l>, const boost::proto::exprns_::expr<boost::proto::tagns_::tag::subscript, boost::proto::argsns_::list2<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, int(), boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, boost::spirit::unused_type, boost::spirit::unused_type>&, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::minus_assign_eval, boost::fusion::vector<boost::spirit::attribute<0>, boost::spirit::argument<0>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > >&>, 0l> >, 2l>&>, 2l>&>, 2l>&>, 2l>))' /usr/include/boost/spirit/home/qi/nonterminal/rule.hpp: In static member function 'static void boost::spirit::qi::rule<Iterator, T1, T2, T3, T4>::define(boost::spirit::qi::rule<Iterator, T1, T2, T3, T4>&, const Expr&, mpl_::false_) [with Auto = mpl_::bool_<false>, Expr = boost::proto::exprns_::expr<boost::proto::tagns_::tag::multiplies, boost::proto::argsns_::list2<const boost::proto::exprns_::expr<boost::proto::tagns_::tag::subscript, boost::proto::argsns_::list2<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, int(), boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, boost::spirit::unused_type, boost::spirit::unused_type>&, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::assign_eval, boost::fusion::vector<boost::spirit::attribute<0>, boost::spirit::argument<0>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > >&>, 0l> >, 2l>&, const boost::proto::exprns_::expr<boost::proto::tagns_::tag::bitwise_or, boost::proto::argsns_::list2<const boost::proto::exprns_::expr<boost::proto::tagns_::tag::shift_right, boost::proto::argsns_::list2<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const char&>, 0l>, const boost::proto::exprns_::expr<boost::proto::tagns_::tag::subscript, boost::proto::argsns_::list2<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, int(), boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, boost::spirit::unused_type, boost::spirit::unused_type>&, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::multiplies_assign_eval, boost::fusion::vector<boost::spirit::attribute<0>, boost::spirit::argument<0>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > >&>, 0l> >, 2l>&>, 2l>&, const boost::proto::exprns_::expr<boost::proto::tagns_::tag::shift_right, boost::proto::argsns_::list2<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const char&>, 0l>, const boost::proto::exprns_::expr<boost::proto::tagns_::tag::subscript, boost::proto::argsns_::list2<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, int(), boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, boost::spirit::unused_type, boost::spirit::unused_type>&, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::divides_assign_eval, boost::fusion::vector<boost::spirit::attribute<0>, boost::spirit::argument<0>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > >&>, 0l> >, 2l>&>, 2l>&>, 2l>&>, 2l>, Iterator = __gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, T1 = int(), T2 = boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, T3 = boost::spirit::unused_type, T4 = boost::spirit::unused_type]': /usr/include/boost/spirit/home/qi/nonterminal/rule.hpp:220: instantiated from 'boost::spirit::qi::rule<Iterator, T1, T2, T3, T4>& boost::spirit::qi::rule<Iterator, T1, T2, T3, T4>::operator=(const Expr&) [with Expr = boost::proto::exprns_::expr<boost::proto::tagns_::tag::multiplies, boost::proto::argsns_::list2<const boost::proto::exprns_::expr<boost::proto::tagns_::tag::subscript, boost::proto::argsns_::list2<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, int(), boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, boost::spirit::unused_type, boost::spirit::unused_type>&, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::assign_eval, boost::fusion::vector<boost::spirit::attribute<0>, boost::spirit::argument<0>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > >&>, 0l> >, 2l>&, const boost::proto::exprns_::expr<boost::proto::tagns_::tag::bitwise_or, boost::proto::argsns_::list2<const boost::proto::exprns_::expr<boost::proto::tagns_::tag::shift_right, boost::proto::argsns_::list2<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const char&>, 0l>, const boost::proto::exprns_::expr<boost::proto::tagns_::tag::subscript, boost::proto::argsns_::list2<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, int(), boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, boost::spirit::unused_type, boost::spirit::unused_type>&, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::multiplies_assign_eval, boost::fusion::vector<boost::spirit::attribute<0>, boost::spirit::argument<0>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > >&>, 0l> >, 2l>&>, 2l>&, const boost::proto::exprns_::expr<boost::proto::tagns_::tag::shift_right, boost::proto::argsns_::list2<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const char&>, 0l>, const boost::proto::exprns_::expr<boost::proto::tagns_::tag::subscript, boost::proto::argsns_::list2<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, int(), boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, boost::spirit::unused_type, boost::spirit::unused_type>&, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::divides_assign_eval, boost::fusion::vector<boost::spirit::attribute<0>, boost::spirit::argument<0>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > >&>, 0l> >, 2l>&>, 2l>&>, 2l>&>, 2l>, Iterator = __gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, T1 = int(), T2 = boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, T3 = boost::spirit::unused_type, T4 = boost::spirit::unused_type]' calc3.cpp:56: instantiated from 'client::calculator<Iterator>::calculator() [with Iterator = __gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >]' calc3.cpp:90: instantiated from here /usr/include/boost/spirit/home/qi/nonterminal/rule.hpp:176: error: no matching function for call to 'assertion_failed(mpl_::failed************ (boost::spirit::qi::rule<Iterator, T1, T2, T3, T4>::define(boost::spirit::qi::rule<Iterator, T1, T2, T3, T4>&, const Expr&, mpl_::false_) [with Auto = mpl_::bool_<false>, Expr = boost::proto::exprns_::expr<boost::proto::tagns_::tag::multiplies, boost::proto::argsns_::list2<const boost::proto::exprns_::expr<boost::proto::tagns_::tag::subscript, boost::proto::argsns_::list2<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, int(), boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, boost::spirit::unused_type, boost::spirit::unused_type>&, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::assign_eval, boost::fusion::vector<boost::spirit::attribute<0>, boost::spirit::argument<0>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > >&>, 0l> >, 2l>&, const boost::proto::exprns_::expr<boost::proto::tagns_::tag::bitwise_or, boost::proto::argsns_::list2<const boost::proto::exprns_::expr<boost::proto::tagns_::tag::shift_right, boost::proto::argsns_::list2<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const char&>, 0l>, const boost::proto::exprns_::expr<boost::proto::tagns_::tag::subscript, boost::proto::argsns_::list2<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, int(), boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, boost::spirit::unused_type, boost::spirit::unused_type>&, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::multiplies_assign_eval, boost::fusion::vector<boost::spirit::attribute<0>, boost::spirit::argument<0>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > >&>, 0l> >, 2l>&>, 2l>&, const boost::proto::exprns_::expr<boost::proto::tagns_::tag::shift_right, boost::proto::argsns_::list2<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const char&>, 0l>, const boost::proto::exprns_::expr<boost::proto::tagns_::tag::subscript, boost::proto::argsns_::list2<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, int(), boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, boost::spirit::unused_type, boost::spirit::unused_type>&, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::divides_assign_eval, boost::fusion::vector<boost::spirit::attribute<0>, boost::spirit::argument<0>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > >&>, 0l> >, 2l>&>, 2l>&>, 2l>&>, 2l>, Iterator = __gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, T1 = int(), T2 = boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, T3 = boost::spirit::unused_type, T4 = boost::spirit::unused_type]::error_invalid_expression::************)(boost::proto::exprns_::expr<boost::proto::tagns_::tag::multiplies, boost::proto::argsns_::list2<const boost::proto::exprns_::expr<boost::proto::tagns_::tag::subscript, boost::proto::argsns_::list2<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, int(), boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, boost::spirit::unused_type, boost::spirit::unused_type>&, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::assign_eval, boost::fusion::vector<boost::spirit::attribute<0>, boost::spirit::argument<0>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > >&>, 0l> >, 2l>&, const boost::proto::exprns_::expr<boost::proto::tagns_::tag::bitwise_or, boost::proto::argsns_::list2<const boost::proto::exprns_::expr<boost::proto::tagns_::tag::shift_right, boost::proto::argsns_::list2<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const char&>, 0l>, const boost::proto::exprns_::expr<boost::proto::tagns_::tag::subscript, boost::proto::argsns_::list2<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, int(), boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, boost::spirit::unused_type, boost::spirit::unused_type>&, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::multiplies_assign_eval, boost::fusion::vector<boost::spirit::attribute<0>, boost::spirit::argument<0>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > >&>, 0l> >, 2l>&>, 2l>&, const boost::proto::exprns_::expr<boost::proto::tagns_::tag::shift_right, boost::proto::argsns_::list2<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const char&>, 0l>, const boost::proto::exprns_::expr<boost::proto::tagns_::tag::subscript, boost::proto::argsns_::list2<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, int(), boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, boost::spirit::unused_type, boost::spirit::unused_type>&, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<const boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::divides_assign_eval, boost::fusion::vector<boost::spirit::attribute<0>, boost::spirit::argument<0>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > >&>, 0l> >, 2l>&>, 2l>&>, 2l>&>, 2l>))'

On 11/24/2011 8:06 AM, Brent Spillner wrote:
On 23 Nov 2011 10:21:50 Joel de Guzman wrote:
a good approximation of lambda is already existing in Boost. Namely: bind, lambda and phoenix. I posted a Spirit example of lambda in action using Phoenix:
Try as hard as you can, but you cannot ever come close to the clarity of he syntax presented there. Proponents of locals have cited error-messages generated by the compiler as a justification for Locals. Locals is probably a good workaround. But hear me out...
First of all, this "trivial" example didn't even compile on the first machine I tried it on, which had Boost 1.45 (which doesn't expose qi:_val_type or qi:_1_type). This is a pretty good reminder of the
Man! Of course you should use the examples in 1.45. Those examples are for 1.48 and uses new features implemented in 1.48! Sheesh!
On a machine with 1.48, the example compiles just fine. If you were to pass std::string::iterators instead of const_iterators to phrase_parse(), though ( a pretty common class of mistake in the real world), you get this error message (GCC 4.4.4):
Double Sheesh! If you've spent even a few seconds looking into the error (beyond fault-finding), you will see that a simple textual search of "error" will reveal this:
------------------- Horrific error messages follow ---------------------
calc3.cpp:90: instantiated from here /usr/include/boost/spirit/home/qi/nonterminal/rule.hpp:176: error: no matching function for call to 'assertion_failed(mpl_::failed************ (boost::spirit::qi::rule<Iterator, T1, T2, T3, T4>::define(boost::spirit::qi::rule<Iterator, T1, T2, T3, T4>&, const Expr&, mpl_::false_) [snip ... some complex types] ::error_invalid_expression::************) And double clicking on it using your editor will lead you here: static void define(rule& lhs, Expr const& expr, mpl::false_) { // Report invalid expression error as early as possible. // If you got an error_invalid_expression error message here, // then the expression (expr) is not a valid spirit qi expression. BOOST_SPIRIT_ASSERT_MATCH(qi::domain, Expr); } If you've read the Spirit docs, you will know what that means. Now having said that, and before I leave this thread, let me remind everyone that **you can also have statement syntax in Phoenix** if you have a complex statement and you fear writing complex phoenix lambda expressions. It's called phoenix::functions. In fact here's what I advocate: 1) For simple 1-2-3 liners, use a phoenix lambda expression. It's hard to get them wrong. For example, you'll have to be absurdly dumb to get this wrong: auto plus = _1 + _2; 2) For more complex expressions, especially those involving statements, use a phoenix function plus a simple lambda expression that forwards to the phoenix function. Example: for_each(f, l, call_complex_function(_1)); Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com

On 11/23/2011 07:11 PM, Joel de Guzman wrote:
On 11/24/2011 8:06 AM, Brent Spillner wrote:
On 23 Nov 2011 10:21:50 Joel de Guzman wrote:
a good approximation of lambda is already existing in Boost. Namely: bind, lambda and phoenix. I posted a Spirit example of lambda in action using Phoenix:
Try as hard as you can, but you cannot ever come close to the clarity of he syntax presented there. Proponents of locals have cited error-messages generated by the compiler as a justification for Locals. Locals is probably a good workaround. But hear me out...
First of all, this "trivial" example didn't even compile on the first machine I tried it on, which had Boost 1.45 (which doesn't expose qi:_val_type or qi:_1_type). This is a pretty good reminder of the
Man! Of course you should use the examples in 1.45. Those examples are for 1.48 and uses new features implemented in 1.48! Sheesh!
On a machine with 1.48, the example compiles just fine. If you were to pass std::string::iterators instead of const_iterators to phrase_parse(), though ( a pretty common class of mistake in the real world), you get this error message (GCC 4.4.4):
Double Sheesh! If you've spent even a few seconds looking into the error (beyond fault-finding), you will see that a simple textual search of "error" will reveal this:
------------------- Horrific error messages follow ---------------------
calc3.cpp:90: instantiated from here /usr/include/boost/spirit/home/qi/nonterminal/rule.hpp:176: error: no matching function for call to 'assertion_failed(mpl_::failed************ (boost::spirit::qi::rule<Iterator, T1, T2, T3, T4>::define(boost::spirit::qi::rule<Iterator, T1, T2, T3, T4>&, const Expr&, mpl_::false_)
[snip ... some complex types]
::error_invalid_expression::************)
And double clicking on it using your editor will lead you here:
static void define(rule& lhs, Expr const& expr, mpl::false_) { // Report invalid expression error as early as possible. // If you got an error_invalid_expression error message here, // then the expression (expr) is not a valid spirit qi expression. BOOST_SPIRIT_ASSERT_MATCH(qi::domain, Expr); }
If you've read the Spirit docs, you will know what that means.
Now having said that, and before I leave this thread, let me remind everyone that **you can also have statement syntax in Phoenix** if you have a complex statement and you fear writing complex phoenix lambda expressions. It's called phoenix::functions.
In fact here's what I advocate:
1) For simple 1-2-3 liners, use a phoenix lambda expression. It's hard to get them wrong. For example, you'll have to be absurdly dumb to get this wrong:
auto plus = _1 + _2;
2) For more complex expressions, especially those involving statements, use a phoenix function plus a simple lambda expression that forwards to the phoenix function. Example:
for_each(f, l, call_complex_function(_1));
Additionally, phoenix3 provides you with facilities to adapt already existing functions (yes, even templates) and functors: http://tinyurl.com/cg72pyy
Regards,

On Wed, Nov 23, 2011 at 8:11 PM, Joel de Guzman <joel@boost-consulting.com> wrote:
On 11/24/2011 8:06 AM, Brent Spillner wrote:
On 23 Nov 2011 10:21:50 Joel de Guzman wrote:
a good approximation of lambda is already existing in Boost. Namely: bind, lambda and phoenix. I posted a Spirit example of lambda in action using Phoenix:
Try as hard as you can, but you cannot ever come close to the clarity of he syntax presented there. Proponents of locals have cited error-messages generated by the compiler as a justification for Locals. Locals is probably a good workaround. But hear me out...
First of all, this "trivial" example didn't even compile on the first machine I tried it on, which had Boost 1.45 (which doesn't expose qi:_val_type or qi:_1_type). This is a pretty good reminder of the
Man! Of course you should use the examples in 1.45. Those examples are for 1.48 and uses new features implemented in 1.48! Sheesh!
On a machine with 1.48, the example compiles just fine. If you were to pass std::string::iterators instead of const_iterators to phrase_parse(), though ( a pretty common class of mistake in the real world), you get this error message (GCC 4.4.4):
Double Sheesh! If you've spent even a few seconds looking into the error (beyond fault-finding), you will see that a simple textual search of "error" will reveal this:
------------------- Horrific error messages follow ---------------------
calc3.cpp:90: instantiated from here /usr/include/boost/spirit/home/qi/nonterminal/rule.hpp:176: error: no matching function for call to 'assertion_failed(mpl_::failed************ (boost::spirit::qi::rule<Iterator, T1, T2, T3, T4>::define(boost::spirit::qi::rule<Iterator, T1, T2, T3, T4>&, const Expr&, mpl_::false_)
[snip ... some complex types]
::error_invalid_expression::************)
And double clicking on it using your editor will lead you here:
static void define(rule& lhs, Expr const& expr, mpl::false_) { // Report invalid expression error as early as possible. // If you got an error_invalid_expression error message here, // then the expression (expr) is not a valid spirit qi expression. BOOST_SPIRIT_ASSERT_MATCH(qi::domain, Expr); }
If you've read the Spirit docs, you will know what that means.
Now having said that, and before I leave this thread, let me remind everyone that **you can also have statement syntax in Phoenix** if you have a complex statement and you fear writing complex phoenix lambda expressions. It's called phoenix::functions.
In fact here's what I advocate:
1) For simple 1-2-3 liners, use a phoenix lambda expression. It's hard to get them wrong. For example, you'll have to be absurdly dumb to get this wrong:
auto plus = _1 + _2;
2) For more complex expressions, especially those involving statements, use a phoenix function plus a simple lambda expression that forwards to the phoenix function. Example:
for_each(f, l, call_complex_function(_1));
IF (this is a big IF) I understand it correctly, Phoenix functions are non-local functors: http://www.boost.org/doc/libs/1_48_0/libs/phoenix/doc/html/phoenix/starter_k... // non-local scope struct is_odd_impl { typedef bool result_type; template <typename Arg> bool operator()(Arg arg1) const { return arg1 % 2 == 1; } }; // local scope function<is_odd_impl> is_odd; A couple of people have argued that for complex task it is OK to push the code to non-local scope. That is their point of view but of course it goes fundamentally against local functions principles (e.g., N2511) and the fact that even local functions can be long in some programming style (as expressed by others commenting before). IMO, our library users should be felt the opportunity to program as they see more fit to themselves and their problem domain (long/short local functions, long/short global Phoenix functions, long/short Phoenix lambdas ( yes, even long Phoenix lambdas if the users want to) ). HTH, --Lorenzo

On Thu, Nov 24, 2011 at 8:16 PM, Lorenzo Caminiti <lorcaminiti@gmail.com> wrote:
On Wed, Nov 23, 2011 at 8:11 PM, Joel de Guzman <joel@boost-consulting.com> wrote:
If you've read the Spirit docs, you will know what that means.
Now having said that, and before I leave this thread, let me remind everyone that **you can also have statement syntax in Phoenix** if you have a complex statement and you fear writing complex phoenix lambda expressions. It's called phoenix::functions.
In fact here's what I advocate:
1) For simple 1-2-3 liners, use a phoenix lambda expression. It's hard to get them wrong. For example, you'll have to be absurdly dumb to get this wrong:
auto plus = _1 + _2;
2) For more complex expressions, especially those involving statements, use a phoenix function plus a simple lambda expression that forwards to the phoenix function. Example:
for_each(f, l, call_complex_function(_1));
IF (this is a big IF) I understand it correctly, Phoenix functions are non-local functors: http://www.boost.org/doc/libs/1_48_0/libs/phoenix/doc/html/phoenix/starter_k...
// non-local scope struct is_odd_impl { typedef bool result_type;
template <typename Arg> bool operator()(Arg arg1) const { return arg1 % 2 == 1; } };
// local scope function<is_odd_impl> is_odd;
A couple of people have argued that for complex task it is OK to push the code to non-local scope. That is their point of view but of course it goes fundamentally against local functions principles (e.g., N2511) and the fact that even local functions can be long in some programming style (as expressed by others commenting before).
Lorenzo, I think you've done a good job of implementing the currently proposed library for inclusion. I do not doubt that your solution works and in your eyes it's as close to perfect as you can make it. BUT (and this is a big BUT), even after reading your documentation, I'm still unconvinced that local functions are an elegant solution to the problem they're purportedly meant to solve. C++ (and if I know my C correctly, even C) has gone fine without local functions. What C++11 already has right now is much more powerful than just local functions in lambdas. The only difference between a local function and a non-local function is where it's located. If the function is named appropriately and is located in a convenient location (i.e. logically outside any given function) I and the hundreds and thousands of other C++ programmers don't see what the problem is with non-location functions. One thing I do see that is a problem with local functions the way you implement it are: 1. They're not real functions. That means I cannot refer to it inside a function pointer. Which means the myriad libraries that take function pointers out there won't be able to leverage whatever local function objects made with your library. 2. They clutter up my current function's scope. Instead of being defined in-line like C++11 lambdas, Boost.Phoenix lambda's, Boost.Lambda lambda's, and Boost.Bind lambda's, they're being defined outside the point of usage. The difference between the function being defined in-line (as in C++11 lambdas) and not in-line is *HUGE* (difference between 0 and at least one) while the difference between a function defined in a function's scope and outside the function's scope is marginal at best. 3. I cannot, no matter how I look at your examples and the "logic" behind the concept of local functions, comprehend why this is a good way to organize code. Aren't we all past the phase yet of functions that have more than 10 lines of code? The advantages of having a function (a logical piece of code that's meant to be used as a unit *anyway*) at namespace scope or class scope is that re-usability is something you get for free. On the other hand, defining a local function with Boost.Local that I'd want to define as an external function at some point (because oh I don't know I want to use the same function somewhere else all of a sudden) will be too much work with all the macro voodoo that I have to wade through. I (and maybe others as well who follow the same logic I follow) don't see a large enough gap between C++11 lambdas and Boost.Phoenix/Lambda/Bind function objects that merits being addressed by local functions. Until you can convince us that local functions are "absolutely necessary" and that C++ should have it because it makes certain programming paradigms/techniques possible, I'm afraid what you have is a solution that's looking for a problem.
IMO, our library users should be felt the opportunity to program as they see more fit to themselves and their problem domain (long/short local functions, long/short global Phoenix functions, long/short Phoenix lambdas ( yes, even long Phoenix lambdas if the users want to) ).
I agree 100%. I still don't think though for your library to succeed that it has to be in Boost. Cheers -- Dean Michael Berris http://goo.gl/CKCJX

Dean Michael Berris wrote:
3. I cannot, no matter how I look at your examples and the "logic" behind the concept of local functions, comprehend why this is a good way to organize code. Aren't we all past the phase yet of functions that have more than 10 lines of code?
What are you trying to say here? Is there something wrong with functions having more than 10 lines of code? Looking at my pieces of code I'm most proud of, all have at least one function with more than 10 lines of code. One of this pieces of code was a cache optimal heap. So I looked whether there is any heap implementation in boost that doesn't have at least one function with more than 10 lines of code. There are surprisingly many different heap implementations in boost. All have at least one function with more than 10 lines of code (even if I count only the body and remove all comments/empty lines). What I'm trying to say here is that separation of concerns (which is a good way to organize code) can't be measured in lines of code. Regards, Thomas

On Thu, Nov 24, 2011 at 11:44 PM, Thomas Klimpel <Thomas.Klimpel@synopsys.com> wrote:
Dean Michael Berris wrote:
3. I cannot, no matter how I look at your examples and the "logic" behind the concept of local functions, comprehend why this is a good way to organize code. Aren't we all past the phase yet of functions that have more than 10 lines of code?
What are you trying to say here? Is there something wrong with functions having more than 10 lines of code? Looking at my pieces of code I'm most proud of, all have at least one function with more than 10 lines of code. One of this pieces of code was a cache optimal heap. So I looked whether there is any heap implementation in boost that doesn't have at least one function with more than 10 lines of code. There are surprisingly many different heap implementations in boost. All have at least one function with more than 10 lines of code (even if I count only the body and remove all comments/empty lines).
I think you're missing the forest for the trees here. What I'm saying is: If I had a choice between one function that contains another function that has N+M lines of code total versus two functions where one has N and the other has M lines of code, I'd say functions with less code is better -- or mathematically: For any (N,M) where N > 0 and M > 0, N < N+M and M < N+M. If we agree that having functions that have less lines of code is better than a function that has more lines of code for readability and maintainability, then there's no logical reason to prefer one function having more lines of code _when it itself contains another function that can otherwise stand alone_.
What I'm trying to say here is that separation of concerns (which is a good way to organize code) can't be measured in lines of code.
Sure, but readability and maintainability can be -- it's a physical fact: if you read one line of code at X milliseconds, then by the (simple) rule of multiplication more lines of code will take more time to read. I haven't met a person yet who can maintain code without reading it first. Cheers -- Dean Michael Berris http://goo.gl/CKCJX

On Nov 24, 2011, at 10:57 PM, Dean Michael Berris wrote:
I think you're missing the forest for the trees here.
What I'm saying is: If I had a choice between one function that contains another function that has N+M lines of code total versus two functions where one has N and the other has M lines of code, I'd say functions with less code is better -- or mathematically:
For any (N,M) where N > 0 and M > 0, N < N+M and M < N+M.
If we agree that having functions that have less lines of code is better than a function that has more lines of code for readability and maintainability, then there's no logical reason to prefer one function having more lines of code _when it itself contains another function that can otherwise stand alone_.
The flaw in your argument is that merely splitting a function in two doesn't automatically decrease the total amount of code that needs to be read; if anything, it *adds* to the total number of lines of code that have to be read because of the additional declarations that were introduced. Now, often when a function becomes long what you discover is that many of the parts make sense on their own and in that case it can improve clarity to split them off. However, when this doesn't come naturally there is no point in forcing it. There is another perfectly good way to break a long list of lines of codes into more comprehensible part: by taking each logical step in your function and inserting a few lines of code explaining what is about to happen and why, i.e. "Step 1: First, we do ...", "Step 2: Having done Step 1, we have X but now we need Y, so now we do ...". This both breaks up the long string of codes into smaller parts *and* provides a clear narrative of what is going on, without artificially introducing the additional unnecessary complexity of function barriers between the parts of the code. Along these lines, my favorite coding tool in the world is Leo: http://webpages.charter.net/edreamleo/front.html What makes Leo interesting is that it lets you structure *all* of your codebase as a tree with --- and this is the key point that makes it special among editors --- *arbitrary* granularity. So my personal solution for functions that are getting too long but for which it doesn't make sense to split the parts into separate functions is to structure the function as a tree with the parts being placed in named subnodes, ideally with a running commentary at the beginning of each subnode. Anyway, so in short I agree that it can often be useful to break large functions up into smaller functions, but I think that there is no point in being dogmatic about this. Break functions into smaller functions when it makes sense to do so, and when it doesn't just use comments to break up the code --- heck, if anything I figure that readers of your code are *more* likely to have code be broken up by a series of insightful comments forming a narrative than merely having it be split into smaller functions. :-) Cheers, Greg

On Fri, Nov 25, 2011 at 1:36 AM, Gregory Crosswhite <gcrosswhite@gmail.com> wrote:
On Nov 24, 2011, at 10:57 PM, Dean Michael Berris wrote:
I think you're missing the forest for the trees here.
What I'm saying is: If I had a choice between one function that contains another function that has N+M lines of code total versus two functions where one has N and the other has M lines of code, I'd say functions with less code is better -- or mathematically:
For any (N,M) where N > 0 and M > 0, N < N+M and M < N+M.
If we agree that having functions that have less lines of code is better than a function that has more lines of code for readability and maintainability, then there's no logical reason to prefer one function having more lines of code _when it itself contains another function that can otherwise stand alone_.
The flaw in your argument is that merely splitting a function in two doesn't automatically decrease the total amount of code that needs to be read; if anything, it *adds* to the total number of lines of code that have to be read because of the additional declarations that were introduced.
What are you talking about? There's *exactly* the same number of lines in a local function as there is with a class/namespace function. What am I missing?
Now, often when a function becomes long what you discover is that many of the parts make sense on their own and in that case it can improve clarity to split them off. However, when this doesn't come naturally there is no point in forcing it.
There is another perfectly good way to break a long list of lines of codes into more comprehensible part: by taking each logical step in your function and inserting a few lines of code explaining what is about to happen and why, i.e. "Step 1: First, we do ...", "Step 2: Having done Step 1, we have X but now we need Y, so now we do ...". This both breaks up the long string of codes into smaller parts *and* provides a clear narrative of what is going on, without artificially introducing the additional unnecessary complexity of function barriers between the parts of the code.
Along these lines, my favorite coding tool in the world is Leo:
http://webpages.charter.net/edreamleo/front.html
What makes Leo interesting is that it lets you structure *all* of your codebase as a tree with --- and this is the key point that makes it special among editors --- *arbitrary* granularity. So my personal solution for functions that are getting too long but for which it doesn't make sense to split the parts into separate functions is to structure the function as a tree with the parts being placed in named subnodes, ideally with a running commentary at the beginning of each subnode.
Anyway, so in short I agree that it can often be useful to break large functions up into smaller functions, but I think that there is no point in being dogmatic about this. Break functions into smaller functions when it makes sense to do so, and when it doesn't just use comments to break up the code --- heck, if anything I figure that readers of your code are *more* likely to have code be broken up by a series of insightful comments forming a narrative than merely having it be split into smaller functions. :-)
Sorry, but this sounds like you're not using C++ -- it sounds like you're using a different programming language that encourages this kind of programming, i.e. scripting or (worse) spaghetti code. A reasonably modern C++ approach is to define algorithms and data structures of your solution and use the control flow and concurrency mechanisms afforded by well-written libraries to achieve any sort of useful work. In the course of writing these programs typically you find concepts that arise that encourage generic programming, and dynamic structures that lend itself to good object oriented programming. You will also find that C++ already has a lot of pre-defined algorithms in the STL and in libraries like Boost where you mix and match and compose and eventually you have a solution from common /generic/ building blocks. C++ programmers have been programming without local functions since the beginning of C++, I don't see why (especially now) that we suddenly need local functions and (worse) implement it using macros. I think I'm still missing not entirely obvious to me here. Cheers -- Dean Michael Berris http://goo.gl/CKCJX

Dean Michael Berris wrote:
What are you talking about?
There's *exactly* the same number of lines in a local function as there is with a class/namespace function. What am I missing?
I'm actually also asking myself what I'm missing. Everybody seems to claim that a namespace function provides the same functionality as a local function. I know that I can use a "namespace functor" (i.e. a class that implements an "operator()" member function) instead of a local function. I guess I could also use Boost.Phoenix (or a similar library) to adapt a "namespace function" to provide the same functionality as a local function. Should I understand the claim that a "namespace function" provides the same functionality as Boost.Local in this way, or is there something simpler that I'm missing? Regards, Thomas

On Fri, Nov 25, 2011 at 2:33 AM, Thomas Klimpel <Thomas.Klimpel@synopsys.com> wrote:
Dean Michael Berris wrote:
What are you talking about?
There's *exactly* the same number of lines in a local function as there is with a class/namespace function. What am I missing?
I'm actually also asking myself what I'm missing. Everybody seems to claim that a namespace function provides the same functionality as a local function.
I know that I can use a "namespace functor" (i.e. a class that implements an "operator()" member function) instead of a local function. I guess I could also use Boost.Phoenix (or a similar library) to adapt a "namespace function" to provide the same functionality as a local function.
Should I understand the claim that a "namespace function" provides the same functionality as Boost.Local in this way, or is there something simpler that I'm missing?
namespace foo { void function_bar(int a, int b) { // do something with a & b } void function_baz() { function_bar(1, 2); } } as opposed to: namespace foo { void function_baz() { BOOST_LOCAL_FUNCTION(function_bar, (int a), (int b)) { // do something with a & b } BOOST_LOCAL_FUNCTION_END function_bar(1, 2); } } It's not that complicated. The first approach is the way we've been doing it with normal C++. It works. It's not broken. I don't see why we'd ever need Boost.Local at all. Cheers -- Dean Michael Berris http://goo.gl/CKCJX

Dean Michael Berris wrote:
There's *exactly* the same number of lines in a local function as there is with a class/namespace function. What am I missing?
I'm actually also asking myself what I'm missing. Everybody seems to claim that a namespace function provides the same functionality as a local function.
[...] It's not that complicated.
The first approach is the way we've been doing it with normal C++. It works. It's not broken.
I don't see why we'd ever need Boost.Local at all.
OK, now I understand what you are missing. An important part of the functionality of Boost.Local is to capture variables from the enclosing function. Some even argued that C++ will never have true local functions, because you still have to specify the names of the variables from the enclosing function that you want to capture. Of course, if you don't need to capture any variable from the enclosing function, using Boost.Local will only make your code more ugly. Regards, Thomas

On Fri, Nov 25, 2011 at 3:23 AM, Thomas Klimpel <Thomas.Klimpel@synopsys.com> wrote:
Dean Michael Berris wrote:
There's *exactly* the same number of lines in a local function as there is with a class/namespace function. What am I missing?
I'm actually also asking myself what I'm missing. Everybody seems to claim that a namespace function provides the same functionality as a local function.
[...] It's not that complicated.
The first approach is the way we've been doing it with normal C++. It works. It's not broken.
I don't see why we'd ever need Boost.Local at all.
OK, now I understand what you are missing. An important part of the functionality of Boost.Local is to capture variables from the enclosing function. Some even argued that C++ will never have true local functions, because you still have to specify the names of the variables from the enclosing function that you want to capture.
int i; bool b; long l; auto local_function = [i, b, l](std::string const & s) { // do what I want here. } // use local_function somewhere else Hmmm? Or better yet, works now: namespace foo { void non_local_function(int i, bool b, long l, std::string const & s) { // do what I want here. } void f(std::vector<double> const &v) { int i; bool b; long l; for_each(begin(v), end(v), bind(&non_local_function, i, b, l, _1)); } } See, no voodoo required here.
Of course, if you don't need to capture any variable from the enclosing function, using Boost.Local will only make your code more ugly.
I agree. Now, do I still miss anything? Cheers -- Dean Michael Berris http://goo.gl/CKCJX

On Thu, Nov 24, 2011 at 11:40 AM, Dean Michael Berris <mikhailberis@gmail.com> wrote:
On Fri, Nov 25, 2011 at 3:23 AM, Thomas Klimpel <Thomas.Klimpel@synopsys.com> wrote:
Dean Michael Berris wrote:
There's *exactly* the same number of lines in a local function as there is with a class/namespace function. What am I missing?
I'm actually also asking myself what I'm missing. Everybody seems to claim that a namespace function provides the same functionality as a local function.
[...] It's not that complicated.
The first approach is the way we've been doing it with normal C++. It works. It's not broken.
I don't see why we'd ever need Boost.Local at all.
OK, now I understand what you are missing. An important part of the functionality of Boost.Local is to capture variables from the enclosing function. Some even argued that C++ will never have true local functions, because you still have to specify the names of the variables from the enclosing function that you want to capture.
int i; bool b; long l; auto local_function = [i, b, l](std::string const & s) { // do what I want here. } // use local_function somewhere else
Hmmm?
Or better yet, works now:
namespace foo { void non_local_function(int i, bool b, long l, std::string const & s) { // do what I want here. }
void f(std::vector<double> const &v) { int i; bool b; long l; for_each(begin(v), end(v), bind(&non_local_function, i, b, l, _1)); } }
See, no voodoo required here.
Of course, if you don't need to capture any variable from the enclosing function, using Boost.Local will only make your code more ugly.
I agree.
Now, do I still miss anything?
Again, you are missing C++03 which will make your local_function example above not compile (obviously) and which is the point of this thread. HTH, --Lorenzo

On Fri, Nov 25, 2011 at 3:44 AM, Lorenzo Caminiti <lorcaminiti@gmail.com> wrote:
On Thu, Nov 24, 2011 at 11:40 AM, Dean Michael Berris <mikhailberis@gmail.com> wrote:
int i; bool b; long l; auto local_function = [i, b, l](std::string const & s) { // do what I want here. } // use local_function somewhere else
Hmmm?
Or better yet, works now:
namespace foo { void non_local_function(int i, bool b, long l, std::string const & s) { // do what I want here. }
void f(std::vector<double> const &v) { int i; bool b; long l; for_each(begin(v), end(v), bind(&non_local_function, i, b, l, _1)); } }
See, no voodoo required here.
Of course, if you don't need to capture any variable from the enclosing function, using Boost.Local will only make your code more ugly.
I agree.
Now, do I still miss anything?
Again, you are missing C++03 which will make your local_function example above not compile (obviously) and which is the point of this thread.
But I did show a solution that works in both C++03 and C++11 which is to use bind to do it and a non-local function. Phoenix already enables doing magical things for in-line lambda's. Actually in all honesty, for any function that's going to be more than a couple of instructions long, as far as good engineering practice is concerned (and of course YMMV) you're going to want to make it an external function anyway so that: 1. You can test it. 2. You can make it externally linked. 3. You can use it again somewhere else (for free, you don't really have to if you don't want to). HTH -- Dean Michael Berris http://goo.gl/CKCJX

Dean Michael Berris wrote:
Or better yet, works now:
namespace foo { void non_local_function(int i, bool b, long l, std::string const & s) { // do what I want here. }
void f(std::vector<double> const &v) { int i; bool b; long l; for_each(begin(v), end(v), bind(&non_local_function, i, b, l, _1)); } }
See, no voodoo required here.
To be honest, this one really looks nice and convincing. I find it even looks nicer than the C++11 Lambda. And as you wrote in response to Lorenzo:
But I did show a solution that works in both C++03 and C++11 which is to use bind to do it and a non-local function. Phoenix already enables doing magical things for in-line lambda's.
Actually in all honesty, for any function that's going to be more than a couple of instructions long, as far as good engineering practice is concerned (and of course YMMV) you're going to want to make it an external function anyway so that:
1. You can test it. 2. You can make it externally linked. 3. You can use it again somewhere else (for free, you don't really have to if you don't want to).
I guess the compile and runtime performance will also not be worse than Boost.Local or Boost.Phoenix. (I just looked at the comparison chart in the documentation of Boost.Local, but it doesn't seem to include this version.) You know what, I'm really happy now that I didn't participate in the review of Boost.Local! I'm no longer sure now whether it adds significant value or not. I have seen enough places where Boost.ScopeExit was the right solution, but these were one liners.
Of course, if you don't need to capture any variable from the enclosing function, using Boost.Local will only make your code more ugly.
I agree.
Now, do I still miss anything?
No, you got everything correct now. Regards, Thomas

On Thu, Nov 24, 2011 at 12:56 PM, Thomas Klimpel <Thomas.Klimpel@synopsys.com> wrote:
Dean Michael Berris wrote:
Or better yet, works now:
namespace foo { void non_local_function(int i, bool b, long l, std::string const & s) { // do what I want here. }
void f(std::vector<double> const &v) { int i; bool b; long l; for_each(begin(v), end(v), bind(&non_local_function, i, b, l, _1)); } }
See, no voodoo required here.
To be honest, this one really looks nice and convincing. I find it even looks nicer than the C++11 Lambda. And as you wrote in response to Lorenzo:
But I did show a solution that works in both C++03 and C++11 which is to use bind to do it and a non-local function. Phoenix already enables
As you say this solution, which works on both C++03 and C++11, does not implement a local function. N2511 indicates one prospective arguing maintenance and readability benefits when the function can be declared locally, e.g., just above the for_each in this case. That is not possible in your example. That's what Boost.Local does instead. (Note that with your example above you also explicitly specified the bound types but I'm sure you can workaround that with templates so I'm not arguing this point as an issue.) Now, it is really OK if you think that local functions are not needed. IMO, as a user you should be fully empowered to use or not a library based on your personal preferences and the needs of your problem domain. As for the library under review, as I said, the reviewers have directly spoken to the question of whether local functions are or not useful:
- What is your evaluation of the potential usefulness of the library?
HTH, --Lorenzo

On Fri, Nov 25, 2011 at 7:36 AM, Lorenzo Caminiti <lorcaminiti@gmail.com> wrote:
On Thu, Nov 24, 2011 at 12:56 PM, Thomas Klimpel <Thomas.Klimpel@synopsys.com> wrote:
Dean Michael Berris wrote:
Or better yet, works now:
namespace foo { void non_local_function(int i, bool b, long l, std::string const & s) { // do what I want here. }
void f(std::vector<double> const &v) { int i; bool b; long l; for_each(begin(v), end(v), bind(&non_local_function, i, b, l, _1)); } }
See, no voodoo required here.
To be honest, this one really looks nice and convincing. I find it even looks nicer than the C++11 Lambda. And as you wrote in response to Lorenzo:
But I did show a solution that works in both C++03 and C++11 which is to use bind to do it and a non-local function. Phoenix already enables
As you say this solution, which works on both C++03 and C++11, does not implement a local function. N2511 indicates one prospective arguing maintenance and readability benefits when the function can be declared locally, e.g., just above the for_each in this case. That is not possible in your example. That's what Boost.Local does instead. (Note that with your example above you also explicitly specified the bound types but I'm sure you can workaround that with templates so I'm not arguing this point as an issue.)
Am I missing something here, N2511 was sent in 2008 and lambda's already are in C++11 which already encompass the capabilities defined in N2511. Why do we keep coming back to N2511 when C++11 is already out? The first assertion that I have an issue with is that "we need local functions". C++03 and certainly C++11 are *fine* without local functions. I have yet to see a convincing argument for that basic assumption.
Now, it is really OK if you think that local functions are not needed.
Which I do think they're not needed.
IMO, as a user you should be fully empowered to use or not a library based on your personal preferences and the needs of your problem domain.
Indeed. I don't discount this fact.
As for the library under review, as I said, the reviewers have directly spoken to the question of whether local functions are or not useful:
- What is your evaluation of the potential usefulness of the library?
If you really want to know, I think there's absolutely 0 potential usefulness of this library given what's already there in C++03 and C++11 to be included in Boost. Cheers -- Dean Michael Berris http://goo.gl/CKCJX

On Thu, Nov 24, 2011 at 9:58 PM, Dean Michael Berris <mikhailberis@gmail.com> wrote:
On Fri, Nov 25, 2011 at 7:36 AM, Lorenzo Caminiti <lorcaminiti@gmail.com> wrote:
On Thu, Nov 24, 2011 at 12:56 PM, Thomas Klimpel <Thomas.Klimpel@synopsys.com> wrote:
Dean Michael Berris wrote:
Or better yet, works now:
namespace foo { void non_local_function(int i, bool b, long l, std::string const & s)
Actually, I have tried to rework this example so I don't have to specify the type of i, b, l and I wasn't able too (without starting to re-implement the type deduction mechanisms that Local uses). Can you please show me how you will do that without the explicit bound types? (That will make maintenance easier given that you don't need to change the "local function" if you change the bound variable types which is true for any closure.)
{ // do what I want here. }
void f(std::vector<double> const &v) { int i; bool b; long l; for_each(begin(v), end(v), bind(&non_local_function, i, b, l, _1)); } }
See, no voodoo required here.
To be honest, this one really looks nice and convincing. I find it even looks nicer than the C++11 Lambda. And as you wrote in response to Lorenzo:
But I did show a solution that works in both C++03 and C++11 which is to use bind to do it and a non-local function. Phoenix already enables
As you say this solution, which works on both C++03 and C++11, does not implement a local function. N2511 indicates one prospective arguing maintenance and readability benefits when the function can be declared locally, e.g., just above the for_each in this case. That is not possible in your example. That's what Boost.Local does instead. (Note that with your example above you also explicitly specified the bound types but I'm sure you can workaround that with templates so I'm not arguing this point as an issue.)
Am I missing something here, N2511 was sent in 2008 and lambda's already are in C++11 which already encompass the capabilities defined in N2511. Why do we keep coming back to N2511 when C++11 is already out?
For C++03 which does not support lambdas. Again there other Boost libraries that implement C++11 features for C++03, why would the situation be different for Local?
- What is your evaluation of the potential usefulness of the library?
If you really want to know, I think there's absolutely 0 potential usefulness of this library given what's already there in C++03 and
It's not already there in C++03 because in C++03 you cannot 1) declare a function locally, 2) binding variables, and 3) using statement syntax for its body. However, I see you statement that yo don't care about 1-2-3. So the point it is not that it's that 1-2-3 are already in C++03, because they are not, it is instead that you (personally) don't need 1-2-3.
C++11 to be included in Boost.
Thanks. --Lorenzo

On Fri, Nov 25, 2011 at 9:33 PM, Lorenzo Caminiti <lorcaminiti@gmail.com> wrote:
On Thu, Nov 24, 2011 at 9:58 PM, Dean Michael Berris <mikhailberis@gmail.com> wrote:
namespace foo { void non_local_function(int i, bool b, long l, std::string const & s)
Actually, I have tried to rework this example so I don't have to specify the type of i, b, l and I wasn't able too (without starting to re-implement the type deduction mechanisms that Local uses). Can you please show me how you will do that without the explicit bound types? (That will make maintenance easier given that you don't need to change the "local function" if you change the bound variable types which is true for any closure.)
Phoenix functions. Look it up. 'nuf said.
Am I missing something here, N2511 was sent in 2008 and lambda's already are in C++11 which already encompass the capabilities defined in N2511. Why do we keep coming back to N2511 when C++11 is already out?
For C++03 which does not support lambdas. Again there other Boost libraries that implement C++11 features for C++03, why would the situation be different for Local?
Precisely because there are already libraries in Boost that do exactly what Local does, which is why it makes 0 sense to have yet another one. Phoenix v3 can supersede Boost.Lambda now as it's a superset of Boost.Lambda functionality. Now if it was the case the Boost didn't have Phoenix, Bind, and Lambda, and that C++11 doesn't have lambda's built into the language, and if you couldn't absolutely program in C++ effectively without local functions, then maybe Boost.Local would make sense as a very narrow but still I would argue marginally useful library. But the situation now is different which is why you're getting opposition at all.
If you really want to know, I think there's absolutely 0 potential usefulness of this library given what's already there in C++03 and
It's not already there in C++03 because in C++03 you cannot 1) declare a function locally, 2) binding variables, and 3) using statement syntax for its body. However, I see you statement that yo don't care about 1-2-3. So the point it is not that it's that 1-2-3 are already in C++03, because they are not, it is instead that you (personally) don't need 1-2-3.
It's not that I personally don't need 1-2-3, it's that thousands and thousands of C++ programmers have lived without local function approximations -- and for those that do want in-lined function generation Boost already has Phoenix. Heck even before Phoenix/Bind people (including me) have been creating function objects to be used as arguments for STL algorithms, and guess what: it works and it's fine. If the rejection of N2511 in favor of the current lambda is any indication, even the committee finds that local functions an unnecessary complication. What makes you think that bringing N2511 back into C++03 would even make sense especially since there are already approximations to C++11 lambda's? It's logic really: 1) C++11 lambda > N2511 2) Since Phoenix ~ (C++11 lambda + more) and Local ~ N2511 3) Therefore, Phoenix > N2511 4) QED No? Cheers PS. I am in no way a contributor nor author of *anything* in Phoenix. -- Dean Michael Berris http://goo.gl/CKCJX

On 25 Nov 2011, at 11:01, Dean Michael Berris wrote:
If the rejection of N2511 in favor of the current lambda is any indication, even the committee finds that local functions an unnecessary complication. What makes you think that bringing N2511 back into C++03 would even make sense especially since there are already approximations to C++11 lambda's?
It's logic really:
1) C++11 lambda > N2511 2) Since Phoenix ~ (C++11 lambda + more) and Local ~ N2511 3) Therefore, Phoenix > N2511 4) QED
No?
No. You seem simply unwilling to accept that there are people who refuse to use Pheonix, and libraries built on it because: 1) It leads to horribly large compile times. 2) Error messages. I know you hate this, so let me write out my opinion one last time. Phoenix leads to error messages which: * On the compilers I commonly use - I can't change this, there aren't that many compilers around. * Are very, very large * and require different skills to read and understand than any other library. And therefore, I choose not to use it, as developing code using these libraries is much more difficult than the alternatives, even after I spend time trying to understand their idiosyncrasies. You seem to think that anyone who doesn't agree with you on this point is stupid, or just "doesn't get it". I have written several large parsers using boost::spirit. They had great performance, and once the code was written they looked very nice to read. However, I have still removed those parsers, and will not write any more (after I have re-evaluated the current root of spirit/phoenix, to see if error messages have improved in the last 6 months or so). I have spent a lot of time reading C++ error messages, and writing large C++ libraries. I have contributed quite a few patches to boost. Personally, having now taken the time to full evaluate local (which I wouldn't have done without this discussion), I believe it is a valuable C++ library, which I will make use of during the C++03 -> C++11 migration period (which I expect to take several years, given clang still doesn't support lambdas today). Chris

On Fri, Nov 25, 2011 at 10:16 PM, Christopher Jefferson <chris@bubblescope.net> wrote:
On 25 Nov 2011, at 11:01, Dean Michael Berris wrote:
If the rejection of N2511 in favor of the current lambda is any indication, even the committee finds that local functions an unnecessary complication. What makes you think that bringing N2511 back into C++03 would even make sense especially since there are already approximations to C++11 lambda's?
It's logic really:
1) C++11 lambda > N2511 2) Since Phoenix ~ (C++11 lambda + more) and Local ~ N2511 3) Therefore, Phoenix > N2511 4) QED
No?
No.
Of course, my bad. I don't have my compiler handy, what was I thinking?! 1) C++11 lambda > N2511 2) Since Phoenix ~ (C++11 lambda + more) and Local ~ N2511 3) Therefore Phoenix > Local 4) QED
You seem simply unwilling to accept that there are people who refuse to use Pheonix, and libraries built on it because:
Wait, did I reject that people refuse to use Phoenix? Are you putting words in my mouth? Nobody -- I repeat hoping that it sinks in somehow -- as in ABSOLUTELY NOBODY is forcing anybody to use Phoenix. If you don't want in-lined lambdas defined using C++03 syntax that approximates C++11 lambda's, then don't use it. It's that simple.
1) It leads to horribly large compile times.
Compilers suck at compiling TMP. Compilers need to get better at it.
2) Error messages. I know you hate this, so let me write out my opinion one last time.
Phoenix leads to error messages which: * On the compilers I commonly use - I can't change this, there aren't that many compilers around. * Are very, very large * and require different skills to read and understand than any other library.
Wait, doesn't Phoenix have tests that compile? I hope you're not saying Phoenix is broken that people who use it automatically encounter these horrible error messages with their favorite compiler even if their code is correct. Or do the error messages only ever show up when you use Phoenix incorrectly? So you're saying it takes different skills to read other than the same skills you use to read? I absolutely don't get this.
And therefore, I choose not to use it, as developing code using these libraries is much more difficult than the alternatives, even after I spend time trying to understand their idiosyncrasies.
Everyone is entitled to their own opinion. I'm not forcing you or anybody to use Phoenix, I'm saying that Boost.Local provides marginal benefit at best. There's nobody stopping you from using Boost.Local either but I don't necessarily think it has to be in Boost for it to be used by people who want to use it.
You seem to think that anyone who doesn't agree with you on this point is stupid, or just "doesn't get it".
What makes you think that? Have I called anybody stupid? What's happening is that people are just disagreeing but don't give clarity to their positions with *logic*.
I have written several large parsers using boost::spirit. They had great performance, and once the code was written they looked very nice to read. However, I have still removed those parsers, and will not write any more (after I have re-evaluated the current root of spirit/phoenix, to see if error messages have improved in the last 6 months or so).
I don't see why this is relevant to the discussion. This is your personal preference and unfortunately logic doesn't care what you like or don't like.
I have spent a lot of time reading C++ error messages, and writing large C++ libraries. I have contributed quite a few patches to boost.
Good for you then.
Personally, having now taken the time to full evaluate local (which I wouldn't have done without this discussion), I believe it is a valuable C++ library, which I will make use of during the C++03 -> C++11 migration period (which I expect to take several years, given clang still doesn't support lambdas today).
Good for you. I still don't think it should be in Boost though for reasons I've already mentioned several times. Have a good day. Cheers -- Dean Michael Berris http://goo.gl/CKCJX

On 25 Nov 2011, at 11:38, Dean Michael Berris wrote:
On Fri, Nov 25, 2011 at 10:16 PM, Christopher Jefferson <chris@bubblescope.net> wrote:
On 25 Nov 2011, at 11:01, Dean Michael Berris wrote:
If the rejection of N2511 in favor of the current lambda is any indication, even the committee finds that local functions an unnecessary complication. What makes you think that bringing N2511 back into C++03 would even make sense especially since there are already approximations to C++11 lambda's?
It's logic really:
1) C++11 lambda > N2511 2) Since Phoenix ~ (C++11 lambda + more) and Local ~ N2511 3) Therefore, Phoenix > N2511 4) QED
No?
No.
Of course, my bad. I don't have my compiler handy, what was I thinking?!
1) C++11 lambda > N2511 2) Since Phoenix ~ (C++11 lambda + more) and Local ~ N2511 3) Therefore Phoenix > Local 4) QED
This will be my last comment in this thread :) Your flaw is that Pheonix ~ (C++11 lambda + more), as Phoenix has significant disadvantages compared to C++11 lambda (that have been discussed at length), as well as significant advantages (as you say). Therefore Phoenix is incomparable with C++11 lambda, so Phoenix is comparable with Local. Chris

On Fri, Nov 25, 2011 at 6:16 AM, Christopher Jefferson <chris@bubblescope.net> wrote:
Personally, having now taken the time to full evaluate local (which I wouldn't have done without this discussion), I believe it is a valuable C++ library, which I will make use of during the C++03 -> C++11 migration period (which I expect to take several years, given clang still doesn't support lambdas today).
Is there any other "modern" compiler a part from clang out there that still does not support lambbas? [In my case the limitation comes from the fact that I can't yet use the version of the compiler with lambdas because it has to be certified by a 3rd party before I can start using it (which in my case might take ~3 years).] --Lorenzo

On 11/25/2011 7:16 PM, Christopher Jefferson wrote:
You seem simply unwilling to accept that there are people who refuse to use Pheonix, and libraries built on it because:
1) It leads to horribly large compile times. 2) Error messages. I know you hate this, so let me write out my opinion one last time.
Phoenix leads to error messages which: * On the compilers I commonly use - I can't change this, there aren't that many compilers around. * Are very, very large * and require different skills to read and understand than any other library.
Ok, let's put that to a test. Assumptions: 1) You are using a phoenix function 2) Your lambda is solely for capturing locals, and should be the basic counterpart of how you do it using Locals (i.e. no fancy phoenix expressions -- we restrict ourself to simple capturing of locals). Here's a sample code: http://pastebin.com/t3nW4SFY I set it up so you can disable phoenix and test the net time. Here's what I get: with Phoenix: start time: 19:44:57.73 end time: 19:44:59.69 : 2.0 secs without Phoenix: start time: 19:44:29.07 end time: 19:44:30.81 : 1.7 secs Now, how about error messages. Here's the lambda part: std::for_each(vec.begin(), vec.end(), plus4(_1, b, l, s)); You'd have to be absurdly dumb to get that wrong! A more realistic scenario is a mistake in the function body itself. In which case (try it out), you'll get the same errors as plain-'ole C++. 1) Leads to horribly large compile times? Not if you keep your phoenix expressions simple. 2) Error messages? Again, none if you keep your phoenix expressions simple. And "simple" is the point. Locals does not have complex expressions anyway. The complexity is in the function body. THAT is the use case of Locals and THAT is one of the cases where phoenix really shines. So, gentlemen, please, let's keep the FUD to a minimum. Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com

On Fri, Nov 25, 2011 at 7:24 AM, Joel de Guzman <joel@boost-consulting.com> wrote:
On 11/25/2011 7:16 PM, Christopher Jefferson wrote:
You seem simply unwilling to accept that there are people who refuse to use Pheonix, and libraries built on it because:
1) It leads to horribly large compile times. 2) Error messages. I know you hate this, so let me write out my opinion one last time.
Phoenix leads to error messages which: * On the compilers I commonly use - I can't change this, there aren't that many compilers around. * Are very, very large * and require different skills to read and understand than any other library.
Ok, let's put that to a test.
Assumptions: 1) You are using a phoenix function 2) Your lambda is solely for capturing locals, and should be the basic counterpart of how you do it using Locals (i.e. no fancy phoenix expressions -- we restrict ourself to simple capturing of locals).
Here's a sample code:
And the example can be modified to use templates so not to explicitly specify the bound types: #include <boost/phoenix/core.hpp> #include <boost/phoenix/function.hpp> #include <iostream> #include <string> #include <vector> #include <algorithm> namespace impl { template<typename B, typename L, typename S> void plus4(int i, B b, L l, S s) { std::cout << (i + l) << std::endl; } } BOOST_PHOENIX_ADAPT_FUNCTION(void, plus4, impl::plus4, 4) int main() { using boost::phoenix::arg_names::_1; bool b = true; long l = 999; std::string const s = "Hello"; std::vector<int> vec; vec.push_back(1); vec.push_back(2); vec.push_back(3); std::for_each(vec.begin(), vec.end(), plus4(_1, b, l, s)); } As I said before, what it is missing here is that plus4 is not defined at local scope so it is not a local function. However, I recognize and understand that in your opinion there is no need to declare functions at local scope using statement syntax (in your opinion that can be done outside the function scope like in the example above).
I set it up so you can disable phoenix and test the net time. Here's what I get:
with Phoenix: start time: 19:44:57.73 end time: 19:44:59.69 : 2.0 secs
without Phoenix: start time: 19:44:29.07 end time: 19:44:30.81 : 1.7 secs
Now, how about error messages. Here's the lambda part:
std::for_each(vec.begin(), vec.end(), plus4(_1, b, l, s));
You'd have to be absurdly dumb to get that wrong! A more realistic scenario is a mistake in the function body itself. In which case (try it out), you'll get the same errors as plain-'ole C++.
1) Leads to horribly large compile times? Not if you keep your phoenix expressions simple. 2) Error messages? Again, none if you keep your phoenix expressions simple.
And "simple" is the point. Locals does not have complex expressions anyway. The complexity is in the function body. THAT is the use case of Locals and THAT is one of the cases where phoenix really shines.
So, gentlemen, please, let's keep the FUD to a minimum.
--Lorenzo

On 25 Nov 2011, at 12:24, Joel de Guzman wrote:
On 11/25/2011 7:16 PM, Christopher Jefferson wrote:
You seem simply unwilling to accept that there are people who refuse to use Pheonix, and libraries built on it because:
1) It leads to horribly large compile times. 2) Error messages. I know you hate this, so let me write out my opinion one last time.
Phoenix leads to error messages which: * On the compilers I commonly use - I can't change this, there aren't that many compilers around. * Are very, very large * and require different skills to read and understand than any other library.
Ok, let's put that to a test.
Sorry to ask you another problem, but could you try another test? The following code shows a small example, which comes from a real program of mine. I have changed my old out-of-line code to use boost local. Note that in practice there are many different types which implement my 'vars' interface (getMax/getMin/setMax/setMin). #include <iostream> #include <string> #include <vector> #include <algorithm> #include "local/boost/local/function.hpp" template<typename T> struct Var { void setMax(int i) {} void setMin(int i) {} int getMax() { return 0; } int getMin() { return 0; } }; template<typename T> void squeeze_vars_local(std::vector<T>& vec, int i) { void BOOST_LOCAL_FUNCTION_PARAMS_TPL(T& t, bind i) { t.first.setMax(t.second.getMax() - i); t.second.setMin(t.first.getMin() + i); } BOOST_LOCAL_FUNCTION_NAME(squeeze); std::for_each(vec.begin(), vec.end(), squeeze); } int main(void) { std::vector<std::pair<Var<int>, Var<double> > > v; squeeze_vars_local(v,2); } In the past I tried turning this into phoenix code, and failed. I'd be genuinely interested to know what it would look like as a phoenix function. Chris

On 11/25/2011 9:21 PM, Christopher Jefferson wrote:
Sorry to ask you another problem, but could you try another test?
The following code shows a small example, which comes from a real program of mine. I
have changed my old out-of-line code to use boost local. Note that in practice there are many different types which implement my 'vars' interface (getMax/getMin/setMax/setMin).
#include <iostream> #include <string> #include <vector> #include <algorithm> #include "local/boost/local/function.hpp"
template<typename T> struct Var { void setMax(int i) {} void setMin(int i) {} int getMax() { return 0; } int getMin() { return 0; } };
template<typename T> void squeeze_vars_local(std::vector<T>& vec, int i) {
void BOOST_LOCAL_FUNCTION_PARAMS_TPL(T& t, bind i) { t.first.setMax(t.second.getMax() - i); t.second.setMin(t.first.getMin() + i); } BOOST_LOCAL_FUNCTION_NAME(squeeze);
std::for_each(vec.begin(), vec.end(), squeeze); }
int main(void) { std::vector<std::pair<Var<int>, Var<double> > > v; squeeze_vars_local(v,2); }
In the past I tried turning this into phoenix code, and failed. I'd be genuinely interested to know what it would look like as a phoenix function.
Here's the phoenix functions rendition of that: http://pastebin.com/QWnHiKkv Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com

On 11/25/2011 9:58 PM, Joel de Guzman wrote:
In the past I tried turning this into phoenix code, and failed. I'd be genuinely interested to know what it would look like as a phoenix function.
Here's the phoenix functions rendition of that:
Oops, T1 should be a reference. One more try: http://pastebin.com/dcriF8FA Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com

On 25 Nov 2011, at 14:04, Joel de Guzman wrote:
On 11/25/2011 9:58 PM, Joel de Guzman wrote:
In the past I tried turning this into phoenix code, and failed. I'd be genuinely interested to know what it would look like as a phoenix function.
Here's the phoenix functions rendition of that:
Oops, T1 should be a reference. One more try:
Thank you, but I was thinking more doing the whole thing in-line (else, I would just write a function object, much like you did, without the phoenix), or use std::bind. Of course, it might be the case that writing that inline is just more pain than it is worth. Certainly it would get a bit long hanging off a for-each! Chris

On 11/25/2011 10:26 PM, Christopher Jefferson wrote:
On 25 Nov 2011, at 14:04, Joel de Guzman wrote:
On 11/25/2011 9:58 PM, Joel de Guzman wrote:
In the past I tried turning this into phoenix code, and failed. I'd be genuinely interested to know what it would look like as a phoenix function.
Here's the phoenix functions rendition of that:
Oops, T1 should be a reference. One more try:
Thank you, but I was thinking more doing the whole thing in-line (else, I would just write a function object, much like you did, without the phoenix), or use std::bind.
Of course, it might be the case that writing that inline is just more pain than it is worth. Certainly it would get a bit long hanging off a for-each!
Well, the point of the exercise is to use "statement syntax", just as you did using Locals. By doing so, you lessen the chance of having "outrageous error messages" and minimize compile time. There are advantages with phoenix functions over plain function objects, but I do not want to digress. At the very least, you have easy binding of locals. Also, it's just as succinct and clear as your Locals version. Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com

On 11/25/2011 10:40 PM, Joel de Guzman wrote:
On 11/25/2011 10:26 PM, Christopher Jefferson wrote:
Thank you, but I was thinking more doing the whole thing in-line (else, I would just write a function object, much like you did, without the phoenix), or use std::bind.
Of course, it might be the case that writing that inline is just more pain than it is worth. Certainly it would get a bit long hanging off a for-each!
Well, the point of the exercise is to use "statement syntax", just as you did using Locals. By doing so, you lessen the chance of having "outrageous error messages" and minimize compile time.
There are advantages with phoenix functions over plain function objects, but I do not want to digress. At the very least, you have easy binding of locals. Also, it's just as succinct and clear as your Locals version.
Oh, and BTW, speaking of std::bind, you're also at the same risk when it comes to spewing error messages as with phoenix. Like phoenix, std::bind is also a TMP library and is not immune to bad error messages. Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com

On 25 Nov 2011, at 14:40, Joel de Guzman wrote:
On 11/25/2011 10:26 PM, Christopher Jefferson wrote:
On 25 Nov 2011, at 14:04, Joel de Guzman wrote:
On 11/25/2011 9:58 PM, Joel de Guzman wrote:
In the past I tried turning this into phoenix code, and failed. I'd be genuinely interested to know what it would look like as a phoenix function.
Here's the phoenix functions rendition of that:
Oops, T1 should be a reference. One more try:
Thank you, but I was thinking more doing the whole thing in-line (else, I would just write a function object, much like you did, without the phoenix), or use std::bind.
Of course, it might be the case that writing that inline is just more pain than it is worth. Certainly it would get a bit long hanging off a for-each!
Well, the point of the exercise is to use "statement syntax", just as you did using Locals. By doing so, you lessen the chance of having "outrageous error messages" and minimize compile time.
Then, there is a misunderstanding on the purpose of boost::local (in my opinion), which I apologise for not making clear! The reason I would boost::local is the same as C++11 lamdas, to be able to declare (small) functions at the point where they are used, rather than out-of-line. Of course, in my very reduced example, the function may as well have been declared out-of-line, as the function is so short. Certainly if we remove from boost::local the advantage of declaring new functions in the middle of existing functions, then it has almost no purpose in existing. It is these larger functions where life gets painful with phoenix. So, in my opinion, boost's local place for existence is for functions which: * We don't want to define out-of-line * Which are slightly too big for comfortable phoenixing (so not just _1 + 1, or cout << _1) * For people can't assume a C++11 compiler with lambdas. Is that a big enough area? I'm not positive. It seems much of this discussion is turning into what boost 'should be'. That's an interesting argument which might be better had without boost::local hanging around. One of the biggest problems with C++ (in my opinion) is that it is a pain to distribute and use libraries, as there is no nice library package manager, so boost is currently the closest thing there is to a C++ package library. A better package manager might alleviate that pressure, to allow boost to become "whatever it wants to be". Chris

Christopher Jefferson wrote:
Thank you, but I was thinking more doing the whole thing in-line (else, I would just write a function object, much like you did, without the phoenix), or use std::bind.
std::bind with a function template doesn't work without a cast though. Would it not be cool if we get automatic lambdaization of overload sets in C++38, taking care of this, by then, non-problem.

Joel de Guzman-2 wrote
On 11/25/2011 7:16 PM, Christopher Jefferson wrote:
You seem simply unwilling to accept that there are people who refuse to use Pheonix, and libraries built on it because:
1) It leads to horribly large compile times. 2) Error messages. I know you hate this, so let me write out my opinion one last time.
Phoenix leads to error messages which: * On the compilers I commonly use - I can't change this, there aren't that many compilers around. * Are very, very large * and require different skills to read and understand than any other library.
Ok, let's put that to a test.
Assumptions: 1) You are using a phoenix function 2) Your lambda is solely for capturing locals, and should be the basic counterpart of how you do it using Locals (i.e. no fancy phoenix expressions -- we restrict ourself to simple capturing of locals).
Here's a sample code:
I set it up so you can disable phoenix and test the net time. Here's what I get:
with Phoenix: start time: 19:44:57.73 end time: 19:44:59.69 : 2.0 secs
without Phoenix: start time: 19:44:29.07 end time: 19:44:30.81 : 1.7 secs
Now, how about error messages. Here's the lambda part:
std::for_each(vec.begin(), vec.end(), plus4(_1, b, l, s));
You'd have to be absurdly dumb to get that wrong! A more realistic scenario is a mistake in the function body itself. In which case (try it out), you'll get the same errors as plain-'ole C++.
1) Leads to horribly large compile times? Not if you keep your phoenix expressions simple. 2) Error messages? Again, none if you keep your phoenix expressions simple.
And "simple" is the point. Locals does not have complex expressions anyway. The complexity is in the function body. THAT is the use case of Locals and THAT is one of the cases where phoenix really shines.
Hi, Joel your arguments have convinced me that maybe we don't need Boost.Local. Best, Vicente -- View this message in context: http://boost.2283326.n4.nabble.com/New-libraries-implementing-C-11-features-... Sent from the Boost - Dev mailing list archive at Nabble.com.

AMDG On 11/25/2011 04:24 AM, Joel de Guzman wrote:
Now, how about error messages. Here's the lambda part:
std::for_each(vec.begin(), vec.end(), plus4(_1, b, l, s));
You'd have to be absurdly dumb to get that wrong!
You keep saying this, but I find it really hard to believe. My experience is that *nothing* is so simple that you can't get it wrong. In Christ, Steven Watanabe

On 11/25/2011 11:49 PM, Steven Watanabe wrote:
AMDG
On 11/25/2011 04:24 AM, Joel de Guzman wrote:
Now, how about error messages. Here's the lambda part:
std::for_each(vec.begin(), vec.end(), plus4(_1, b, l, s));
You'd have to be absurdly dumb to get that wrong!
You keep saying this, but I find it really hard to believe. My experience is that *nothing* is so simple that you can't get it wrong.
Ok, agreed. Pardon the wording. It's a passionate exaggeration. Let me rephrase that into something more positive: std::for_each(vec.begin(), vec.end(), plus4(_1, b, l, s)); --> It's reasonably easy to get that right. Modest enough? Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com

Joel de Guzman wrote:
Now, how about error messages. Here's the lambda part:
std::for_each(vec.begin(), vec.end(), plus4(_1, b, l, s));
You'd have to be absurdly dumb to get that wrong!
I encountered the following ways to get such a thing wrong (with Boost.Bind) in the past: 1) I included both Boost.Lambda and Boost.Bind, and somehow managed that the compiler used the Boost.Lambda placeholders instead of the Boost.Bind placeholders. This produced a weird error message, if I remember correctly. 2) Make something wrong related to the number of arguments (I don't remember exactly details anymore). Got an internal compiler error from MSVC8 instead of an error message. At least the compiler crash once took me a long time to find out what I did wrong, even so it was something really obviously wrong (after I found it). I guess it's better now with Boost.Phoenix. However, perhaps interactions between std::bind and Boost.Phoenix similar to the interactions between Boost.Bind and Boost.Lambda will also generate a similar experience for newer generations of programmers. Regards, Thomas

On 11/26/2011 12:34 AM, Thomas Klimpel wrote:
Joel de Guzman wrote:
Now, how about error messages. Here's the lambda part:
std::for_each(vec.begin(), vec.end(), plus4(_1, b, l, s));
You'd have to be absurdly dumb to get that wrong!
I encountered the following ways to get such a thing wrong (with Boost.Bind) in the past: 1) I included both Boost.Lambda and Boost.Bind, and somehow managed that the compiler used the Boost.Lambda placeholders instead of the Boost.Bind placeholders. This produced a weird error message, if I remember correctly. 2) Make something wrong related to the number of arguments (I don't remember exactly details anymore). Got an internal compiler error from MSVC8 instead of an error message.
At least the compiler crash once took me a long time to find out what I did wrong, even so it was something really obviously wrong (after I found it).
I guess it's better now with Boost.Phoenix. However, perhaps interactions between std::bind and Boost.Phoenix similar to the interactions between Boost.Bind and Boost.Lambda will also generate a similar experience for newer generations of programmers.
Indeed. Anyway, as I said in my reply to Steven. I'm sorry about that insensitive remark. It's an exaggeration. That said, I would say that it's easier than the bind syntax because it's just like a function call, except for the placeholders. It is polymorphic and as Peter Dimov said, requires no casts for plain overloaded functions as well as template functions. IME, it's a common complaint with bind not being able to *easily* call overloaded and template functions. If I were to write "Effective Phoenix", I'd start with phoenix functions. They are simple and yet very powerful critters. We've focused too much on esoteric advanced-pushing-the-limit power stuff, and as a result, we've alienated those who just want to just make things work with minimum fuss. We hear you loud and clear. Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com

From: joel@boost-consulting.com
On 11/25/2011 7:16 PM, Christopher Jefferson wrote:
You seem simply unwilling to accept that there are people who refuse to use Pheonix, and libraries built on it because:
1) It leads to horribly large compile times. 2) Error messages. I know you hate this, so let me write out my opinion one last time.
Phoenix leads to error messages which: * On the compilers I commonly use - I can't change this, there aren't that many compilers around. * Are very, very large * and require different skills to read and understand than any other library.
Ok, let's put that to a test.
Assumptions: 1) You are using a phoenix function 2) Your lambda is solely for capturing locals, and should be the basic counterpart of how you do it using Locals (i.e. no fancy phoenix expressions -- we restrict ourself to simple capturing of locals).
[snip]
So, gentlemen, please, let's keep the FUD to a minimum.
Everyone here already knows that Phoenix can give you local definition OR statement syntax + readable error messages. What we're saying is Boost.Local can give you local definition AND statement syntax + readable error messages. I don't see the FUD. Regards, Nate

On 11/26/2011 1:03 AM, Nathan Ridge wrote:
Everyone here already knows that Phoenix can give you local definition OR statement syntax + readable error messages.
Not everyone, evidently.
What we're saying is Boost.Local can give you local definition AND statement syntax + readable error messages.
Ok. Fair enough. Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com

On Thu, Nov 24, 2011 at 8:40 AM, Dean Michael Berris <mikhailberis@gmail.com
wrote:
On Fri, Nov 25, 2011 at 3:23 AM, Thomas Klimpel <Thomas.Klimpel@synopsys.com> wrote:
Dean Michael Berris wrote:
There's *exactly* the same number of lines in a local function as there is with a class/namespace function. What am I missing?
I'm actually also asking myself what I'm missing. Everybody seems to claim that a namespace function provides the same functionality as a local function.
[...] It's not that complicated.
The first approach is the way we've been doing it with normal C++. It works. It's not broken.
I don't see why we'd ever need Boost.Local at all.
OK, now I understand what you are missing. An important part of the functionality of Boost.Local is to capture variables from the enclosing function. Some even argued that C++ will never have true local functions, because you still have to specify the names of the variables from the enclosing function that you want to capture.
int i; bool b; long l; auto local_function = [i, b, l](std::string const & s) { // do what I want here. } // use local_function somewhere else
Hmmm?
Or better yet, works now:
namespace foo { void non_local_function(int i, bool b, long l, std::string const & s) { // do what I want here. }
void f(std::vector<double> const &v) { int i; bool b; long l; for_each(begin(v), end(v), bind(&non_local_function, i, b, l, _1)); } }
See, no voodoo required here.
Of course, if you don't need to capture any variable from the enclosing function, using Boost.Local will only make your code more ugly.
I agree.
Now, do I still miss anything?
Well, a templated non_local_function would be...annoying, at best. In that case, I'd expect the best thing is to transition to a function object: struct non_local_function { void operator()(int i, bool b, long l, std::string const & s) { /*...*/ } }; ... bind(non_local_function, i, b, l, _1) ... However, it is...let's say, sometimes convenient...to declare the bound variables at the point of definition of non_local_function rather than at the point of higher-order use. And, if you prefer including bound variables to non_local_function explicitly like that, then you would probably be adding member variables and constructors (or...use brace initialization?). Local's macros wrap all this boilerplate up for you (for better or for worse). - Jeff

On Nov 25, 2011, at 1:05 AM, Dean Michael Berris wrote:
What are you talking about? There's *exactly* the same number of lines in a local function as there is with a class/namespace function. What am I missing?
Nothing, I think that I misread your argument as a general argument against long functions rather than against having non-trivial local functions within another function, which is why my response was off; my bad. In fairness you then responded to me by telling me a bunch of basic stuff about programming that I already know and practice as if I somehow must never have heard of these things before, merely because I said that I occasionally have longs function in my code that I don't go out of my way to break up out of a sense of dogmatism that they should never exist, so I'll call it even. :-) Cheers, Greg

On Fri, Nov 25, 2011 at 2:52 AM, Gregory Crosswhite <gcrosswhite@gmail.com> wrote:
On Nov 25, 2011, at 1:05 AM, Dean Michael Berris wrote:
What are you talking about? There's *exactly* the same number of lines in a local function as there is with a class/namespace function. What am I missing?
Nothing, I think that I misread your argument as a general argument against long functions rather than against having non-trivial local functions within another function, which is why my response was off; my bad. In fairness you then responded to me by telling me a bunch of basic stuff about programming that I already know and practice as if I somehow must never have heard of these things before, merely because I said that I occasionally have longs function in my code that I don't go out of my way to break up out of a sense of dogmatism that they should never exist, so I'll call it even. :-)
Right, but as I explained too, in reasonably modern C++ code you wouldn't be needing to write these long functions precisely because it's not natural in a rich language like C++. Maybe if you were writing a program that needed to be a script but doing it in C++ then you'd end up with non-idiomatic solutions from a different frame of reference. So I guess it's really not even, more like you kinda missed the point I was making. ;) Cheers -- Dean Michael Berris http://goo.gl/CKCJX

On Thu, Nov 24, 2011 at 5:57 AM, Dean Michael Berris <mikhailberis@gmail.com> wrote:
On Thu, Nov 24, 2011 at 8:16 PM, Lorenzo Caminiti <lorcaminiti@gmail.com> wrote:
On Wed, Nov 23, 2011 at 8:11 PM, Joel de Guzman <joel@boost-consulting.com> wrote:
If you've read the Spirit docs, you will know what that means.
Now having said that, and before I leave this thread, let me remind everyone that **you can also have statement syntax in Phoenix** if you have a complex statement and you fear writing complex phoenix lambda expressions. It's called phoenix::functions.
In fact here's what I advocate:
1) For simple 1-2-3 liners, use a phoenix lambda expression. It's hard to get them wrong. For example, you'll have to be absurdly dumb to get this wrong:
auto plus = _1 + _2;
2) For more complex expressions, especially those involving statements, use a phoenix function plus a simple lambda expression that forwards to the phoenix function. Example:
for_each(f, l, call_complex_function(_1));
IF (this is a big IF) I understand it correctly, Phoenix functions are non-local functors: http://www.boost.org/doc/libs/1_48_0/libs/phoenix/doc/html/phoenix/starter_k...
// non-local scope struct is_odd_impl { typedef bool result_type;
template <typename Arg> bool operator()(Arg arg1) const { return arg1 % 2 == 1; } };
// local scope function<is_odd_impl> is_odd;
A couple of people have argued that for complex task it is OK to push the code to non-local scope. That is their point of view but of course it goes fundamentally against local functions principles (e.g., N2511) and the fact that even local functions can be long in some programming style (as expressed by others commenting before).
Lorenzo, I think you've done a good job of implementing the currently proposed library for inclusion. I do not doubt that your solution works and in your eyes it's as close to perfect as you can make it.
BUT (and this is a big BUT), even after reading your documentation, I'm still unconvinced that local functions are an elegant solution to the problem they're purportedly meant to solve.
C++ (and if I know my C correctly, even C) has gone fine without local functions. What C++11 already has right now is much more powerful than just local functions in lambdas.
The only difference between a local function and a non-local function is where it's located. If the function is named appropriately and is located in a convenient location (i.e. logically outside any given function) I and the hundreds and thousands of other C++ programmers don't see what the problem is with non-location functions. One thing I do see that is a problem with local functions the way you implement it are:
1. They're not real functions. That means I cannot refer to it inside a function pointer. Which means the myriad libraries that take function pointers out there won't be able to leverage whatever local function objects made with your library.
2. They clutter up my current function's scope. Instead of being defined in-line like C++11 lambdas, Boost.Phoenix lambda's, Boost.Lambda lambda's, and Boost.Bind lambda's, they're being defined outside the point of usage. The difference between the function being defined in-line (as in C++11 lambdas) and not in-line is *HUGE* (difference between 0 and at least one) while the difference between a function defined in a function's scope and outside the function's scope is marginal at best.
3. I cannot, no matter how I look at your examples and the "logic" behind the concept of local functions, comprehend why this is a good way to organize code. Aren't we all past the phase yet of functions that have more than 10 lines of code?
The advantages of having a function (a logical piece of code that's meant to be used as a unit *anyway*) at namespace scope or class scope is that re-usability is something you get for free. On the other hand, defining a local function with Boost.Local that I'd want to define as an external function at some point (because oh I don't know I want to use the same function somewhere else all of a sudden) will be too much work with all the macro voodoo that I have to wade through.
I (and maybe others as well who follow the same logic I follow) don't see a large enough gap between C++11 lambdas and Boost.Phoenix/Lambda/Bind function objects that merits being addressed by local functions. Until you can convince us that local functions are "absolutely necessary" and that C++ should have it because it makes certain programming paradigms/techniques possible, I'm afraid what you have is a solution that's looking for a problem.
1) I don't think I have to convince anyone. Following Boost process, I have first asked for interest in the library about ~1year ago plus all the reviewers have answered the question:
- What is your evaluation of the potential usefulness of the library? With that information I am confident that the review manager will be able to assess the library usefulness taking into consideration the opinion of /all/ the people that reviewed the library.
2) As for local functions, namespace, or global structs see N2511. If you disagree with N2511 arguments, it's truly OK because IMO everyone is free to use whatever idiom they see fit for themselves and their problem domain. I am sure the library reviewers had namespaces and global structs in mind when they assessed the "potential usefulness of the library" (also because these are mentioned in the library docs). So also in this case /all/ submitted reviews should allow the review manager to come to a conclusion on this issue. 3) [MOST IMPORTANT OF ALL] This thread is about "should new libraries implementing C++11 features in C++03" be considered for admission in Boost or not. It's not about the usefulness of Local, namespace, struct, Phoenix, etc. Let's all try to provide the review manager with this piece of information about C++11, C++03, and Boost. HTH, --Lorenzo

On Fri, Nov 25, 2011 at 3:18 AM, Lorenzo Caminiti <lorcaminiti@gmail.com> wrote:
On Thu, Nov 24, 2011 at 5:57 AM, Dean Michael Berris <mikhailberis@gmail.com> wrote:
I (and maybe others as well who follow the same logic I follow) don't see a large enough gap between C++11 lambdas and Boost.Phoenix/Lambda/Bind function objects that merits being addressed by local functions. Until you can convince us that local functions are "absolutely necessary" and that C++ should have it because it makes certain programming paradigms/techniques possible, I'm afraid what you have is a solution that's looking for a problem.
1) I don't think I have to convince anyone. Following Boost process, I have first asked for interest in the library about ~1year ago plus all the reviewers have answered the question:
- What is your evaluation of the potential usefulness of the library? With that information I am confident that the review manager will be able to assess the library usefulness taking into consideration the opinion of /all/ the people that reviewed the library.
Actually, you kinda have to convince people -- especially now that questions have been raised by members of the community, both in the review and on this thread. There have been "non-inclusion" votes already sent in. I'll throw my hat into that side of the ring too now if it's not too late and too much work to write a review.
2) As for local functions, namespace, or global structs see N2511. If you disagree with N2511 arguments, it's truly OK because IMO everyone is free to use whatever idiom they see fit for themselves and their problem domain. I am sure the library reviewers had namespaces and global structs in mind when they assessed the "potential usefulness of the library" (also because these are mentioned in the library docs). So also in this case /all/ submitted reviews should allow the review manager to come to a conclusion on this issue.
Sure, but again I don't see what's in N2511 that's not doable with C++11 lambda's. So in no uncertain terms am I expressing my opinion in the context of this thread that for libraries like Local, *no*, this should not be a library but rather should be a proper language extension for it to provide any real advantage over whatever is already out there and especially what's already in C++11.
3) [MOST IMPORTANT OF ALL] This thread is about "should new libraries implementing C++11 features in C++03" be considered for admission in Boost or not. It's not about the usefulness of Local, namespace, struct, Phoenix, etc. Let's all try to provide the review manager with this piece of information about C++11, C++03, and Boost.
But I've already done that part -- see my first response. I responded to your post because you brought up the comparison with Phoenix. I don't know if you noticed but the context of my whole reply was exactly that, the relationship between C++11, C++03, and Boost where the case in point is the Local library under review. Cheers -- Dean Michael Berris http://goo.gl/CKCJX

On Thu, Nov 24, 2011 at 11:32 AM, Dean Michael Berris <mikhailberis@gmail.com> wrote:
3) [MOST IMPORTANT OF ALL] This thread is about "should new libraries implementing C++11 features in C++03" be considered for admission in Boost or not. It's not about the usefulness of Local, namespace, struct, Phoenix, etc. Let's all try to provide the review manager with this piece of information about C++11, C++03, and Boost.
But I've already done that part -- see my first response.
I responded to your post because you brought up the comparison with Phoenix. I don't know if you noticed but the context of my whole reply was exactly that, the relationship between C++11, C++03, and Boost where the case in point is the Local library under review.
OK, can you please summarize your position with respect to C++11 and C++03 libraries here for me once again? Just a couple of sentences will be enough. Thanks a lot :) --Lorenzo

On Fri, Nov 25, 2011 at 3:49 AM, Lorenzo Caminiti <lorcaminiti@gmail.com> wrote:
On Thu, Nov 24, 2011 at 11:32 AM, Dean Michael Berris <mikhailberis@gmail.com> wrote:
3) [MOST IMPORTANT OF ALL] This thread is about "should new libraries implementing C++11 features in C++03" be considered for admission in Boost or not. It's not about the usefulness of Local, namespace, struct, Phoenix, etc. Let's all try to provide the review manager with this piece of information about C++11, C++03, and Boost.
But I've already done that part -- see my first response.
I responded to your post because you brought up the comparison with Phoenix. I don't know if you noticed but the context of my whole reply was exactly that, the relationship between C++11, C++03, and Boost where the case in point is the Local library under review.
OK, can you please summarize your position with respect to C++11 and C++03 libraries here for me once again? Just a couple of sentences will be enough. Thanks a lot :)
Here it is: http://article.gmane.org/gmane.comp.lib.boost.devel/225871 Cheers :) -- Dean Michael Berris http://goo.gl/CKCJX

On Thu, Nov 24, 2011 at 8:32 AM, Dean Michael Berris <mikhailberis@gmail.com
wrote:
On Fri, Nov 25, 2011 at 3:18 AM, Lorenzo Caminiti <lorcaminiti@gmail.com> wrote:
On Thu, Nov 24, 2011 at 5:57 AM, Dean Michael Berris <mikhailberis@gmail.com> wrote:
I (and maybe others as well who follow the same logic I follow) don't see a large enough gap between C++11 lambdas and Boost.Phoenix/Lambda/Bind function objects that merits being addressed by local functions. Until you can convince us that local functions are "absolutely necessary" and that C++ should have it because it makes certain programming paradigms/techniques possible, I'm afraid what you have is a solution that's looking for a problem.
1) I don't think I have to convince anyone. Following Boost process, I have first asked for interest in the library about ~1year ago plus all the reviewers have answered the question:
- What is your evaluation of the potential usefulness of the library? With that information I am confident that the review manager will be able to assess the library usefulness taking into consideration the opinion of /all/ the people that reviewed the library.
Actually, you kinda have to convince people -- especially now that questions have been raised by members of the community, both in the review and on this thread. There have been "non-inclusion" votes already sent in. I'll throw my hat into that side of the ring too now if it's not too late and too much work to write a review.
Noted. More than anything, I just want to let everyone know that I'm closely following this thread (and related threads) to help me make a decision on Local. I was hoping this discussion would also clarify the position Boost takes on similar libraries. However, clearly, whatever decision I make is going to be quite polarizing, and, honestly, I don't think it's a decision *I* should make; the community appears to be having difficulty coming to anything resembling a consensus :/ Personally, going into this review, I thought Local was a shoe-in for acceptance. But I feel like those against inclusion have brought up some very good points. - Jeff

On Thu, Nov 24, 2011 at 3:43 PM, Jeffrey Lee Hellrung, Jr. <jeffrey.hellrung@gmail.com> wrote:
On Thu, Nov 24, 2011 at 8:32 AM, Dean Michael Berris <mikhailberis@gmail.com
wrote:
On Fri, Nov 25, 2011 at 3:18 AM, Lorenzo Caminiti <lorcaminiti@gmail.com> wrote:
On Thu, Nov 24, 2011 at 5:57 AM, Dean Michael Berris <mikhailberis@gmail.com> wrote:
I (and maybe others as well who follow the same logic I follow) don't see a large enough gap between C++11 lambdas and Boost.Phoenix/Lambda/Bind function objects that merits being addressed by local functions. Until you can convince us that local functions are "absolutely necessary" and that C++ should have it because it makes certain programming paradigms/techniques possible, I'm afraid what you have is a solution that's looking for a problem.
1) I don't think I have to convince anyone. Following Boost process, I have first asked for interest in the library about ~1year ago plus all the reviewers have answered the question:
- What is your evaluation of the potential usefulness of the library? With that information I am confident that the review manager will be able to assess the library usefulness taking into consideration the opinion of /all/ the people that reviewed the library.
Actually, you kinda have to convince people -- especially now that questions have been raised by members of the community, both in the review and on this thread. There have been "non-inclusion" votes already sent in. I'll throw my hat into that side of the ring too now if it's not too late and too much work to write a review.
Noted.
More than anything, I just want to let everyone know that I'm closely following this thread (and related threads) to help me make a decision on Local. I was hoping this discussion would also clarify the position Boost takes on similar libraries. However, clearly, whatever decision I make is going to be quite polarizing, and, honestly, I don't think it's a decision *I* should make; the community appears to be having difficulty coming to anything resembling a consensus :/
Personally, going into this review, I thought Local was a shoe-in for acceptance. But I feel like those against inclusion have brought up some very good points.
A process question: How does Boost work? Does it count the votes of the reviewers or anyone expressing an opinion even if not submitting a review? Thanks. --Lorenzo

On Thu, Nov 24, 2011 at 12:53 PM, Lorenzo Caminiti <lorcaminiti@gmail.com>wrote:
On Thu, Nov 24, 2011 at 3:43 PM, Jeffrey Lee Hellrung, Jr. <jeffrey.hellrung@gmail.com> wrote:
On Thu, Nov 24, 2011 at 8:32 AM, Dean Michael Berris < mikhailberis@gmail.com
wrote:
On Fri, Nov 25, 2011 at 3:18 AM, Lorenzo Caminiti < lorcaminiti@gmail.com> wrote:
On Thu, Nov 24, 2011 at 5:57 AM, Dean Michael Berris <mikhailberis@gmail.com> wrote:
I (and maybe others as well who follow the same logic I follow) don't see a large enough gap between C++11 lambdas and Boost.Phoenix/Lambda/Bind function objects that merits being
addressed
by local functions. Until you can convince us that local functions are "absolutely necessary" and that C++ should have it because it makes certain programming paradigms/techniques possible, I'm afraid what you have is a solution that's looking for a problem.
1) I don't think I have to convince anyone. Following Boost process, I have first asked for interest in the library about ~1year ago plus all the reviewers have answered the question:
- What is your evaluation of the potential usefulness of the library? With that information I am confident that the review manager will be able to assess the library usefulness taking into consideration the opinion of /all/ the people that reviewed the library.
Actually, you kinda have to convince people -- especially now that questions have been raised by members of the community, both in the review and on this thread. There have been "non-inclusion" votes already sent in. I'll throw my hat into that side of the ring too now if it's not too late and too much work to write a review.
Noted.
More than anything, I just want to let everyone know that I'm closely following this thread (and related threads) to help me make a decision on Local. I was hoping this discussion would also clarify the position Boost takes on similar libraries. However, clearly, whatever decision I make is going to be quite polarizing, and, honestly, I don't think it's a decision *I* should make; the community appears to be having difficulty coming to anything resembling a consensus :/
Personally, going into this review, I thought Local was a shoe-in for acceptance. But I feel like those against inclusion have brought up some very good points.
A process question: How does Boost work? Does it count the votes of the reviewers or anyone expressing an opinion even if not submitting a review?
I'm going off of http://www.boost.org/community/reviews.html which indicates (to me) that it doesn't have to come down to a comparison of the number of "yes" votes versus the number of "no" votes. Rather, the following two quotes from the above link, "The final "accept" or "reject" decision is made by the Review Manager<http://www.boost.org/community/reviews.html#Review_Manager>, based on the review comments received from boost mailing list members." "The Review Manager: [...] Decides if there is consensus to accept the library, and if there are any conditions attached." indicate a much more "holistic" process to coming to a conclusion. And, now that I'm rereading the above, there certainly is not "consensus" to accept Local :( (Of course, there is also not consensus to reject Local, but it appears the review guidelines take an understandably conservative approach that naturally raises the bar for acceptance.) - Jeff

AMDG On 11/24/2011 12:43 PM, Jeffrey Lee Hellrung, Jr. wrote:
More than anything, I just want to let everyone know that I'm closely following this thread (and related threads) to help me make a decision on Local. I was hoping this discussion would also clarify the position Boost takes on similar libraries. However, clearly, whatever decision I make is going to be quite polarizing, and, honestly, I don't think it's a decision *I* should make; the community appears to be having difficulty coming to anything resembling a consensus :/
You're the review manager. You have to take responsibility for making this decision. No one else is going to do it for you, nor is Boost some kind of magic entity that gives reviews a "correct" result even when the reviewers vehemently disagree among themselves.
Personally, going into this review, I thought Local was a shoe-in for acceptance. But I feel like those against inclusion have brought up some very good points.
In Christ, Steven Watanabe

On Thu, Nov 24, 2011 at 4:36 PM, Steven Watanabe <watanabesj@gmail.com> wrote: > AMDG > > On 11/24/2011 12:43 PM, Jeffrey Lee Hellrung, Jr. wrote: >> >> More than anything, I just want to let everyone know that I'm closely >> following this thread (and related threads) to help me make a decision on >> Local. I was hoping this discussion would also clarify the position Boost >> takes on similar libraries. However, clearly, whatever decision I make is >> going to be quite polarizing, and, honestly, I don't think it's a decision >> *I* should make; the community appears to be having difficulty coming to >> anything resembling a consensus :/ >> > > You're the review manager. You have to take > responsibility for making this decision. No > one else is going to do it for you, nor is Boost > some kind of magic entity that gives reviews > a "correct" result even when the reviewers > vehemently disagree among themselves. >From my participation in Boost, I noted that it has been common practice for review managers to determine consensus counting the reviews "yes" vs. "no" but there is no Boost guideline that states that should be the process. Therefore, I understand if a different process is used by the review manager to define and determine if there's consensus around Boost.Local. I only ask for *transparency* which means stating clearly not only the decisions but also 1) what process was used to decide and 2) why such a process was selected (especially if a process different from the common vote count were to be used). >From my records, the objection raised so far are: 1) Local functions are not useful in C++ in general. 2) Local function syntax is ugly and uses macros. 3) Local functions are made obsolete in C++11 by lambdas even if there exist other Boost libraries that implement C++11 features for C++03. 4) For C++03 there are other solutions for local functions even if they don't use statement syntax for the functions body. More? Also from my records, all reviewers have spoken to these points. So people that voted for inclusion have provided their counter arguments to 1-2-3-4. Thank you. --Lorenzo

Jeffrey Lee Hellrung, Jr. <jeffrey.hellrung@gmail.com> wrote: More than anything, I just want to let everyone know that I'm closely following this thread (and related threads) to help me make a decision on Local. I was hoping this discussion would also clarify the position Boost takes on similar libraries. However, clearly, whatever decision I make is going to be quite polarizing, and, honestly, I don't think it's a decision *I* should make; the community appears to be having difficulty coming to anything resembling a consensus :/
Well, that's exactly the reason why we have review managers - to make the decision if the community does not come to a conclusive answer. The good part for you is that the community will accept your decision whatever it is. You won't be blamed as long as you lay out your rationale. Regards Hartmut --------------- http://boost-spirit.com http://stellar.cct.lsu.edu

On Fri, Nov 25, 2011 at 11:08 AM, Hartmut Kaiser <hartmut.kaiser@gmail.com> wrote:
Jeffrey Lee Hellrung, Jr. <jeffrey.hellrung@gmail.com> wrote: More than anything, I just want to let everyone know that I'm closely following this thread (and related threads) to help me make a decision on Local. I was hoping this discussion would also clarify the position Boost takes on similar libraries. However, clearly, whatever decision I make is going to be quite polarizing, and, honestly, I don't think it's a decision *I* should make; the community appears to be having difficulty coming to anything resembling a consensus :/
Well, that's exactly the reason why we have review managers - to make the decision if the community does not come to a conclusive answer. The good part for you is that the community will accept your decision whatever it is. You won't be blamed as long as you lay out your rationale.
+1. As a member of the community, I'd like to express the same opinion. I appreciate the review manager's work and I will of course respect his decision. --Lorenzo

2) As for local functions, namespace, or global structs see N2511. If you disagree with N2511 arguments, it's truly OK because IMO everyone is free to use whatever idiom they see fit for themselves and their problem domain. I am sure the library reviewers had namespaces and global structs in mind when they assessed the "potential usefulness of the library" (also because these are mentioned in the library docs). So also in this case /all/ submitted reviews should allow the review manager to come to a conclusion on this issue.
Sure, but again I don't see what's in N2511 that's not doable with C++11 lambda's.
So in no uncertain terms am I expressing my opinion in the context of this thread that for libraries like Local, *no*, this should not be a library but rather should be a proper language extension for it to provide any real advantage over whatever is already out there and especially what's already in C++11.
You are conflating two different arguments over and over again: 1. Whether Boost.Local provides any advantage over pure C++03 alternatives 2. Whether it is appropriate to add something to Boost that improves the C++03 experience but has been obsoleted by a C++11 feature Can we please keep these discussions separate and argue them on their own merits? It doesn't help when every time someone refutes your position about (1) you bring up (2) saying that it makes (1) irrelevant. We already know your position about (2): that now C++11 is here, we should be moving on and stop giving attention to C++03 problems which are solved by C++11. I, on the other hand, think the reality is that C++03 will be around for a while longer, and it's worth caring about the C++03 experience evem if there are C++11 solutions. Obviously our positions are derived from different experiences working with C++, and I don't think there's much point arguing about this, other than asking others to weigh in and share their positions based on their experiences.
3. I cannot, no matter how I look at your examples and the "logic" behind the concept of local functions, comprehend why this is a good way to organize code. Aren't we all past the phase yet of functions that have more than 10 lines of code?
The advantages of having a function (a logical piece of code that's meant to be used as a unit *anyway*) at namespace scope or class scope is that re-usability is something you get for free. On the other hand, defining a local function with Boost.Local that I'd want to define as an external function at some point (because oh I don't know I want to use the same function somewhere else all of a sudden) will be too much work with all the macro voodoo that I have to wade through.
I think this is a matter of one's coding style. When I write a function, I think of it as being part of some interface. It if it's a public method of a class, it's part of the class's public interface. If it's a private method, it's still part of some interface: the interface used by the implementations of the public methods. By moving a function A from being local to another function B, to being in the same scope as B, you are adding function A to the same interface that function B is part of. I view this as a design choice: a choice I may or may not want to make, and I wouldn't like the langauge to stand in my way and force me to make one choice because it doesn't support the other. So, at least in my coding style, I see a use case for local functions which does not have a C++03 solution. I think a language, and C++ in particular, should be flexible enough to accomodate different coding styles. Now given that we don't have language-level support for local functions in C++03, the next best thing we can do is emulate it with a library.
Hey I'm not complaining that people are bashing libraries. What I'm complaining about is why people aren't complaining (meta isn't it?) that their compilers suck at displaying better error messages for broken code.
Again, *THE LIBRARY DOESN'T SPEW ERROR MESSAGES, COMPILERS DO THAT FOR BROKEN CODE*.
So again, how does broken code using any library become a basis for whether the library is a good library for inclusion in Boost? It just seems silly to me.
The problem is that errors point to code that is deep within the library's implementation, or worse, the implementations of helper libraries used by the library. To understand the error, the user has to look at the library's implementation. The user shouldn't have to do that - understanding the implementation of a library should not be a prerequisite for using it. If you have any specific suggestions about what compilers could do to turn these errors from deep within the library implementation, into errors that do not require knowing anything about the library implementation, I would like to hear it, and I'm sure so would compiler writers. Otherwise, you have to accept that library writers face a trade-off between the user-friendliness of error messages, and the expressiveness, terseness, and power obtained by extensive use of advanced techniques such as template metaprogramming. There is no one right answer to this tradeoff, and it is good for users to have different alternatives available to them. Regards, Nate

On Fri, Nov 25, 2011 at 9:23 AM, Nathan Ridge <zeratul976@hotmail.com> wrote:
2) As for local functions, namespace, or global structs see N2511. If you disagree with N2511 arguments, it's truly OK because IMO everyone is free to use whatever idiom they see fit for themselves and their problem domain. I am sure the library reviewers had namespaces and global structs in mind when they assessed the "potential usefulness of the library" (also because these are mentioned in the library docs). So also in this case /all/ submitted reviews should allow the review manager to come to a conclusion on this issue.
Sure, but again I don't see what's in N2511 that's not doable with C++11 lambda's.
So in no uncertain terms am I expressing my opinion in the context of this thread that for libraries like Local, *no*, this should not be a library but rather should be a proper language extension for it to provide any real advantage over whatever is already out there and especially what's already in C++11.
You are conflating two different arguments over and over again:
1. Whether Boost.Local provides any advantage over pure C++03 alternatives
2. Whether it is appropriate to add something to Boost that improves the C++03 experience but has been obsoleted by a C++11 feature
Can we please keep these discussions separate and argue them on their own merits? It doesn't help when every time someone refutes your position about (1) you bring up (2) saying that it makes (1) irrelevant.
I don't know if you actually *read* what I wrote. I said, for _libraries *like* Local_ they should not even be libraries. So for the clarity of the situation: 1. Does Boost.Local provide any advantage over pure C++03 alternatives? No. Absolutely no, IMO. 2. Do we want libraries that add something to Boost that improves the C++03 experience but has been obsoleted by a C++ feature? Answer is: it depends.
The advantages of having a function (a logical piece of code that's meant to be used as a unit *anyway*) at namespace scope or class scope is that re-usability is something you get for free. On the other hand, defining a local function with Boost.Local that I'd want to define as an external function at some point (because oh I don't know I want to use the same function somewhere else all of a sudden) will be too much work with all the macro voodoo that I have to wade through.
I think this is a matter of one's coding style.
Huh? This is a matter of the rules of the programming language and (dare I say) sane engineering practices.
When I write a function, I think of it as being part of some interface. It if it's a public method of a class, it's part of the class's public interface. If it's a private method, it's still part of some interface: the interface used by the implementations of the public methods. By moving a function A from being local to another function B, to being in the same scope as B, you are adding function A to the same interface that function B is part of. I view this as a design choice: a choice I may or may not want to make, and I wouldn't like the langauge to stand in my way and force me to make one choice because it doesn't support the other.
Did you really mean that C++ shouldn't stand in your way when you want to write Pascal code? I'm sorry but this logic is just broken. You're saying "I want local functions in C++ but I don't want it as a language extension". This makes no sense at all. If you really don't want the language to stand in your way and force you to make one choice because it doesn't support another, use Lisp.
So, at least in my coding style, I see a use case for local functions which does not have a C++03 solution. I think a language, and C++ in particular, should be flexible enough to accomodate different coding styles. Now given that we don't have language-level support for local functions in C++03, the next best thing we can do is emulate it with a library.
But there are C++03 solutions, it involves functions in namespaces/class scope and binders. If you really want in-lined functions defined then you use an approximation of lambda's like Phoenix/Bind/Lambda in C++03.
Hey I'm not complaining that people are bashing libraries. What I'm complaining about is why people aren't complaining (meta isn't it?) that their compilers suck at displaying better error messages for broken code. > Again, *THE LIBRARY DOESN'T SPEW ERROR MESSAGES, COMPILERS DO THAT FOR BROKEN CODE*. > So again, how does broken code using any library become a basis for whether the library is a good library for inclusion in Boost? It just seems silly to me.
The problem is that errors point to code that is deep within the library's implementation, or worse, the implementations of helper libraries used by the library. To understand the error, the user has to look at the library's implementation. The user shouldn't have to do that - understanding the implementation of a library should not be a prerequisite for using it.
Okay, again though there's two things working together here: the compiler pointing you at the wrong place and the library failing somewhere "deep". There's only so much a library writer can do about making it not fail somewhere deep but... there's nothing the library writer can do about the compiler choosing to not pander to humans.
If you have any specific suggestions about what compilers could do to turn these errors from deep within the library implementation, into errors that do not require knowing anything about the library implementation, I would like to hear it, and I'm sure so would compiler writers.
Oh believe me they've heard it from me (or at least I've made some noise about it already). I can make more noise and maybe send in patches but there's other things in this world that concern me (and others) to actually do anything substantial in other fronts.
Otherwise, you have to accept that library writers face a trade-off between the user-friendliness of error messages, and the expressiveness, terseness, and power obtained by extensive use of advanced techniques such as template metaprogramming. There is no one right answer to this tradeoff, and it is good for users to have different alternatives available to them.
Sure. So what's the point again? I don't see how this paragraph is relevant to the discussion. Cheers -- Dean Michael Berris http://goo.gl/CKCJX

The advantages of having a function (a logical piece of code that's meant to be used as a unit *anyway*) at namespace scope or class scope is that re-usability is something you get for free. On the other hand, defining a local function with Boost.Local that I'd want to define as an external function at some point (because oh I don't know I want to use the same function somewhere else all of a sudden) will be too much work with all the macro voodoo that I have to wade through.
I think this is a matter of one's coding style.
Huh? This is a matter of the rules of the programming language and (dare I say) sane engineering practices.
Are you saying it's sane engineering practice to avoid local functions? If so, why don't you avoid C++11 lambdas equally?
When I write a function, I think of it as being part of some interface. It if it's a public method of a class, it's part of the class's public interface. If it's a private method, it's still part of some interface: the interface used by the implementations of the public methods. By moving a function A from being local to another function B, to being in the same scope as B, you are adding function A to the same interface that function B is part of. I view this as a design choice: a choice I may or may not want to make, and I wouldn't like the langauge to stand in my way and force me to make one choice because it doesn't support the other.
Did you really mean that C++ shouldn't stand in your way when you want to write Pascal code?
I don't know Pascal so I can't comment on that. I am saying that C++ shouldn't stand in my way when I want to make design choices like making one function local to another versus making them "equals" by putting them in the same scope.
I'm sorry but this logic is just broken. You're saying "I want local functions in C++ but I don't want it as a language extension". This makes no sense at all.
I do want it as a language extension. We have it as a language extension, it's called C++11 lambdas. But let's let the poor guys who can't use C++11 yet have an approximation of it, too!
So, at least in my coding style, I see a use case for local functions which does not have a C++03 solution. I think a language, and C++ in particular, should be flexible enough to accomodate different coding styles. Now given that we don't have language-level support for local functions in C++03, the next best thing we can do is emulate it with a library.
But there are C++03 solutions, it involves functions in namespaces/class scope and binders. If you really want in-lined functions defined then you use an approximation of lambda's like Phoenix/Bind/Lambda in C++03.
The key word here being "approximation". Boost.Local is an approximation, too, just in a different way. Boost.Local gives you something Phoenix etc. do not (C++ statement syntax), and Phoenix etc. give you something Boost.Local does not (in-line definition in an expression context). Both approximations are useful, and some may prefer one over the other.
If you have any specific suggestions about what compilers could do to turn these errors from deep within the library implementation, into errors that do not require knowing anything about the library implementation, I would like to hear it, and I'm sure so would compiler writers.
Oh believe me they've heard it from me (or at least I've made some noise about it already). I can make more noise and maybe send in patches but there's other things in this world that concern me (and others) to actually do anything substantial in other fronts.
Could you point me to some links? I am genuinely interested.
Otherwise, you have to accept that library writers face a trade-off between the user-friendliness of error messages, and the expressiveness, terseness, and power obtained by extensive use of advanced techniques such as template metaprogramming. There is no one right answer to this tradeoff, and it is good for users to have different alternatives available to them.
Sure. So what's the point again? I don't see how this paragraph is relevant to the discussion.
You asked:
So again, how does broken code using any library become a basis for whether the library is a good library for inclusion in Boost? It just seems silly to me.
Since dealing frequently with errors is a reality of programming, and we cannot rely on compilers to substantially improve the readability of TMP error messages in the short term, a library that solves a similar problem to an existing library, but using different techniques that give rise to more user-friendly error messages, adds value, and therefore can reasonably be considered for inclusion in Boost. Regards, Nate

On 11/24/2011 10:38 PM, Nathan Ridge wrote:
The key word here being "approximation". Boost.Local is an approximation, too, just in a different way. Boost.Local gives you something Phoenix etc. do not (C++ statement syntax), and Phoenix etc. give you something Boost.Local does not (in-line definition in an expression context). Both approximations are useful, and some may prefer one over the other.
That's just plain wrong. In case you missed it: http://tinyurl.com/cg72pyy and: http://tinyurl.com/7ehowv3 Besides: http://thread.gmane.org/gmane.comp.lib.boost.devel/225989

From: thom.heller@googlemail.com On 11/24/2011 10:38 PM, Nathan Ridge wrote:
The key word here being "approximation". Boost.Local is an approximation, too, just in a different way. Boost.Local gives you something Phoenix etc. do not (C++ statement syntax), and Phoenix etc. give you something Boost.Local does not (in-line definition in an expression context). Both approximations are useful, and some may prefer one over the other.
That's just plain wrong. In case you missed it: http://tinyurl.com/cg72pyy and: http://tinyurl.com/7ehowv3
Huh? Those are out-of-line, non-local function definitions... Am I missing something?
Besides: http://thread.gmane.org/gmane.comp.lib.boost.devel/225989
That's a neat idea, but I'm not sure how it's relevant to this discussion. Those who are objecting to Boost.Local are not objecting on the basis that the generated function object is not a Phoenix function... Regards, Nate

On 11/24/2011 10:55 PM, Nathan Ridge wrote:
From: thom.heller@googlemail.com On 11/24/2011 10:38 PM, Nathan Ridge wrote:
The key word here being "approximation". Boost.Local is an approximation, too, just in a different way. Boost.Local gives you something Phoenix etc. do not (C++ statement syntax), and Phoenix etc. give you something Boost.Local does not (in-line definition in an expression context). Both approximations are useful, and some may prefer one over the other.
That's just plain wrong. In case you missed it: http://tinyurl.com/cg72pyy and: http://tinyurl.com/7ehowv3
Huh? Those are out-of-line, non-local function definitions... Am I missing something?
Besides: http://thread.gmane.org/gmane.comp.lib.boost.devel/225989
That's a neat idea, but I'm not sure how it's relevant to this discussion. Those who are objecting to Boost.Local are not objecting on the basis that the generated function object is not a Phoenix function...
You are of course right, the first two links are not about local function definitions. However, the third link proposes a solution that should bring everyone together: Local functions combined with the power of Phoenix.

On Thu, Nov 24, 2011 at 8:56 PM, Thomas Heller <thom.heller@googlemail.com>wrote:
On 11/24/2011 10:55 PM, Nathan Ridge wrote:
From: thom.heller@googlemail.com
Besides:
That's a neat idea, but I'm not sure how it's relevant to this discussion. Those who are objecting to Boost.Local are not objecting on the basis that the generated function object is not a Phoenix function...
You are of course right, the first two links are not about local function definitions. However, the third link proposes a solution that should bring everyone together: Local functions combined with the power of Phoenix.
Actually, I'm confused as well, Thomas. I wouldn't have thought that making the local functions generated by Lorenzo's Local macros could have addressed your previously expressed objections to Local. Granted, there's been a lot of discussion and I may have missed something. Care to clarify? - Jeff

On 11/25/2011 12:05 AM, Jeffrey Lee Hellrung, Jr. wrote:
On Thu, Nov 24, 2011 at 8:56 PM, Thomas Heller <thom.heller@googlemail.com>wrote:
On 11/24/2011 10:55 PM, Nathan Ridge wrote:
From: thom.heller@googlemail.com
Besides:
That's a neat idea, but I'm not sure how it's relevant to this discussion. Those who are objecting to Boost.Local are not objecting on the basis that the generated function object is not a Phoenix function...
You are of course right, the first two links are not about local function definitions. However, the third link proposes a solution that should bring everyone together: Local functions combined with the power of Phoenix.
Actually, I'm confused as well, Thomas. I wouldn't have thought that making the local functions generated by Lorenzo's Local macros could have addressed your previously expressed objections to Local. Granted, there's been a lot of discussion and I may have missed something. Care to clarify?
Well, the idea was to find a consensus. People obviously want to have local functions. I was trying to find a solution that somehow get rid of some of my objections, mainly verbosity of the local function declarations. The complexity mainly comes due to the need to capture variables in scope. By generating a local phoenix function, which can be used in regular phoenix expressions, which can capture variables in scope very natural (just pass them as function parameters). I am still not happy that we need macros to declare (local) functions, but i guess that is what it takes.

On Thu, Nov 24, 2011 at 11:43 PM, Thomas Heller <thom.heller@googlemail.com> wrote:
On 11/24/2011 10:38 PM, Nathan Ridge wrote:
The key word here being "approximation". Boost.Local is an approximation, too, just in a different way. Boost.Local gives you something Phoenix etc. do not (C++ statement syntax), and Phoenix etc. give you something Boost.Local does not (in-line definition in an expression context). Both approximations are useful, and some may prefer one over the other.
That's just plain wrong. In case you missed it: http://tinyurl.com/cg72pyy and: http://tinyurl.com/7ehowv3 Besides: http://thread.gmane.org/gmane.comp.lib.boost.devel/225989
It is for statement syntax at local scope. --Lorenzo

On Fri, Nov 25, 2011 at 3:38 PM, Nathan Ridge <zeratul976@hotmail.com> wrote:
Stuff I (mikhailberis@gmail.com) said...
Huh? This is a matter of the rules of the programming language and (dare I say) sane engineering practices.
Are you saying it's sane engineering practice to avoid local functions? If so, why don't you avoid C++11 lambdas equally?
Because C++11 lambdas are function objects I can define in-line where I need function objects -- which is what modern C++ prefers over function pointers. The STL and every other sanely engineered C++ library that requires function object callbacks will support function objects whether they're defined in-line with C++11 lambdas or as hand-rolled function objects using operator() overloading. Also, I personally do not see a need for a construct like this: auto f = []() { /* do something here */ } I say this because there's absolutely 0 point in doing this if I can drop the lambda in where I need f *anyway*.
Did you really mean that C++ shouldn't stand in your way when you want to write Pascal code?
I don't know Pascal so I can't comment on that. I am saying that C++ shouldn't stand in my way when I want to make design choices like making one function local to another versus making them "equals" by putting them in the same scope.
What's the problem with making them equals? They're just functions.
I'm sorry but this logic is just broken. You're saying "I want local functions in C++ but I don't want it as a language extension". This makes no sense at all.
I do want it as a language extension. We have it as a language extension, it's called C++11 lambdas. But let's let the poor guys who can't use C++11 yet have an approximation of it, too!
Then propose/implement it as a language extension, or keep the current implementation and use it -- nobody's stopping you or anybody from doing that. Putting it in Boost is what I object to "just because".
But there are C++03 solutions, it involves functions in namespaces/class scope and binders. If you really want in-lined functions defined then you use an approximation of lambda's like Phoenix/Bind/Lambda in C++03.
The key word here being "approximation". Boost.Local is an approximation, too, just in a different way. Boost.Local gives you something Phoenix etc. do not (C++ statement syntax), and Phoenix etc. give you something Boost.Local does not (in-line definition in an expression context). Both approximations are useful, and some may prefer one over the other.
I know what gives me C++ statement syntax: C++ functions. I still don't get why local functions are so worth the trouble of muddying up a function's implementation when functions can stand alone anyway and be just as good.
Oh believe me they've heard it from me (or at least I've made some noise about it already). I can make more noise and maybe send in patches but there's other things in this world that concern me (and others) to actually do anything substantial in other fronts.
Could you point me to some links? I am genuinely interested.
Just this one: http://cplusplus-soup.com/2010/11/21/c-hating/ and look at the comments too if you have enough time. :)
Otherwise, you have to accept that library writers face a trade-off between the user-friendliness of error messages, and the expressiveness, terseness, and power obtained by extensive use of advanced techniques such as template metaprogramming. There is no one right answer to this tradeoff, and it is good for users to have different alternatives available to them.
Sure. So what's the point again? I don't see how this paragraph is relevant to the discussion.
You asked:
So again, how does broken code using any library become a basis for whether the library is a good library for inclusion in Boost? It just seems silly to me.
Since dealing frequently with errors is a reality of programming, and we cannot rely on compilers to substantially improve the readability of TMP error messages in the short term, a library that solves a similar problem to an existing library, but using different techniques that give rise to more user-friendly error messages, adds value, and therefore can reasonably be considered for inclusion in Boost.
No, sorry -- just having "prettier error messages" isn't a good measure for inclusion in Boost. You're going to have to try harder than that to make a case for a library. Cheers -- Dean Michael Berris http://goo.gl/CKCJX

From: mikhailberis@gmail.com Subject: Re: [boost] New libraries implementing C++11 features in C++03
On Fri, Nov 25, 2011 at 3:38 PM, Nathan Ridge <zeratul976@hotmail.com> wrote:
Stuff I (mikhailberis@gmail.com) said...
Huh? This is a matter of the rules of the programming language and (dare I say) sane engineering practices.
Are you saying it's sane engineering practice to avoid local functions? If so, why don't you avoid C++11 lambdas equally?
Because C++11 lambdas are function objects I can define in-line where I need function objects -- which is what modern C++ prefers over function pointers. The STL and every other sanely engineered C++ library that requires function object callbacks will support function objects whether they're defined in-line with C++11 lambdas or as hand-rolled function objects using operator() overloading.
And they won't support Boost.Local functions?
Also, I personally do not see a need for a construct like this:
auto f = []() { /* do something here */ }
I say this because there's absolutely 0 point in doing this if I can drop the lambda in where I need f *anyway*.
I use this construct frequently in my C++11 projects. It can serve to make the code more readable. Here's an example of a line of code I wrote recently. It makes use of Boost.Range in addition to C++11 lambdas: vector<message> messages; push_back(messages, message_ids | transformed(lookup_message_by_id) | filtered(newer_than_one_year) | filtered(not_hidden) | filtered(satisfies_user_preference)); The arguments to the transformed and filtered adaptors are all C++11 lambdas defined immediately above these lines. How do you think this code would look if I instead dropped the lambda definitions inline? Or would you have wasted extra lines and time defining these little one-or-two-liner functions that are not used anywhere else, out of line?
Did you really mean that C++ shouldn't stand in your way when you want to write Pascal code?
I don't know Pascal so I can't comment on that. I am saying that C++ shouldn't stand in my way when I want to make design choices like making one function local to another versus making them "equals" by putting them in the same scope.
What's the problem with making them equals? They're just functions.
Suppose you have a namespace N, with two classes N::A and N::B. Suppose you have a function f() that is used only in the implementation of A, and has nothing to do with B. Is it better to put this function inside namespace N, where it is visible by both A and B, or inside the class A, where it is only visible by A? Now repeat this argument, substituting a class for namespace N, and two methods for A and B, and you have a use case for local functions.
I'm sorry but this logic is just broken. You're saying "I want local functions in C++ but I don't want it as a language extension". This makes no sense at all.
I do want it as a language extension. We have it as a language extension, it's called C++11 lambdas. But let's let the poor guys who can't use C++11 yet have an approximation of it, too!
Then propose/implement it as a language extension, or keep the current implementation and use it -- nobody's stopping you or anybody from doing that. Putting it in Boost is what I object to "just because".
Perhaps I'm not understanding what you mean by a "language extension". Are you suggesting I propose it for the next standard of C++? That would be silly, as C++11 already has a solution (lambdas), and the whole point is to make the feature accessible to people who can't use C++11 yet. Or are you suggesting that I propose a compiler-specific language extension for my compiler? That will not fly in an environment where one's code has to run of many different compilers, and in any case it goes completely against the spirit of having a language standard in the first place.
Otherwise, you have to accept that library writers face a trade-off between the user-friendliness of error messages, and the expressiveness, terseness, and power obtained by extensive use of advanced techniques such as template metaprogramming. There is no one right answer to this tradeoff, and it is good for users to have different alternatives available to them.
Sure. So what's the point again? I don't see how this paragraph is relevant to the discussion.
You asked:
So again, how does broken code using any library become a basis for whether the library is a good library for inclusion in Boost? It just seems silly to me.
Since dealing frequently with errors is a reality of programming, and we cannot rely on compilers to substantially improve the readability of TMP error messages in the short term, a library that solves a similar problem to an existing library, but using different techniques that give rise to more user-friendly error messages, adds value, and therefore can reasonably be considered for inclusion in Boost.
No, sorry -- just having "prettier error messages" isn't a good measure for inclusion in Boost. You're going to have to try harder than that to make a case for a library.
So you admit that bad errors messages are a problem that users run into frequently, and that we're not likely to see a solution from the compiler side any time soon... but you say a library that solves this problem is not important enough for Boost? Regards, Nate

On Fri, Nov 25, 2011 at 6:06 PM, Nathan Ridge <zeratul976@hotmail.com> wrote:
From: mikhailberis@gmail.com Subject: Re: [boost] New libraries implementing C++11 features in C++03
On Fri, Nov 25, 2011 at 3:38 PM, Nathan Ridge <zeratul976@hotmail.com> wrote:
Stuff I (mikhailberis@gmail.com) said...
Huh? This is a matter of the rules of the programming language and (dare I say) sane engineering practices.
Are you saying it's sane engineering practice to avoid local functions? If so, why don't you avoid C++11 lambdas equally?
Because C++11 lambdas are function objects I can define in-line where I need function objects -- which is what modern C++ prefers over function pointers. The STL and every other sanely engineered C++ library that requires function object callbacks will support function objects whether they're defined in-line with C++11 lambdas or as hand-rolled function objects using operator() overloading.
And they won't support Boost.Local functions?
If Boost.Local functions are just function objects then sure. My point is that I don't see the need for Boost.Local for me to be able to leverage these libraries in C++03 or C++11.
Also, I personally do not see a need for a construct like this:
auto f = []() { /* do something here */ }
I say this because there's absolutely 0 point in doing this if I can drop the lambda in where I need f *anyway*.
I use this construct frequently in my C++11 projects. It can serve to make the code more readable. Here's an example of a line of code I wrote recently. It makes use of Boost.Range in addition to C++11 lambdas:
vector<message> messages; push_back(messages, message_ids | transformed(lookup_message_by_id) | filtered(newer_than_one_year) | filtered(not_hidden) | filtered(satisfies_user_preference));
The arguments to the transformed and filtered adaptors are all C++11 lambdas defined immediately above these lines. How do you think this code would look if I instead dropped the lambda definitions inline? Or would you have wasted extra lines and time defining these little one-or-two-liner functions that are not used anywhere else, out of line?
I think You're Doing It Wrong (tm). If I was doing this, all these function objects would be types themselves, they would be testable outside this context, and they can even be just forward declared and linked statically later on. I don't see why they need or have to be C++11 lambdas at all if they're not just a one-or-two liner and if they have names that do make sense as types.
Did you really mean that C++ shouldn't stand in your way when you want to write Pascal code?
I don't know Pascal so I can't comment on that. I am saying that C++ shouldn't stand in my way when I want to make design choices like making one function local to another versus making them "equals" by putting them in the same scope.
What's the problem with making them equals? They're just functions.
Suppose you have a namespace N, with two classes N::A and N::B. Suppose you have a function f() that is used only in the implementation of A, and has nothing to do with B. Is it better to put this function inside namespace N, where it is visible by both A and B, or inside the class A, where it is only visible by A?
I'd make it private in A.
Now repeat this argument, substituting a class for namespace N, and two methods for A and B, and you have a use case for local functions.
No, I would create nested namespaces specific to the requirements of functions A and B (maybe call them impl_A and impl_B). There's no *need* for local functions in this case you're proposing.
Then propose/implement it as a language extension, or keep the current implementation and use it -- nobody's stopping you or anybody from doing that. Putting it in Boost is what I object to "just because".
Perhaps I'm not understanding what you mean by a "language extension".
Like what Microsoft does with their language extensions.
Are you suggesting I propose it for the next standard of C++? That would be silly, as C++11 already has a solution (lambdas), and the whole point is to make the feature accessible to people who can't use C++11 yet.
See my point now?
Or are you suggesting that I propose a compiler-specific language extension for my compiler? That will not fly in an environment where one's code has to run of many different compilers, and in any case it goes completely against the spirit of having a language standard in the first place.
Right, so you see why I don't think Boost.Local even makes sense?
No, sorry -- just having "prettier error messages" isn't a good measure for inclusion in Boost. You're going to have to try harder than that to make a case for a library.
So you admit that bad errors messages are a problem that users run into frequently, and that we're not likely to see a solution from the compiler side any time soon... but you say a library that solves this problem is not important enough for Boost?
Yes, they're not important enough for Boost if what it provides isn't compelling enough to be provided by Boost. If the only thing a library has is "prettier error messages when things are broken" then it's a great library for broken code -- which makes that advantage null and void because IT'S NOT AN ADVANTAGE BECAUSE YOUR CODE DOESN'T COMPILE. Am I not getting through here? Whoever loved a library that looked great when your code fails to compile? This is CRAZY TALK. Cheers -- Dean Michael Berris http://goo.gl/CKCJX

From: mikhailberis@gmail.com
On Fri, Nov 25, 2011 at 6:06 PM, Nathan Ridge <zeratul976@hotmail.com> wrote:
From: mikhailberis@gmail.com Subject: Re: [boost] New libraries implementing C++11 features in C++03
On Fri, Nov 25, 2011 at 3:38 PM, Nathan Ridge <zeratul976@hotmail.com> wrote:
Stuff I (mikhailberis@gmail.com) said...
Huh? This is a matter of the rules of the programming language and (dare I say) sane engineering practices.
Are you saying it's sane engineering practice to avoid local functions? If so, why don't you avoid C++11 lambdas equally?
Because C++11 lambdas are function objects I can define in-line where I need function objects -- which is what modern C++ prefers over function pointers. The STL and every other sanely engineered C++ library that requires function object callbacks will support function objects whether they're defined in-line with C++11 lambdas or as hand-rolled function objects using operator() overloading.
And they won't support Boost.Local functions?
If Boost.Local functions are just function objects then sure. My point is that I don't see the need for Boost.Local for me to be able to leverage these libraries in C++03 or C++11.
Who ever said the point of Boost.Local was to be able to leverage these libraries? The point of Boost.Local is to be able define functions locally, close to where they are used, with C++ statement syntax.
Also, I personally do not see a need for a construct like this:
auto f = []() { /* do something here */ }
I say this because there's absolutely 0 point in doing this if I can drop the lambda in where I need f *anyway*.
I use this construct frequently in my C++11 projects. It can serve to make the code more readable. Here's an example of a line of code I wrote recently. It makes use of Boost.Range in addition to C++11 lambdas:
vector<message> messages; push_back(messages, message_ids | transformed(lookup_message_by_id) | filtered(newer_than_one_year) | filtered(not_hidden) | filtered(satisfies_user_preference));
The arguments to the transformed and filtered adaptors are all C++11 lambdas defined immediately above these lines. How do you think this code would look if I instead dropped the lambda definitions inline? Or would you have wasted extra lines and time defining these little one-or-two-liner functions that are not used anywhere else, out of line?
I think You're Doing It Wrong (tm). If I was doing this, all these function objects would be types themselves, they would be testable outside this context, and they can even be just forward declared and linked statically later on.
I don't see why they need or have to be C++11 lambdas at all if they're not just a one-or-two liner and if they have names that do make sense as types.
They *are* one-or-two-liners, and it would be overkill to define them out of line.
Did you really mean that C++ shouldn't stand in your way when you want to write Pascal code?
I don't know Pascal so I can't comment on that. I am saying that C++ shouldn't stand in my way when I want to make design choices like making one function local to another versus making them "equals" by putting them in the same scope.
What's the problem with making them equals? They're just functions.
Suppose you have a namespace N, with two classes N::A and N::B. Suppose you have a function f() that is used only in the implementation of A, and has nothing to do with B. Is it better to put this function inside namespace N, where it is visible by both A and B, or inside the class A, where it is only visible by A?
I'd make it private in A.
Now repeat this argument, substituting a class for namespace N, and two methods for A and B, and you have a use case for local functions.
No, I would create nested namespaces specific to the requirements of functions A and B (maybe call them impl_A and impl_B). There's no *need* for local functions in this case you're proposing.
Right, so now you're introducing a new construct (impl_A) just for this purpose, because you can't do the natural thing which is to just define the functions where they are used, i.e. inside A. What if you were defining A inline in the class definition? How far would impl_A be from A?
Are you suggesting I propose it for the next standard of C++? That would be silly, as C++11 already has a solution (lambdas), and the whole point is to make the feature accessible to people who can't use C++11 yet.
See my point now?
Well that is precisely why we are proposing a library, and not a language feature - we already have the language feature, some people just can't use it yet!
Or are you suggesting that I propose a compiler-specific language extension for my compiler? That will not fly in an environment where one's code has to run of many different compilers, and in any case it goes completely against the spirit of having a language standard in the first place.
Right, so you see why I don't think Boost.Local even makes sense?
I'm sorry, I don't. While compiler-specific language extensions go against the spirit of having a language standard, libraries that emulate a language feature and work in standard C++ across platforms, such as Boost.Local, are very much in line with that spirit. This is what Boost has been doing with Foreach, Typeof, Lambda, etc.
No, sorry -- just having "prettier error messages" isn't a good measure for inclusion in Boost. You're going to have to try harder than that to make a case for a library.
So you admit that bad errors messages are a problem that users run into frequently, and that we're not likely to see a solution from the compiler side any time soon... but you say a library that solves this problem is not important enough for Boost?
Yes, they're not important enough for Boost if what it provides isn't compelling enough to be provided by Boost. If the only thing a library has is "prettier error messages when things are broken" then it's a great library for broken code -- which makes that advantage null and void because IT'S NOT AN ADVANTAGE BECAUSE YOUR CODE DOESN'T COMPILE.
Am I not getting through here?
Whoever loved a library that looked great when your code fails to compile? This is CRAZY TALK.
How often do you write code that is right the first time around? Even for an experienced programmer, and especially with a new library, more often than not your code will initially be broken. To fix it, you need to understand the problem, and you need the library's help to do so. So, since writing wrong code is just as much a reality of programming as writing working code, having a library that helps you fix your code when it's broken is just as important as having a library that runs your working code (OK, maybe not "just as important" - but quite important). Regards, Nate

On Fri, Nov 25, 2011 at 7:11 PM, Nathan Ridge <zeratul976@hotmail.com> wrote:
From: mikhailberis@gmail.com
On Fri, Nov 25, 2011 at 6:06 PM, Nathan Ridge <zeratul976@hotmail.com> wrote:
And they won't support Boost.Local functions?
If Boost.Local functions are just function objects then sure. My point is that I don't see the need for Boost.Local for me to be able to leverage these libraries in C++03 or C++11.
Who ever said the point of Boost.Local was to be able to leverage these libraries?
So what was your point in asking the question in the first place?
The point of Boost.Local is to be able define functions locally, close to where they are used, with C++ statement syntax.
You mean using C++03 statement syntax of course. And "close to where they are used" is not the same as "where they are required". Like I said already before, the difference between 0 lines (Phoenix functions defined in-lined) and at least 1 line away (Boost.Local) is *infinite* compared to 1 line away for local functions and more lines away with non-local functions.
The arguments to the transformed and filtered adaptors are all C++11 lambdas defined immediately above these lines. How do you think this code would look if I instead dropped the lambda definitions inline? Or would you have wasted extra lines and time defining these little one-or-two-liner functions that are not used anywhere else, out of line?
I think You're Doing It Wrong (tm). If I was doing this, all these function objects would be types themselves, they would be testable outside this context, and they can even be just forward declared and linked statically later on.
I don't see why they need or have to be C++11 lambdas at all if they're not just a one-or-two liner and if they have names that do make sense as types.
They *are* one-or-two-liners, and it would be overkill to define them out of line.
So why are they even chained filters? Why not just: push_back(container, range | transformed([](element_type const & e) { // put all the logic right here } | filtered([](element_type const & e) { // put all the filtering logic right here }); Since you're already using C++11? Why even have the placeholder names in place at all if you don't need it anymore? I don't get it.
Now repeat this argument, substituting a class for namespace N, and two methods for A and B, and you have a use case for local functions.
No, I would create nested namespaces specific to the requirements of functions A and B (maybe call them impl_A and impl_B). There's no *need* for local functions in this case you're proposing.
Right, so now you're introducing a new construct (impl_A) just for this purpose, because you can't do the natural thing which is to just define the functions where they are used, i.e. inside A.
What if you were defining A inline in the class definition? How far would impl_A be from A?
So here's the thing: from a design perspective, why do you even need a local function at all? What would you need that local function for? Is it going to be a parameter to an STL algorithm? If so make it testable in isolation (from an engineering standpoint) and make it something that stands alone (from a design standpoint). Is it going to be called like a normal function? If so then why not make them normal functions that are not local and be done with it? If you really needed that function defined in-line as a lambda function then we already have libraries for that. I don't see what the need is for yet another library to do exactly the same thing.
Are you suggesting I propose it for the next standard of C++? That would be silly, as C++11 already has a solution (lambdas), and the whole point is to make the feature accessible to people who can't use C++11 yet.
See my point now?
Well that is precisely why we are proposing a library, and not a language feature - we already have the language feature, some people just can't use it yet!
But it's not lambda's you want to use, it's local functions (which aren't really even local functions) so there's no point in having this library at all!
Or are you suggesting that I propose a compiler-specific language extension for my compiler? That will not fly in an environment where one's code has to run of many different compilers, and in any case it goes completely against the spirit of having a language standard in the first place.
Right, so you see why I don't think Boost.Local even makes sense?
I'm sorry, I don't. While compiler-specific language extensions go against the spirit of having a language standard, libraries that emulate a language feature and work in standard C++ across platforms, such as Boost.Local, are very much in line with that spirit. This is what Boost has been doing with Foreach, Typeof, Lambda, etc.
So Foreach came at a time when there wasn't even a discussion on C++0x. Same for typeof, lambda, shared_ptr, etc. -- it's not the same situation now, where someone's proposing a library that's immediately antiquated and superseded by a language feature. *This* is why I don't think it even makes sense to have Boost.Local at all because, frankly, we've been fine without local functions since 2003 -- what's another couple of years?
So you admit that bad errors messages are a problem that users run into frequently, and that we're not likely to see a solution from the compiler side any time soon... but you say a library that solves this problem is not important enough for Boost?
Yes, they're not important enough for Boost if what it provides isn't compelling enough to be provided by Boost. If the only thing a library has is "prettier error messages when things are broken" then it's a great library for broken code -- which makes that advantage null and void because IT'S NOT AN ADVANTAGE BECAUSE YOUR CODE DOESN'T COMPILE.
Am I not getting through here?
Whoever loved a library that looked great when your code fails to compile? This is CRAZY TALK.
How often do you write code that is right the first time around?
Why does this matter?
Even for an experienced programmer, and especially with a new library, more often than not your code will initially be broken. To fix it, you need to understand the problem, and you need the library's help to do so.
This is anecdotal. Hardly convincing in a *logical* and *rational* standpoint.
So, since writing wrong code is just as much a reality of programming as writing working code, having a library that helps you fix your code when it's broken is just as important as having a library that runs your working code (OK, maybe not "just as important" - but quite important).
You got it wrong here: having a *compiler* that helps you fix your code when it's broken is what you want, not a library! *sigh* This is getting tiring because I just keep repeating myself. This is useless. Cheers -- Dean Michael Berris http://goo.gl/CKCJX

AMDG On 11/25/2011 01:24 AM, Dean Michael Berris wrote:
On Fri, Nov 25, 2011 at 7:11 PM, Nathan Ridge <zeratul976@hotmail.com> wrote:
Even for an experienced programmer, and especially with a new library, more often than not your code will initially be broken. To fix it, you need to understand the problem, and you need the library's help to do so.
This is anecdotal. Hardly convincing in a *logical* and *rational* standpoint.
So, since writing wrong code is just as much a reality of programming as writing working code, having a library that helps you fix your code when it's broken is just as important as having a library that runs your working code (OK, maybe not "just as important" - but quite important).
You got it wrong here: having a *compiler* that helps you fix your code when it's broken is what you want, not a library!
*sigh*
This is getting tiring because I just keep repeating myself. This is useless.
Of course you're getting nowhere. The idea that horrible template error messages are all the compiler's fault and have nothing to do with the library is utterly insane. The compiler cannot generate truly good error messages, because it doesn't have enough information. A good error message would a) Indicate the point of the actual user error. b) Indicate how the code is wrong. (a) requires the compiler to be able to distinguish between library bugs and user errors. (b) requires the compiler to understand the domain of the library. Both of these are impossible for all practical purposes. I have yet to find a compiler that can read my mind. If anything you can blame the C++ language for not being able to express these constraints well. In Christ, Steven Watanabe

On Fri, Nov 25, 2011 at 3:11 AM, Nathan Ridge <zeratul976@hotmail.com> wrote:
Yes, they're not important enough for Boost if what it provides isn't compelling enough to be provided by Boost. If the only thing a library has is "prettier error messages when things are broken" then it's a great library for broken code -- which makes that advantage null and void because IT'S NOT AN ADVANTAGE BECAUSE YOUR CODE DOESN'T COMPILE.
Am I not getting through here?
Whoever loved a library that looked great when your code fails to compile? This is CRAZY TALK.
How often do you write code that is right the first time around? Even for an experienced programmer, and especially with a new library, more often than not your code will initially be broken. To fix it, you need to understand the problem, and you need the library's help to do so.
IMO, a library like any other software development tool, should assist us in the software development /process/. Having a code that compiles and later that runs with no known bugs are just two stages of such a process. Another earlier stage is to have code that does not compile. This stage is still part of the software development process, and I'd expect libraries and other tools (editors, compiler errors, etc) to assist us in making the code compile. Therefore, I reject the argument "which makes that advantage null and void because IT'S NOT AN ADVANTAGE BECAUSE YOUR CODE DOESN'T COMPILE", on the contrary the advantage remains as a better tool that help you making the code to compile with no error and therefore serves one step of the overall software development process. HTH, --Lorenzo

On Fri, Nov 25, 2011 at 9:58 PM, Lorenzo Caminiti <lorcaminiti@gmail.com> wrote:
IMO, a library like any other software development tool, should assist us in the software development /process/.
What is a library but code? It's not a *tool* as it's not something you use to achieve a goal, it's a *building block* an *ingredient* to the solution. Do you need examples to make it clear? The compiler is a tool -- so is your editor, your computer, your web browser, your operating system, etc. A library is an ingredient -- so is every other piece of code you write. To make it even clearer, look at a blender and whatever you can put in a blender -- the blender is the tool and the stuff you put in are the ingredients to whatever you want to make. The tool should assist you in the process of making software -- the ingredients are just ingredients. Now, if you don't know how to use the tools properly (or if you're cooking, don't know how to cook), it doesn't matter what your ingredients are because if you're doing it wrong then whatever comes out of it doesn't count. If you did not achieve the desired result, it's no fault of the tool or the ingredients. How does the ingredient assist you in the process of making food aside from its mere presence? See how illogical that question is?
Having a code that compiles and later that runs with no known bugs are just two stages of such a process. Another earlier stage is to have code that does not compile. This stage is still part of the software development process, and I'd expect libraries and other tools (editors, compiler errors, etc) to assist us in making the code compile.
What does the library have anything to do with the compiler?! Again, the compiler makes the error messages and you are writing incorrect code. I do not see how it's the fault of the library if the library's provably not at fault (i.e. doesn't have bugs).
Therefore, I reject the argument "which makes that advantage null and void because IT'S NOT AN ADVANTAGE BECAUSE YOUR CODE DOESN'T COMPILE", on the contrary the advantage remains as a better tool that help you making the code to compile with no error and therefore serves one step of the overall software development process.
You can reject anything you want to reject but if you can't put forward a logical argument to support your proposition, then I'm afraid it's going to be hard to convince anybody.
HTH,
I hoped so too, unfortunately it kinda doesn't. Cheers -- Dean Michael Berris http://goo.gl/CKCJX

On Fri, Nov 25, 2011 at 6:13 AM, Dean Michael Berris <mikhailberis@gmail.com> wrote:
On Fri, Nov 25, 2011 at 9:58 PM, Lorenzo Caminiti <lorcaminiti@gmail.com> wrote:
IMO, a library like any other software development tool, should assist us in the software development /process/.
What is a library but code? It's not a *tool* as it's not something you use to achieve a goal, it's a *building block* an *ingredient* to the solution.
I disagree. Yes the library is just code as the rest of the program.
From my experience all code is a tool to solve a problem, that includes the library. Code is not the only tool, compiler, debugger, editor, also are. They all serve me as a tool to solve a problem.
--Lorenzo

On Fri, Nov 25, 2011 at 10:28 PM, Lorenzo Caminiti <lorcaminiti@gmail.com> wrote:
On Fri, Nov 25, 2011 at 6:13 AM, Dean Michael Berris <mikhailberis@gmail.com> wrote:
On Fri, Nov 25, 2011 at 9:58 PM, Lorenzo Caminiti <lorcaminiti@gmail.com> wrote:
IMO, a library like any other software development tool, should assist us in the software development /process/.
What is a library but code? It's not a *tool* as it's not something you use to achieve a goal, it's a *building block* an *ingredient* to the solution.
I disagree. Yes the library is just code as the rest of the program.
Wait, you disagree but yes?
From my experience all code is a tool to solve a problem, that includes the library. Code is not the only tool, compiler, debugger, editor, also are. They all serve me as a tool to solve a problem.
Okay, let me rephrase my point: Code is not involved in the software development *process* as a tool, it is an ingredient in the software. The resulting software then is the solution to the problem. Anyway, it's useless to keep arguing this point because it's largely irrelevant. Let's just agree to disagree and leave it there. Cheers -- Dean Michael Berris http://goo.gl/CKCJX

AMDG On 11/25/2011 03:42 AM, Dean Michael Berris wrote:
On Fri, Nov 25, 2011 at 10:28 PM, Lorenzo Caminiti <lorcaminiti@gmail.com> wrote:
On Fri, Nov 25, 2011 at 6:13 AM, Dean Michael Berris <mikhailberis@gmail.com> wrote:
On Fri, Nov 25, 2011 at 9:58 PM, Lorenzo Caminiti <lorcaminiti@gmail.com> wrote:
IMO, a library like any other software development tool, should assist us in the software development /process/.
What is a library but code? It's not a *tool* as it's not something you use to achieve a goal, it's a *building block* an *ingredient* to the solution.
I disagree. Yes the library is just code as the rest of the program.
Wait, you disagree but yes?
From my experience all code is a tool to solve a problem, that includes the library. Code is not the only tool, compiler, debugger, editor, also are. They all serve me as a tool to solve a problem.
Okay, let me rephrase my point:
Code is not involved in the software development *process* as a tool, it is an ingredient in the software. The resulting software then is the solution to the problem.
What is there not to understand? The important distinction is between my code and everything else, not between code and tools (arbitrarily defined) In Christ, Steven Watanabe

Dean Michael Berris wrote:
If the only thing a library has is "prettier error messages when things are broken" then it's a great library for broken code -- which makes that advantage null and void because IT'S NOT AN ADVANTAGE BECAUSE YOUR CODE DOESN'T COMPILE.
Am I not getting through here?
Whoever loved a library that looked great when your code fails to compile? This is CRAZY TALK.
Since errors are inevitable, libraries that trigger readily understood errors when misused are preferable to those that trigger painful error messages, all else being equal. Sometimes, enduring the error message pain is worthwhile because of other factors such as performance or additional, desirable functionality. Discussing error messages to which a library is prone when used erroneously, it decidedly not crazy. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com ________________________________ IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

I use this construct frequently in my C++11 projects. It can serve to make the code more readable. Here's an example of a line of code I wrote recently. It makes use of Boost.Range in addition to C++11 lambdas:
vector<message> messages; push_back(messages, message_ids | transformed(lookup_message_by_id) | filtered(newer_than_one_year) | filtered(not_hidden) | filtered(satisfies_user_preference));
I also often use this kind of constructs: it is for me more readable (in this example) than inline lambdas, and more importantly, named lambdas are necessary when I have to reuse them in the same enclosing function, for example: push_back(hidden_messages, ... | filtered(newer_than_one_year) | filtered(hidden) | filtered(satisfies_user_preference)); Without c++11 lambdas, I would be very happy to use Boost.Local (whether it is accepted in Boost or not, I have no opinion on this issue), because these named lambdas: - don't need to be tested separately (one-or-two liners) - only belong to the logic of the enclosing function (I would prefer using copy/paste to reuse a (small) block of code rather than polluting the outer scope) - can bind to local variables, and can be very easily replaced by c++11 lambdas when they become available to me. Rafaël

If you have any specific suggestions about what compilers could do to turn these errors from deep within the library implementation, into errors that do not require knowing anything about the library implementation, I would like to hear it, and I'm sure so would compiler writers.
Oh believe me they've heard it from me (or at least I've made some noise about it already). I can make more noise and maybe send in patches but there's other things in this world that concern me (and others) to actually do anything substantial in other fronts.
Could you point me to some links? I am genuinely interested.
Just this one: http://cplusplus-soup.com/2010/11/21/c-hating/ and look at the comments too if you have enough time. :)
I read this article and I don't see any suggestions about how to get compilers to make TMP error messages user-friendly. (Admittedly, I haven't read all the comments as there are very many). So until you point me to something more specific, I remain unconvinced that there's anything substantial that compilers can do about this. But, even if we accept, for discussion's sake, that there are solutions from the compiler side...
as writing working code, having a library that helps you fix your code when it's broken is just as important as having a library that runs your working code (OK, maybe not "just as important" - but quite important).
You got it wrong here: having a *compiler* that helps you fix your code when it's broken is what you want, not a library!
... the reality is that solutions from the compiler side are not forthcoming! So, aren't you being a tad idealistic to reject a solution from the library side because, in your opinion, such a solution is not ideal? Regards, Nate

On 24 November 2011 16:23, Nathan Ridge <zeratul976@hotmail.com> wrote:
We already know your position about (2): that now C++11 is here, we should be moving on and stop giving attention to C++03 problems which are solved by C++11. I, on the other hand, think the reality is that C++03 will be around for a while longer, and it's worth caring about the C++03 experience evem if there are C++11 solutions.
While it may be around for a while, the reasons for not upgrading to C++11 also stops most of those people from upgrading their version of Boost, compiler, etc. The user base that can continually upgrade Boost but nothing else will be shrinking quickly, IMHO. -- Nevin ":-)" Liber <mailto:nevin@eviloverlord.com> (847) 691-1404

From: nevin@eviloverlord.com
On 24 November 2011 16:23, Nathan Ridge <zeratul976@hotmail.com> wrote:
We already know your position about (2): that now C++11 is here, we should be moving on and stop giving attention to C++03 problems which are solved by C++11. I, on the other hand, think the reality is that C++03 will be around for a while longer, and it's worth caring about the C++03 experience evem if there are C++11 solutions.
While it may be around for a while, the reasons for not upgrading to C++11 also stops most of those people from upgrading their version of Boost, compiler, etc. The user base that can continually upgrade Boost but nothing else will be shrinking quickly, IMHO.
There is a big difference between upgrading a library and switching to a new language specification. For starters, C++03 and C++11 are ABI- incompatible on most implementations that I know of. That means that before you can even compile your code in C++11 mode, every library and piece of code that your code links to must also be recompiled in C++11 mode. You can see how that might take a while to happen in a large organization. Regards, Nate

On 24 November 2011 22:51, Nathan Ridge <zeratul976@hotmail.com> wrote:
There is a big difference between upgrading a library and switching to a new language specification. For starters, C++03 and C++11 are ABI- incompatible on most implementations that I know of.
Is this documented somewhere? I've seen ABI changes based on compiler releases, but not based on whether or not the C++0x flag is set. I'd love to be as informed as you are on this.
That means that before you can even compile your code in C++11 mode, every library and piece of code that your code links to must also be recompiled in C++11 mode.
Sure sounds like when someone changes a version of Boost, they end up having to recompile the world both to avoid ODR violations and to make sure things link correctly. (Or course, if they have pieces of code that don't use Boost those can remain unchanged.) -- Nevin ":-)" Liber <mailto:nevin@eviloverlord.com> (847) 691-1404

From: nevin@eviloverlord.com Subject: Re: [boost] New libraries implementing C++11 features in C++03
On 24 November 2011 22:51, Nathan Ridge <zeratul976@hotmail.com> wrote:
There is a big difference between upgrading a library and switching to a new language specification. For starters, C++03 and C++11 are ABI- incompatible on most implementations that I know of.
Is this documented somewhere? I've seen ABI changes based on compiler releases, but not based on whether or not the C++0x flag is set. I'd love to be as informed as you are on this.
This thread describes the situation nicely for GCC: http://gcc.gnu.org/ml/gcc/2011-10/msg00113.html. I haven't been following clang as closely, but I think they're doing somewhat better in that the libc++ ABI is the same in both modes.
That means that before you can even compile your code in C++11 mode, every library and piece of code that your code links to must also be recompiled in C++11 mode.
Sure sounds like when someone changes a version of Boost, they end up having to recompile the world both to avoid ODR violations and to make sure things link correctly. (Or course, if they have pieces of code that don't use Boost those can remain unchanged.)
I'm no expert on this topic, but I think as long as calls across module boundaries do not take parameters of Boost types, the modules can be compiled with different versions of Boost... I could be wrong though. And of course, for header-only libraries like Boost.Local there should be no problem whatsoever. Regards, Nate

On 25.11.2011 08:17, Nathan Ridge wrote:
I haven't been following clang as closely, but I think they're doing somewhat better in that the libc++ ABI is the same in both modes. Easy enough - libc++ doesn't have any legacy.
Sebastian
participants (18)
-
Brent Spillner
-
Christopher Jefferson
-
Dean Michael Berris
-
Gregory Crosswhite
-
Hartmut Kaiser
-
Jeffrey Lee Hellrung, Jr.
-
Joel de Guzman
-
Lorenzo Caminiti
-
Nathan Ridge
-
Nevin Liber
-
Peter Dimov
-
Rafaël Fourquet
-
Sebastian Redl
-
Steven Watanabe
-
Stewart, Robert
-
Thomas Heller
-
Thomas Klimpel
-
Vicente Botet