
On Fri, Feb 11, 2011 at 4:31 PM, Lorenzo Caminiti <lorcaminiti@gmail.com> wrote:
From: John Bytheway <jbytheway+boost@gmail.com>
Hi John, first of all, thank you very much for checking my code. Your feedback has been very helpful!
Date: Fri, 11 Feb 2011 20:36:40 +0000 Subject: Re: [boost] [local] Simplifying the parenthesized syntax To: boost@lists.boost.org
On 11/02/11 18:19, Lorenzo Caminiti wrote:
On Thu, Feb 10, 2011 at 4:13 PM, John Bytheway <jbytheway+boost@gmail.com> wrote: binds = static_cast<binds_29*>(bind_params);
I'm puzzled; you say "None of the `..._29` symbols are available" but you're using both function_type_29 and binds_29 here...
Yes, you are right. I cut-n-pasted the code with the `..._29`s from the constructor... Actually, I can move all the code into the constructor (this way the functors is also always well defined): int main () { std::ostringstream output; int // Result type (outside the `PARAMS` macro). // On all C++ preprocessors (including C99 preprocessors) the macro: // // int BOOST_LOCAL_FUNCTION_PARAMS( // (int n) (bool recursion)(default false) (bind& output) ) // // Or, on C99 preprocessors only the macro: // // int BOOST_LOCAL_FUNCTION_PARAMS( // int n, bool recursion, default false, bind& output) // // Expands to code equivalent to the following. // // NOTE: // * Use line number __LINE__ (e.g., 29) to generate unique symbols. // * Parameter name not available separately from its type. // * Function name NOT available. // In actual expansion, the following tokens are made available as macro // parameters (and not as macro symbols) by the `PARAMS` macro: #define PARAMS_arg_0 int n #define PARAMS_arg_with_dflt_0 PARAMS_arg_0 // No default. #define PARAMS_arg_1 bool recursion #define PARAMS_arg_with_dflt_1 PARAMS_arg_1 = false #define PARAMS_bind_0 &output #define PARAMS_is_const_bind_0 0 // Not a constant bind. // Function traits. // NOTE: Following result type specified just before the `PARAMS` macro. // Default parameter values need to be separated from their parameter types // and names because they are not part of the function type so they cannot // be used by the following expressions and their number cannot be count // at compile-time using template metaprogramming. ERROR_missing_result_type_at_line_29(PARAMS_arg_0, PARAMS_arg_1); /** @todo This typeof requires registration of result, arg, etc type? */ typedef BOOST_TYPEOF(ERROR_missing_result_type_at_line_29) function_type_29; typedef boost::function_traits<function_type_29>::result_type result_type_29; typedef boost::function_traits<function_type_29>::arg1_type arg_type_0_29; typedef boost::function_traits<function_type_29>::arg2_type arg_type_1_29; // Handle bound parameters as done by Boost.ScopeEixt, deducing their types // (using Boost.Typeof) and storing them by reference or by value. typedef void (*bind_deduce_type_0_29)(int PARAMS_bind_0); typedef BOOST_TYPEOF(boost::type_of::ensure_obj( boost::scope_exit::aux::wrap(boost::scope_exit::aux::deref( PARAMS_bind_0, static_cast<bind_deduce_type_0_29>(0))))) bind_wrapped_type_0_29; typedef bind_wrapped_type_0_29::type capture_bind_type_0_29; // Hold bound parameter types and values. struct binds_29 { typedef capture_bind_type_0_29 bind_type_0_29; boost::scope_exit::aux::member<bind_type_0_29, bind_deduce_type_0_29> bind_value_0_29; } params_29 = { { boost::scope_exit::aux::deref(PARAMS_bind_0, static_cast<bind_deduce_type_0_29>(0)) } }; // NOTE: The `args` variable is declared globally and not prefixed with // __LINE__ so it can be used by both the `PARAMS` and `NAME`. The special // template declaration type prevents this variable to be declared multiple // times within the same scope. boost::scope_exit::aux::declared<boost::scope_exit::aux::resolve< sizeof(boost_local_auxXargs)>::cmp1<0>::cmp2> boost_local_auxXargs; boost_local_auxXargs.value = ¶ms_29; // Functor for local function. class functor_29: // Base used to assign local functions to `function_ref` which can // then be passed as template parameter. public ::boost::local::aux::function_base<function_type_29, 1> { typedef ::boost::local::function_ref<function_type_29, 1> function_type; binds_29* binds_; // Bound parameter values. public: // Public so it can be used later to deduce function type where // __LINE__ postfixed symbols `..._29` are no longer available. function_type* deduce_function_type; explicit functor_29(void* binds): binds_(static_cast<binds_29*>(binds)) { init_recursion(); } result_type_29 operator()(arg_type_0_29 arg_0, arg_type_1_29 arg_1) { assert(binds_); return body( binds_->bind_value_0_29.value // Using general names `arg_i` because parameter types and // names are not separated by the preprocessor so the actual // argument name (e.g., `n`) is not available here. , arg_0, arg_1 ); } // Overloading to support default parameters. result_type_29 operator()(arg_type_0_29 arg_0) { assert(binds_); return body( binds_->bind_value_0_29.value , arg_0 ); } private: // LIMITATION: Body cannot be static because it has to access the // member named after the function name for recursive calls (the // function name is not know to this macro). However, ideally the body // will be static so to prevent using `this` instead of `this_` by // mistake (in most cases this will still cause a compile-time error // because when functor has a different structure than the bound object // `this_` -- but that is not the case if `this` is mistakenly used // instead of `this` to do pointer operations). Programmers need to // inspect the local function body code by eye and make sure that // `this` is not used by the body code. result_type_29 body( #if PARAMS_is_const_bind_0 ::boost::add_const< // Handle constant binding. #endif binds_29::bind_type_0_29 #if PARAMS_is_const_bind_0 >::type #endif PARAMS_bind_0 , PARAMS_arg_with_dflt_0 , PARAMS_arg_with_dflt_1) // Local function body (programmed outside the macros). { int result = 0; if (n < 2 ) result = 1; else result = n * factorial(n - 1, true); if (!recursion) output << result << " "; return result; } // All `..._29` and `PARAMS_...` symbols are only available for within `PARAMS` // macro expansion for the code above. #undef PARAMS_arg0 #undef PARAMS_dflt0 #undef PARAMS_arg1 #undef PARAMS_dflt1 #undef PARAMS_bind0 // The macro: // // BOOST_LOCAL_FUNCTION_NAME(factorial) // // Expands to code equivalent to the following. Note: // // * Use function name `factorial` to generate unique symbols. // * Function name `factorial` available. // * None of the `..._29` symbols are available (different __LINE__). // Member with function name for recursive calls. This cannot be // defined sooner because the function name is only available here. function_type factorial; // Cannot be programmed in the constructor because it sets the // `factorial` member with name only known in this macro expansion. void init_recursion() { factorial = *this; } } object_factorial(boost_local_auxXargs.value); // The struct type cannot be passed as template parameter but the function // type `function_ref<...>` deduced here can. BOOST_TYPEOF(*object_factorial.deduce_function_type) factorial( object_factorial); // Rest of the program. std::vector<int> v; v.resize(3); v[0] = 1; v[1] = 4; v[2] = 7; std::for_each(v.begin(), v.end(), factorial); std::cout << output.str() << std::endl; return 0; }
factorial = *this; // For recursion. } } object_factorial; object_factorial.init(boost_local_auxXargs.value); BOOST_TYPEOF(object_factorial.factorial) factorial(object_factorial);
I imagine you can get rid of this TYPEOF call with function_traits too (with e.g. an appropriately defined member function in object_factorial?).
I am not sure I understand... how would I do this? Even when I use function_traits above, the function type is determined using TYPEOF... int ERROR_missing_result_type_at_line_29(PARAMS_arg_0, PARAMS_arg_1); typedef BOOST_TYPEOF(ERROR_missing_result_type_at_line_29) function_type_29; // <-- Type of here. typedef boost::function_traits<function_type_29>::result_type result_type_29; typedef boost::function_traits<function_type_29>::arg1_type arg_type_0_29; typedef boost::function_traits<function_type_29>::arg2_type arg_type_1_29; Does this TYPEOF requires registration of the result, arg, etc types?
BTW, is there value in making functor_29's operator() and/or body functions inline?
It depends what you mean by "making" them inline. They're class member functions defined in a class body, so they're already *declared* inline. Adding the inline keyword will make no difference.
However, IIRC from my previous experiments icc will not inline the virtual call through local::function<>, even though I think the standard allows it (and if icc won't do it, I doubt many other compilers will), so in practice they won't actually be inline (but should have only one indirection, which is good).
Yes, I see. Then it is useless to provide a syntax that allows to optionally declare the local function inline: (int) (inline) (factorial)(...) // No use for this.
I think two questions remain for you to consider:
1. Might anyone might want them *not* to be inline, because they're afraid of code bloat, and their compiler optimizer is so clever that it will inline them? I doubt this is an issue because (a) I don't know a compiler that clever and (b) a compiler that clever should be taking the risk of code bloat into effect.
I agree.
2. Might anyone care so much about performance that they absolutely must have the code inlined? The answer is probably "yes" (or at least there will be people who *think* they care, which is also a problem). For these people you could, if you choose, provide an alternate implementation which doesn't indirect through local::function<>, and thus will only work in C++0x (or C++03 with non-standard extensions).
Without inheriting from local::function_base<> and then using local::function_ref<> the local struct cannot be passed as a template parameter in C++. Is this different for C++0x (C++03)? Otherwise, I am not sure I understand your point.
Regardless, as I said last time, you should do some profiling and put the results in your docs so that both you and your users can make informed decisions.
Yes, I will profile the code and add the analysis to the docs after I settle down the macro API and a reasonably stable implementation -- so hopefully soon :) Thanks again! -- Lorenzo