|
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 = ¶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
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk