
On Sat, Feb 12, 2011 at 5:50 AM, John Bytheway <jbytheway+boost@gmail.com> wrote:
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>
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...
Yes, the macros generate symbols like `boost_local...<__LINE__>`. I tripped the prefixes in the code posted here just to make the code more readable.
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.
No, it doesn't. I can strip all the unnecessary stuff.
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.
Yes, I can remove it. I can use the TYPEOF on the `factorial` member variable that is used to support recursion.
// 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.
It could be static in theory but unfortunately local classes cannot have static member variables in C++ :( This is actually VERY unfortunate because if I can make the `factorial` member variable static then I can make the `body` function static so programmers cannot mistakenly use `this` instead of `this_` in the body code (yhe `body` needs to access the `factorial` member functor for recursive calls -- see LIMITATION in my code comments below).
// 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
OK, I'll take a look at what can be declared const (maybe after I have fully implemented the new macros so I can regress the addition of const against all the examples).
BOOST_TYPEOF(object_factorial.factorial) and omit deduce_function_type entirely? Perhaps you're doing it this way to support a non-recursive variant.
Yep, done.
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.
OK, I understand. Yes, the code should be optimized for C++0x/C++03 (it should be easy to do). I'll work on these compiler-specific optimizations if the library gets accepted. Making the above changes, the code becomes: 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 auto int n #define PARAMS_arg_with_dflt_0 PARAMS_arg_0 // No default. #define PARAMS_arg_1 register 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::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 = { // Must use this initializer because reference members. #if defined(__GNUC__) { // NOTE: Curly brakets are required by GCC but not supported by MSVC. #endif boost::scope_exit::aux::deref(PARAMS_bind_0, static_cast<bind_deduce_type_0_29>(0)) #if defined(__GNUC__) } // NOTE: Curly brakets are required by GCC but not supported by MSVC. #endif }; // 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> { public: 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: // Non local functor type that can be passed as template parameter. typedef ::boost::local::function_ref<function_type_29, 1> functor_type; // Hold bound parameter values. binds_29* binds_; // 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 (specified by programmers outside the macros). { int result = 0; if (n < 2 ) result = 1; else result = n * factorial(n - 1, true); // Recursive call. 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` and/or line number `__LINE__` // (e.g., `..._31`) to generate unique symbols. // * Function name `factorial` available. // * None of the `..._29` symbols are available (different `__LINE__`). // 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; } public: // Member with function name for recursive calls. This cannot be // defined sooner because the function name is only available here. // Also, it is public so it can be used to deduce the functor type // outside the expansion of the PARAMS macro (where the PARAMS symbols // are no longer available because of the different __LINE__ number). functor_type factorial; } functor_factorial_31(boost_local_auxXargs.value); // The struct type cannot be passed as template parameter but the function // type `function_ref<...>` deduced here can. BOOST_TYPEOF(functor_factorial_31.factorial) factorial( functor_factorial_31); // Rest of the program. // Factorial of a single number. factorial(3); // Call local function. std::vector<int> v; v.resize(3); v[0] = 1; v[1] = 4; v[2] = 7; // Factorials of all vector's elements. std::for_each(v.begin(), v.end(), factorial); // Pass as template parameter. std::cout << output.str() << std::endl; return 0; } -- Lorenzo