C++ Logo


Advanced search

Re: Some feedback on scope guards

From: Edward Catmur <ecatmur_at_[hidden]>
Date: Sun, 16 Apr 2023 16:41:33 -0300
On Sun, 16 Apr 2023, 12:25 Ville Voutilainen, <ville.voutilainen_at_[hidden]>

> On Sun, 16 Apr 2023 at 14:34, Edward Catmur <ecatmur_at_[hidden]>
> wrote:
> >> I fail to see the problem. It's as if you're suggesting that there's
> >> some unknown specification challenge here, but we have the
> >> specification
> >> you speak of already, in the TS.
> >
> >
> > And that specification breaks with coroutines.
> I don't see what use it is to repeat that, we all know that.
> >> Yes? Neither of those needs other than pointers to them to work. Scope
> >> guards are not like them.
> >> You found two cases that presumably prove my statement wrong, but
> >> (pointers to) those types can
> >> be used as subobjects, that works fine, and they work with other
> >> library types without problems, since
> >> for them it's truly so that you don't need by-value subobjects of them.
> >>
> >> Try harder.
> >
> >
> > And you can take pointers to scope guards. Consider also immovable types
> like scoped_lock - those won't work with most library components.
> I don't want a pointer to a scope guard, I want a reference or a
> pointer to a tuple of scope guards.
> scoped_lock works just fine with a whole lot of library components, I
> can have a tuple of them, an optional of it, a fixed-size
> vector of it.
> >> >> > Working well can be dealt with by the vendor since these are
> library types. Wrong - maybe, but this is a scope guard! The name indicates
> it should be used in a scope.
> >> >> Yeah, which does include packaging them any which way I please and
> >> >> passing them down to helper functions for further processing,
> >> >> or just having a collection-bunch of them in a scope, rather than
> >> >> having to have individual objects of the scope guard type directly,
> >> >> without
> >> >> the possibility to bunch them.
> >> > I've never seen code where that would actually be useful.
> >>
> >> Uhh.. you've never seen code that packages multiple individual objects
> >> into a bunch, to pass them as one object to be processed
> >> by a helper function? You can't fathom code where you'd take a pack of
> >> callbacks and wrap scope guards on top of them, variadically?
> >
> >
> > No, since you may as well wrap the lot into a single scope guard.
> No, I can't, or rather won't - that then leads into using apply() or
> a loop for destruction when none of that is needed
> if we don't wreck the design of these wrappers.

Or a comma fold. I suppose those are a bit abstruse, though.

>> Well, gee whizz, that's an acceptable cost, compared to preventing the
> >> use of scope guards as subobjects, which is not an
> >> acceptable cost.
> >
> >
> > Using scope_guard outside block scope is a code smell, though it can be
> justified in some rare cases. Using scope_success or scope_failure outside
> block scope would not pass code review where I work.
> It's not going to be used outside a block scope, it's going to be used
> in a scope nested under that scope.
> >
> > Meanwhile, using scope_success and scope_failure in coroutine block
> scope across suspension points is completely natural; the only problem is
> that it can break. We should not be adding footguns like that to the
> standard.
> There's nothing natural about that; as Jason points out, it's
> problematic in the same way as any use of any thread local facility
> is, for a coroutine that may be resumed in a different thread.

But there's no obvious reason that scope_failure should use any thread
local facility. The uncaught_exceptions hack is elegant in some light, but
it's a rather tangential way of answering the question "was the destructor
of this object caused by stack unwinding?".

> >> If we have a solution that doesn't do that prevention
> >> and solves the coroutine problem, I'm all ears.
> > There's Andrey's solution, but the overhead is substantial.
> I don't quite follow how that overhead is avoidable just by wrecking
> the composability of these types, or at all.

To recap: the compiler knows when a destructor is being called by normal or
exceptional scope exit. Since the compiler and library are written
together, the former can pass that information to the facility by private
("magic") means. However, this may be trickier to implement if the
destructor of another class intervenes.

Perhaps we should entertain a separate scope fail/success type that is
> coroutine-aware, and leave the TS ones
> without that overhead.

Received on 2023-04-16 19:41:47