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