Boost logo

Boost :

Subject: Re: [boost] [local] Simplifying the parenthesized syntax
From: Lorenzo Caminiti (lorcaminiti_at_[hidden])
Date: 2011-02-12 11:42:14


On Sat, Feb 12, 2011 at 5:50 AM, John Bytheway
<jbytheway+boost_at_[hidden]> wrote:
> 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]>
>>
>>    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 = &params_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

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