bug-bison
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: C++11 move semantics


From: Frank Heckenbach
Subject: Re: C++11 move semantics
Date: Tue, 06 Mar 2018 23:48:09 +0100

Hans Åberg wrote:

> Otherwise, there is no reason for Bison to be Yacc compatible,
> except for the special Yacc mode, which Paul Eggert worked on
> becoming POSIX compliant. For example, Yacc has optional rule
> colons, but that might have been removed in Bison in some modes at
> least. And Bison now has grammar variable naming, as I indicated,
> which in fact is very convenient.

Sure, it's not required (nothing about the C++ mode is), but it
might be convenient for the user as it avoids a trap. I have a
number of such rules in my grammars, and if you hadn't mentioned it,
I'd have been caught by this, not even at compile-time, but at
runtime. (Though such a bug would probably be rather obvious and
easy to fix in all places, not the end of the world.) But if fixing
the behaviour (especially with std::variant) is not more effort than
fixing the documentation (which should be done otherwise), why not
fix the behaviour?

> C++ std::stack though has std::deque as default. If one wants to
> access elements not on top, std::stack does not work, though.

Which Bison needs to do, so std::stack is out. But Bison's stack
implementation is rather short and unproblematic (and it can also
work with std::deque -- I still don't see an advantage of doing so,
but if you insist, you could probably add a %define or #define to
use it).

> > We're talking about user-actions here. To them, $$, $1, etc. do and
> > should behave just like varibles. (That $1 is an element of a
> > container is irrelevant, and again, there's no difference between
> > vector and deque in this regard.) The user action might use them
> > twice ("foo ($1); bar ($1);"), so always moving would be wrong. It's
> > up to the user to move when wanted. And it's not really a problem --
> > with a move-only type, the compiler will complain when you forget
> > it.
> 
> Using inline functions for the actions might do it. The compiler
> can then remove the inlines, but the moves should be still applied
> correctly. Or maybe not, depending on how the C++ standard is
> written.

AFAIK, it allows for (and modern compilers do) automatic moving for
the return value only. So inline functions would help in cases such
as the default action ("$$ = $1", if "$$ =" was replaced by "return"
and "$1" was a function parameter), and similar cases such as
"$$ = $2" (which typically occurs in rules such as '(' expr ')')
where a default action doesn't help.

I think it's an interesting idea, also for syntactic reasons:
Writing "return" instead of "$$ =" seems more natural (though of
course, you have to watch out: "$$ =" can be followed by other
statements, "return" cannot, so Bison couldn't simply replace it,
but a user can deal with it). Also functions could have named
parameters (which you seem to prefer anyway) always instead of "$1"
etc. So if I'd design Bison now, that's probably what I'd do. But
given things as they are, this might be too big a change, both to
implement, and for many users to adopt.

So I'm not sure it's worth the effort. But if, say, a lot of users
now scream they want this feature, I might actually take a stab
trying to implementing it ... :)

I wouldn't mind my grammars to look this way. But just to be sure,
this still doesn't eliminate the need for manual moves in many cases
such as "$$ = myfunc (std::move ($1), std::move ($2))", where myfunc
might be a typical builder function that wants to take ownership of
its arguments.

> >>> GC doesn't call destructors, does it?
> >> 
> >> One can a could of different things with the Boehm GC, register
> >> finalizers, and it also a deallocation function.
> > 
> > If you have to register them explictly, all the beauty of RAII is
> > lost.
> 
> One can mix both GC and standard stack allocations.

Stack allocations are no problem. It's about heap allocations. My
programs have very many of them, and most of them outside of the
parser. Even if GC can deal with them, it will slow it down, since
it has to track all of them for liveness analysis, just to find the
few dead ones. Also some of my programs have soft realtime
requirements, so I prefer more deterministic behaviour.

> Guile uses now the Boehm GC, and I have a similar dynamic
> polymorphy. For that I wrote a template reference class. The use
> of finalizers did not seem to impose a significant overhead. They
> are otherwise only needed so collect non-memory resources, such as
> open streams.

I destructors for various things, e.g. unregistering objects. I
think RAII is a general way for any kind of cleanup; freeing
resources is just a special case of it; freeing memory is a special
case of the special case (though the most common one). GC set out to
solve the special case. Unfortunately, RAII wasn't invented and
fully understood until later; otherwise GC might never have seen
widespead usage. I agree with most of what's written here:
https://minorfs.wordpress.com/2011/04/29/why-garbage-collection-is-anti-productive/
Or as http://viming.de/gc_vs_raii.html put it in the conclusion:
RAII has many advantages over GC, while it solves problems that GC
is supposed to solve as a "side-effect".

> Just to let me follow. You did not explain why you can't allocate
> the move-only type on the heap, and keep a pointer.

What kind of pointer:

1. Raw pointer (manually managed): That's what I've been doing so
   far. I don't need the C++ skeleton for this, the C one suffices.

2. unique_ptr: I'd like to, but it's move-only.

3. shared_ptr: Has the performance problems as I described.

4. A manually made shared pointer which a release method. That's
   possible to implement, and since I don't need all the advanced
   features of shared_ptr, it would be easier than a full
   reimplementation of shared_ptr, but still some work.

Now, I might do 4. if it was my only choice because 2. didn't work.
But so far it looks like 2. does work with relatively small changes
to the skeletons, and it's a more general approach, and hopefully
more useful to user users (if accepted upstream).

> > I didn't remove any CC. You CC'ed one (short) mail to Paul Eggert
> > which just said that Akim Demaille has not been active for some time
> > now.
> 
> I did not see his reply.

Sorry, my "which" (not "who") was referring to your mail, not to
Paul Eggert (who hasn't replied so far). I will try to contact him
again soon.

> > Bison's variant implementation really breaks down here. One could
> > work around it with some extra code to keep track of the dynamic
> > type of $<...>, but that gets really close to reimplementing
> > std::variant, so it seems rather pointless. So std::variant is the
> > way to go, at least in the long run. (As I said, I don't use gcc7
> > set, but I don't require $<...>, so for now I'll be fine with
> > Bison's variant.)
> 
> Since C++17 now is available and supported by GCC7, std::variant would be 
> best to use.

Agreed. It will require some changes to the code using it, but it
will actually make it simpler.

Regards,
Frank



reply via email to

[Prev in Thread] Current Thread [Next in Thread]