C++ Logo


Advanced search

Re: Some feedback on scope guards

From: Andrey Semashev <andrey.semashev_at_[hidden]>
Date: Sat, 8 Apr 2023 02:53:56 +0300
On 4/8/23 01:49, Thiago Macieira via Std-Discussion wrote:
> On Friday, 7 April 2023 14:31:46 -03 Andrey Semashev via Std-Discussion wrote:
>> I think this issue needs to be addressed in the TS before inclusion into
>> the main standard. As a possible solution, I proposed the following idea:
> The standard solution does not need your suggested implementation details. It
> just needs to describe the expected behaviour and require that the
> implementations do it properly. They may have other means of achieving the
> same result.

Sure, the standard may simply require a certain behavior, and I did not
intend my proposed solution to be adopted or even be part of the
standard wording. I simply indicated that there is a way to achieve
this, which is something that is welcome for any proposal, isn't it?

The point is, the current TS wording explicitly mentions
unhandled_exceptions(), and it should not. Instead, it should describe
the required behavior, including when used in conjunction with
coroutines. Though I would still prefer if the behavior was
implementable using the standard library API so that non-standard scope
guard libraries like Boost.Scope could be implemented without resorting
to compiler-specific intrinsics or private APIs.

>> 2. For scope_success/scope_fail, it would be useful to be able to
>> specify a user-defined condition function for detecting the failure
>> condition. This is useful in cases when the failure is indicated not via
>> an exception but via other means, such as returning an error code. The
>> idea was implemented in Boost.Scope, where checking for an exception is
>> merely a default failure condition, with the user being able to specify
>> his own.
> For most cases, this can be implemented already, with the ability to
> deactivate a scope, because the typical use of such guards is to have them
> execute on early returns. At the moment when we've achieved success, it can be
> then dismissed -- compiler optimisations detecting such conditions would be
> welcome too.

This would be error-prone if there are multiple successful (or failure)
return points. Scope guards are specifically aimed to relieve you from
having to worry about missing the action upon leaving the scope, and
having to manually (de)activate the guard before returning goes against
this goal.

> However, I do see agree that some more scenarios can't be written like
> described above. They'd have to do some arm-twisting to get the failing
> conditions early, before dismissal... though reactivating a dismissed guard
> could allow further options.
> For those scenarios, why can't the user-provided lambda that is executed
> decide whether to do anything at all? Why do we need to give the scope guard
> object two lambdas: one to decide whether to run and the other to run?

Yes, scope_success/scope_fail can be implemented in terms of scope_exit,
but their benefit is reducing code duplication. This is most apparent
when the failure condition is the thrown exception - you simply don't
have to write the code for testing whether an exception is in flight.
This is also the case for other common cases, like testing whether an
error code indicates error. The condition function need not be a lambda,
it can be a function object that is written once and then reused in
multiple scope guards.

There is also value in code readability. Seeing scope_fail will
immediately give you the idea that this is an error handler rather than
a general cleanup.

>> 3. There is room for a scope guard that is more lightweight than
>> scope_exit. Specifically, a scope guard that unconditionally calls its
>> action, is not moveable and cannot be deactivated. Think of it as
>> lock_guard compared to unique_lock. In my experience, the majority of
>> scope guards don't need to be able to be (de)activated or moved, so this
>> optimization would be useful.
> Can this simply be QoI? The control variable of a dismissable scope guard that
> isn't dismissed can itself be garbage-collected after optimisation passes such
> as constant propagation and dead code elimination.

I don't think the QoI argument is realistic with current or forseeable
compiler tech, at least I don't see such optimizations taking place with
unique_lock vs. lock_guard in real code bases beyond some simple cases.
And I'd like to emphasize that in my experience unconditional scope
guards are much more prevalent to the dismissable ones.

Received on 2023-04-07 23:53:59