Boost logo

Boost :

Subject: Re: [boost] [local] Simplifying the parenthesized syntax
From: John Bytheway (jbytheway+boost_at_[hidden])
Date: 2011-02-12 05:50:40


On 11/02/11 22:29, Lorenzo Caminiti wrote:
> On Fri, Feb 11, 2011 at 4:31 PM, Lorenzo Caminiti <lorcaminiti_at_[hidden]> wrote:
>> From: John Bytheway <jbytheway+boost_at_[hidden]>
>
> 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


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