Boost logo

Boost :

Subject: Re: [boost] Case study: Boost.Local versus Boost.Phoenix
From: Lorenzo Caminiti (lorcaminiti_at_[hidden])
Date: 2011-02-05 15:52:47


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...

-- 
Lorenzo

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk