Boost logo

Boost :

Subject: [boost] [local] Simplifying the parenthesized syntax
From: Lorenzo Caminiti (lorcaminiti_at_[hidden])
Date: 2011-02-07 11:08:24


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?

The purpose of this email if to brainstorm possible ideas to simplify
the parenthesized syntax. Sorry in advance if not all that I say is
correct -- I haven't implemented it yet so I might be saying
inaccurate/unfeasible things here and there. As always, your feedback
is very welcome :)

PRELIMINARY IDEAS TO SIMPLIFY THE PARENTHESIZED SYNTAX

Consider the following parenthesized syntax for local functions:

    (void) (f)( (int)(x) (double&)(y) (const bind)((a)(&b))
(bind)((&c)(d)) ) // [0]

The followings are possible ways to simply this syntax.

1 MERGING PARAMETER TYPES AND NAMES (C++ preprocessor)

Parameter names can be passed to function types so I can transform
`int x` into `int` at compile-time:

    int x --[make function type]--> void (int x) --[get 1st parm type]--> int

This should allow me to simplify the parenthesized syntax [0] to:

    (void) (f)( (int x) (double& y) (const bind)((a)(&b))
(bind)((&c)(d)) ) // [1]

Note that the (unbound) parameter type and name are merged into a
single sequence element.

The following should also be possible:

    (void) (f)( (int x) (double& y) (const bind a) (const bind& b)
(bind& c) (bind d) ) // [1a]

Is [1a] simpler than [1]? Maybe it is...

2 TOWARD THE NORMAL PARAMETER LIST SYNTAX (C99 preprocessor)

Using variadic macros I _might_ be able to use a variable length tuple
(can I? I am not very familiar with these yet...) instead of a
sequence to represent the function parameters:

    (void) (f)(int x, double& y, const bind a, const bind& b, bind& c,
bind d) // [2]

I think, this is a great simplification over [0] because it is the
exact same syntax used by C++ to declare function parameters!

Note that `const bind` and `bind` have been distributed to all the
bound parameters. Given that sequences are best avoided here (to
reduce extra parenthesis), I do not think there is a less verbose way
to enumerate the bound parameters. The followings could also be
possible:

    (void) (f)(int x, double& y, const bind (a, &b), bind (&c, d) ) // [3]
    (void) (f)(int x, double& y) (const bind)(a, &b) (bind)(&c, d) // [4]

But I think enumerating every bound variable separately as in [2]
makes the parameter list syntax look the closet to normal C++ syntax
even if it requires the repetition of `const bind` and `bind` as the
types of the bound variables. Therefore, I would prefer [2] over both
[3] and [4].

3 RETURN TYPE AND FUNCTION NAME (C++ and C99 preprocessors)

I don't know how/if I can simplify the passing of the return type and
function name because I do need the function name as a separate token
within the preprocessor to do token concatenation and generate unique
names that refer to the function name...

Something like this should be possible (using tuples instead of sequences):

    void, f, ( (int)(x) (double&)(y) (const bind)((a)(&b))
(bind)((&c)(d)) ) // [5]
    void, (f)( (int)(x) (double&)(y) (const bind)((a)(&b))
(bind)((&c)(d)) ) // [6]

And again, on C99:

    void, f, (int x, double& y, const bind a, const bind& b, bind& c,
bind d) // [7]
    void, (f)(int x, double& y, const bind a, const bind& b, bind& c,
bind d) // [8]

However, I don't see how these are simpler than [0] and [2]. I need to
think about this more to see if other syntaxes are possible...

4 DEFAULT PARAMETER VALUES (C++ and C99 preprocessors)

Currently local functions support default parameters via the following
parenthesized syntax:

    (void) (f)( (int)(x)(default)(-1) (double)(y)(default)(1.23) ) // [9]

I think this could be simplified to (note the removal of the
parenthesis around the default value):

    (void) (f)( (int)(x)(default -1) (double)(y)(default 1.23) ) // [10]

An additional simplification could be to merge parameter types and
names together as in [1]:

    (void) (f)( (int x)(default -1) (double y)(default 1.23) ) // [10a]

Plus using C99 variadics as in [2]:

    (void) (f)(int x, default -1, double y, default 1.23) // [11]

I am not a big fan of this because `default` is separated from its
parameter by the comma `,`... but I don't think I can do better than
this.

5 OTHERS CONSIDERATIONS

These considerations are not directly relevant to local functions but
the parenthesized syntax [0] was designed as a general syntax that
makes *all* the elements of a function declaration available for
preprocessor metaprogramming. Instead, syntaxes [1] and [2] cannot
separate the parameter type token from the parameter name token at
preprocessing time. Therefore:

a) I don't think [1] and [2] allow to generate a parameter with the
specified name but a different type. For example, say I want to add
`const&` to `int x`, how can I do that unless `int x` is specified as
`(int)(x)` or `(int, x)`?

    int x --[how can I do this??]--> add_reference<add_const<int>::type>::type x

(I have to double check, but I don't think local functions need to
manipulate parameter types retaining instead the parameter names so I
don't think this is an issue for local functions.)

b) Having the parameter names as separate tokens (which is possible
with [0] but not with [1] or [2]) could be useful to generate unique
names to handle function overloading (even if with some limitations).
For example, in another library, I concatenate the function name token
with the parameter name tokens to generate unique names for overloaded
functions. As long as the overloaded functions use different parameter
names (and not just different parameter types) this mechanism works.
(Overloading is not supported for local functions -- see Rationale in
library docs -- so this is not an issue for local functions.)

Therefore, *IF* there was an interest in defining the parenthesized
syntax to be as general as possible so it could be adopted everywhere
macros spoil function definitions (to avoid requiring programmers to
learn different syntaxes for the different macros), [0] is more
general because it separates the parameter types from their names (at
the cost of the extra parenthesis of course).

Of all of these syntaxes, I _personally_ like [0] because it is pure
C++ preprocessor and it is the most general (maybe with simplification
[10] for default parameters). I also would like to provide [2] if the
C99 preprocessor is detected -- ideally, the same parsing macros will
be smart enough to accept both [2] and [0] on C99 and just [0] on pure
C++ preprocessors.

-- 
Lorenzo

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