
On Sun, Feb 6, 2011 at 3:23 PM, Jeffrey Lee Hellrung, Jr. <jhellrung@ucla.edu> 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