Boost logo

Boost :

From: joel de guzman (djowel_at_[hidden])
Date: 2002-02-22 12:38:03


----- Original Message -----
From: "Douglas Gregor" :

> On Friday 22 February 2002 08:04 am, you wrote:
> > I have to disagree with this. 'auto' is a nice feature that definitely gets
> > publicity but it's _not_ needed for writing libraries (except for the
> > return type autodeduction which is solved by typeof.)
>
> I would put auto in the 'needed for writing libraries' category because it
> allows libraries to be moved further into the metaprogramming domain. My best
> example here is the Spirit parser. The only reason that Spirit parsers are a
> bit pokey now is because we have to declare a type for a Spirit rule, as in
> the following grammar:
>
> rule<> integer;
> rule<> group;
> rule<> expr1;
> rule<> expr2;
> rule<> expr;
> integer = lexeme[ !(ch_p('+') | '-') >> +digit ];
> group = '(' >> expr >> ')';
> expr1 = integer | group;
> expr2 = expr1 >> *(('*' >> expr1) | ('/' >> expr1));
> expr = expr2 >> *(('+' >> expr2) | ('-' >> expr2));
>
> However, if we had 'auto', Spirit could put _everything_ in a giant
> expression template, and therefore not require the expensive redirections
> through virtual functions that it does now. I'm going to invent a little
> syntax here to make this work, but essentially we could have:
>
> struct Expr {};
> auto integer = lexeme[ !(ch_p('+') | '-') >> +digit ];
> auto group = '(' >> rule<Expr>() >> ')';
> auto expr1 = integer | group;
> auto expr2 = expr1 >> *(('*' >> expr1) | ('/' >> expr1));
> auto expr = rulename<Expr>(expr2 >> *(('+' >> expr2) | ('-' >> expr2)));
>
> The rule<Foo>() construct references a rule that has not yet been defined but
> will have the static name 'Foo'. rulename<Foo>(my_rule) gives 'my_rule' the
> static name 'Foo'. This allows recursion statically.
>
> The overall result: this entire grammar can be stored as an expression
> template, and with a little nifty metaprogramming Spirit can produce very
> efficient parsers for static (parts of) languages.

Indeed. I wish I could be as eloquent as Doug. The Spirit inline parser
is a case where the auto keyword will boost its expressiveness **and**
performance a lot. The typeof(x) will only address the performance issue.
For instance here's the same calculator grammar above written using typeofs:

struct Expr {};
typeof(lexeme[ !(ch_p('+') | '-') >> +digit ])
    integer = lexeme[ !(ch_p('+') | '-') >> +digit ];
typeof('(' >> rule<Expr>() >> ')')
    group = '(' >> rule<Expr>() >> ')';
typeof(integer | group)
    expr1 = integer | group;
typeof(expr1 >> *(('*' >> expr1) | ('/' >> expr1)))
    expr2 = expr1 >> *(('*' >> expr1) | ('/' >> expr1));
typeof(rulename<Expr>(expr2 >> *(('+' >> expr2) | ('-' >> expr2))))
    expr = rulename<Expr>(expr2 >> *(('+' >> expr2) | ('-' >> expr2)));

Yuck!

Without the auto keyword, I had to resort to virtual functions to
encapsulate the RHS (much like what Doug has done with boost's
function lib). Without it, and without typeof (as C++ currently is) the
explicit type of the RHS will have to be typed. For instance:

    alternative<chlit<char>, chlit<char> >
        sign= ch_p('+') | '-';

Now I can't imagine rewriting Hartmut's C parser this way.

Bottom line is: expressiveness matters!

Regards,
--Joel


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