Boost logo

Boost :

Subject: Re: [boost] Rave for proposed Boost.Local (functions)
From: Lorenzo Caminiti (lorcaminiti_at_[hidden])
Date: 2011-02-06 20:46:56


On Sun, Feb 6, 2011 at 3:23 PM, Jeffrey Lee Hellrung, Jr.
<jhellrung_at_[hidden]> wrote:
> On 2/6/2011 9:17 AM, Lorenzo Caminiti wrote:
> [...]
>>
>> I am posting here the code generated by the macros which might help
>> curious readers in spotting costly operations performed by Boost.Local
>> (if there are any). This of course does not include the preprocessing
>> time. (I hope it's not too much code... just try to get passed the
>> typeof noise at the beginning...)
>
> Is the use of Boost.Typeof optional?  I.e., can I just provide the types of
> the arguments and bound variables myself?

No but I think you can simply use a normal (not bound) local function
parameters to do this:

 int main() {
    double sum = 0.0;
    int factor = 10;

    BOOST_LOCAL_FUNCTION(
    (void) (add)( (double)(num) (const int)(factor) (double&)(sum) )
// No binds so no typeof.
    ) {
        sum += factor * num;
        std::clog << "Summed: " << sum << std::endl;
    } BOOST_LOCAL_FUNCTION_END(add)
    add(100.0, factor, sum);
}

>> int main() {
>>     double sum = 0.0;
>>     int factor = 10;
>>
>>     // BOOST_LOCAL_FUNCTION( (void) (add)( (double)(num) (const
>> bind)((factor)) ) expands to:
>>     // Determining bound variable types (taken from Boost.ScopeExit).
>>     typedef void (*boost_se_tag_0_add36)(int factor );
>>     typedef void (*boost_se_tag_1_add36)(int&sum );
>
> Where did sum come from???  I don't see it in the BOOST_LOCAL_FUNCTION( ...
> ) line...I guess you're *really* showing the expansion of a similar macro
> invocation, but with sum also bound...?

Ooops, I missed it. The code I posted is the expansion of this:

int main() {
    double sum = 0.0;
    int factor = 10;

    BOOST_LOCAL_FUNCTION(
    (void) (add)( (double)(num) (const bind)((factor)) (bind)((&sum)) )
    ) {
        sum += factor * num;
        std::clog << "Summed: " << sum << std::endl;
    } BOOST_LOCAL_FUNCTION_END(add)
    add(100.0);
}

>>     typedef
>> __typeof__(boost::type_of::ensure_obj(boost::scope_exit::aux::wrap(
>>             boost::scope_exit::aux::deref(factor,
>> (boost_se_tag_0_add36)0))))
>>             boost_se_wrapped_t_0_add36;
>>     typedef boost_se_wrapped_t_0_add36::type boost_se_capture_t_0_add36;
>>     typedef
>> __typeof__(boost::type_of::ensure_obj(boost::scope_exit::aux::wrap(
>>             boost::scope_exit::aux::deref(&sum,
>> (boost_se_tag_1_add36)0))))
>>             boost_se_wrapped_t_1_add36;
>>     typedef boost_se_wrapped_t_1_add36::type boost_se_capture_t_1_add36;
>
> I have a feeling there should be a typename here when
> boost_se_wrapped_t_1_add36 is a dependent type...

Yes, you use `BOOST_LOCAL_FUNCTION_TPL` in dependent type contexts
(from within templates, etc) which adds the typenames. (It's in the
docs.)

>>     struct boost_se_params_t_add36 {
>>         typedef boost_se_capture_t_0_add36 boost_se_param_t_0_add36;
>>         typedef boost_se_capture_t_1_add36 boost_se_param_t_1_add36;
>>         boost::scope_exit::aux::member<  boost_se_param_t_0_add36,
>> boost_se_tag_0_add36>
>>                 boost_se_param_0_add36;
>>         boost::scope_exit::aux::member<  boost_se_param_t_1_add36,
>> boost_se_tag_1_add36>
>>                 boost_se_param_1_add36;
>>     } boost_local_auxXparams_add36 = {
>>           { boost::scope_exit::aux::deref(factor, (boost_se_tag_0_add36)0)
>> }
>>         , { boost::scope_exit::aux::deref(&sum, (boost_se_tag_1_add36)0) }
>>     };
>>     boost::scope_exit::aux::declared<  boost::scope_exit::aux::resolve<
>>             sizeof(boost_local_auxXargs)>::cmp1<0>::cmp2>
>> boost_local_auxXargs;
>
> Shouldn't the name boost_local_auxXargs have some kind of line number or
> counter or function name pasted into it to prevent name collision of
> multiple BOOST_LOCAL_FUNCTION declarations in the same scope?

No, this has to be a global extern variable declared as:

    extern boost::scope_exit::aux::undeclared boost_local_auxXargs;

in one of Boost.Local headers. It's a trick carried over 100% from
Boost.ScopeExit (as I understand it, this is because only global or
static variables can be used from within a nested block
http://lists.boost.org/Archives/boost/2006/11/113658.php).

>>     boost_local_auxXargs.value =&boost_local_auxXparams_add36;
>>     // Functor for the local function.
>>     typedef ::boost::local::function_ref<  void ( double ) , 0>
>> boost_local_auxXaddXref;
>>     struct boost_local_auxXfunctorXadd :
>>             ::boost::local::aux::function_base<  void ( double ), 0>  {
>>         explicit boost_local_auxXfunctorXadd(void* bind_params) :
>>                 boost_local_auxXparams( (boost_se_params_t_add36*)
>> bind_params) {}
>
> static_cast ?  Just being picky here... ;)

Ooops, again. I'll fix this C-style cast (it probably was a quick
piece of code that I then forgot to go back and fix). Thanks.

>>         void operator()( double num ) {
>>             return boost_local_auxXbody( *this
>>                 , boost_local_auxXparams->  boost_se_param_0_add36.value
>>                 , boost_local_auxXparams->  boost_se_param_1_add36.value
>>                 , num
>>             );
>>         }
>>     private:
>>         boost_se_params_t_add36* boost_local_auxXparams;
>>         static void boost_local_auxXbody( boost_local_auxXfunctorXadd&
>>  add
>>             , ::boost::add_const<  boost_se_params_t_add36::
>> boost_se_param_t_0_add36>::type factor
>>             , boost_se_params_t_add36:: boost_se_param_t_1_add36&sum
>>             , double num
>>         )
>>
>>     // Programmers' write the local function body.
>>     {
>>         sum += factor * num;
>>         std::clog<<  "Summed: "<<  sum<<  std::endl;
>>     }
>>
>>     // BOOST_LOCAL_FUNCTION_END(add) expands to:
>>     };
>>     boost_local_auxXfunctorXadd boost_local_auxXobjectXadd(
>>             boost_local_auxXargs.value);
>>     boost_local_auxXaddXref add( boost_local_auxXobjectXadd); //
>> Finally the actual functor...
>>
>>     add(100.0);
>>
>>     return 0;
>> }
>>
>
> For maintenance purposes, do you plan on providing a brief sketch of the
> above mechanism (e.g., the main players in the above macro expansion, and
> the inheritance from function_base and type-erasure to simulate the binding
> of local functions to template parameters)?

Yes, I can add an Appendix with the pseudo code in the library docs.

> I remember you doing an earlier study on the performance impact of using
> this type erasure (compared to moving the local function to namespace scope
> and avoid the overhead of the virtual dispatch)...is that included in the
> documentation?  I don't see it from a quick glance...

A preliminary study was hinted by John Bytheway
(http://lists.boost.org/Archives/boost/2010/09/170891.php). I have not
done any performance study of Boost.Local yet (yes, I will include
performance considerations in the docs once I do them).

-- 
Lorenzo

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