
On 11/02/11 22:29, Lorenzo Caminiti wrote:
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!
:)
On 11/02/11 18:19, Lorenzo Caminiti 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;
I'll suggest in passing that all of these names you're declaring at function scope should start "boost_local" to reduce the risk of clashing with any other libraries. I may be being excessively paranoid, given that you're already including the line number...
// Handle bound parameters as done by Boost.ScopeEixt, deducing their types
s/Eixt/Exit
// 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.
Does the functor have this member when there are no bound values? I hope not.
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;
I find it slightly distressing that this member is here, increasing sizeof(functor_29) even though it is only used for its type. I think it should still work as a static member, which might be better.
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;
Amusingly I think this member could be static too, although it would be rather odd and I'm not claiming it's a good idea. I'm just wondering whether minimizing sizeof(functor_29) could help optimizers. I have no particular evidence, and indeed it could make matters worse to have static members.
// 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);
You could declare object_factorial const. I feel you should.
// 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);
This could be declared const too. Also, couldn't you use BOOST_TYPEOF(object_factorial.factorial) and omit deduce_function_type entirely? Perhaps you're doing it this way to support a non-recursive variant.
// 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;
Yes, you're right. I misread Steven's suggestion; sorry. You do indeed need typeof.
Does this TYPEOF requires registration of the result, arg, etc types?
I don't know. I've never used a compiler without proper typeof() support...
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)?
Yes, in C++0x local structs can be passed as template parameters. Obviously, in C++0x there are lambdas too, so you might think your library is useless, but I'd expect most compilers to support passing local structs as template parameters before they support lambdas, so there is some advantage in having this intermediate implementation. Also, your macros would allow writing code that worked in C++0x and C++03, with the virtual function business in only those compilers/modes where it is necessary. John