|
Boost : |
Subject: Re: [boost] [local] Simplifying the parenthesized syntax
From: Lorenzo Caminiti (lorcaminiti_at_[hidden])
Date: 2011-02-11 17:29:00
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!
> Date: Fri, 11 Feb 2011 20:36:40 +0000
> Subject: Re: [boost] [local] Simplifying the parenthesized syntax
> To: boost_at_[hidden]
>
> On 11/02/11 18:19, Lorenzo Caminiti wrote:
>> On Thu, Feb 10, 2011 at 4:13 PM, John Bytheway
>> <jbytheway+boost_at_[hidden]> 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;
// 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::type_of::ensure_obj(
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 = {
{ boost::scope_exit::aux::deref(PARAMS_bind_0,
static_cast<bind_deduce_type_0_29>(0)) }
};
// 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> {
typedef ::boost::local::function_ref<function_type_29, 1> function_type;
binds_29* binds_; // Bound parameter values.
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;
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;
// 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);
// 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);
// 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;
Does this TYPEOF requires registration of the result, arg, etc types?
>> BTW, is there value in making functor_29's operator() and/or body
>> functions inline?
>
> It depends what you mean by "making" them inline. They're class member
> functions defined in a class body, so they're already *declared* inline.
> Adding the inline keyword will make no difference.
>
> However, IIRC from my previous experiments icc will not inline the
> virtual call through local::function<>, even though I think the standard
> allows it (and if icc won't do it, I doubt many other compilers will),
> so in practice they won't actually be inline (but should have only one
> indirection, which is good).
Yes, I see. Then it is useless to provide a syntax that allows to optionally
declare the local function inline:
(int) (inline) (factorial)(...) // No use for this.
> I think two questions remain for you to consider:
>
> 1. Might anyone might want them *not* to be inline, because they're
> afraid of code bloat, and their compiler optimizer is so clever that it
> will inline them? I doubt this is an issue because (a) I don't know a
> compiler that clever and (b) a compiler that clever should be taking the
> risk of code bloat into effect.
I agree.
> 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)? Otherwise, I am
not sure I understand your point.
> Regardless, as I said last time, you should do some profiling and put
> the results in your docs so that both you and your users can make
> informed decisions.
Yes, I will profile the code and add the analysis to the docs after I
settle down the macro API and a reasonably stable implementation -- so
hopefully soon :)
Thanks again!
-- Lorenzo
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk