Boost logo

Boost :

Subject: Re: [boost] [local] Simplifying the parenthesized syntax
From: Lorenzo Caminiti (lorcaminiti_at_[hidden])
Date: 2011-02-10 10:59:56


On Mon, Feb 7, 2011 at 11:08 AM, Lorenzo Caminiti <lorcaminiti_at_[hidden]> wrote:
> On Sun, Feb 6, 2011 at 10:26 AM, Vicente Botet <vicente.botet_at_[hidden]> wrote:
>> The fact that you can not use variadic macros at work doesn't means that
>> your library can not provide in addition variadic macros on compilers
>> supporting them. I have no idea the work that this suppose, but if the
>> interface could be more appealing your library will have much more people
>> interested in.
>>
>> Have you an idea of how the interface could be simplified if variadic macros
>> were used?

Hello all,

Based on some prototyping that I have done so far, I think I can
simplify the local function macro syntax as follows using C99
variadics:

    void BOOST_LOCAL_FUNCTION_PARAMS(int x, double y, // [1]
            bind a, bind& b, const bind c, const bind& d) {
        ...
    } BOOST_LOCLA_FUNCTION_NAME(f)
    f(1, 1.23);

** Note that there are NO extra parenthesis!! :-)) ** What do you
think? Is this simple enough?

On non-C99 preprocessors, the above will need to use sequences instead
of variable length tuples:

    void BOOST_LOCAL_FUNCTION_PARAMS( (int x) (double y) // [2]
            (bind a) (bind& b) (const bind c) (const bind& d) ) {
        ...
    } BOOST_LOCLA_FUNCTION_NAME(f)
    f(1, 1.23);

This essentially introduces the same amount of extra parenthesis as
Boost.ScopeExit does -- I hope it is acceptable especially because if
you have C99 you can just use [1].

I can program the _same_ macros to accept both [1] and [2] on C99, and
only [2] on non-C99 preprocessors.

RECURSION, ETC

The above macros have the important advantage of using NO extra
parenthesis on C99 but they also have the following (small?)
limitations:

1) They do not allow the local function to recursively call itself
(because the function name is not specified until after the body is
programmed so it is not available within the body for recursive
calls).
2) They always require the use of Boost.Typeof to deduce the function
result type (the library already requires Boost.Typeof for variable
binding anyways).
3) They do not allow to specify the local function `inline` (I still
have to confirm if making a local function `inline` -- which makes all
the members of the local function's local class `inline` -- actually
allows the compiler to optimize something... if not, this is a mud
point).

In most cases these limitations might not be significant. Regardless,
programmers can always use the following alternative macros to
eliminate these limitations (at the cost of some extra parenthesis in
the syntax).
On C99 (still no extra parenthesis in the parameter list):

    BOOST_LOCAL_FUNCTION( // [3]
    (int) (inline) (factorial)(int n, bind& calculations) {
        int result = 1;
        if (n > 1) result = n * factorial(n - 1);
        calculations << result << std::endl;
    } BOOST_LOCAL_FUNCTION_END(factorial)

And, on non-C99 preprocessor (extra parenthesis also in the parameter list):

    BOOST_LOCAL_FUNCTION( // [4]
    (int) (inline) (factorial)( (int n) (bind& calculations) ) {
        int result = 1;
        if (n > 1) result = n * factorial(n - 1);
        calculations << result << std::endl;
    } BOOST_LOCAL_FUNCTION_END(factorial)

Again, I can program the _same_ macros to accept both [3] and [4] on
C99, and only [4] on non-C99 preprocessors.

DEFAULTS

Default parameter values can also be specified for both set of macros
(but IMO defaults look a strange on C99...).
On C99 with no recursion, Boost.Typeof for result type, and no inline:

    void BOOST_LOCAL_FUNCTION_PARAMS(int x, default -1, const bind& a) { // [5]
        ...
    } BOOST_LOCAL_FUNCTION_NAME(f)

And, on non-C99 with same limitations:

    void BOOST_LOCAL_FUNCTION_PARAMS( (int x)(default -1) (const bind&
a) ) { // [6]
        ...
    } BOOST_LOCAL_FUNCTION_NAME(f)

Or, on C99 without the recursion, etc limitations:

    BOOST_LOCAL_FUNCTION( // [7]
    (int) (inline) (factorial)(int n, default 6, bind& calculations) {
        ...
    } BOOST_LOCAL_FUNCTION_END(factorial)

And on non-C99 without the recursion, etc limitations:

    BOOST_LOCAL_FUNCTION( // [8]
    (int) (inline) (factorial)( (int n)(default 6) (bind& calculations) ) {
        ...
    } BOOST_LOCAL_FUNCTION_END(factorial)

IMO the parenthesis `(int n)(default 6)` make the default value look
better then the comma `int n, default 6`.

The following could also be possible on C99:

    void BOOST_LOCAL_FUNCTION_PARAMS( (int x, default -1), const bind&
a) { // [9]
        ...
    } BOOST_LOCAL_FUNCTION_NAME(f)

But the introduced parenthesis are not necessary and I personally find
the mixed use of the parenthesis and commas confusing. What do you
think?

NOTE: I need to separate defaults from the parameter type-name because
defaults cannot be part of a function type. I need to use `default
...` instead of `= ...` because tokens need to start with alphanumeric
symbols so I can concatenate them (PP_CAT) to program something like
this "if the token starts with 'default' then do ..." in the
preprocessor parsing macros.

I am writing a Syntax Rationale section in the library documentation
that lists all the alternative syntaxes that I have been exploring,
their feasibility, and the reasons behind their adoption or rejection.

Thanks!

--
Lorenzo

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