C++ Logo


Advanced search

Re: Some feedback on scope guards

From: Andrey Semashev <andrey.semashev_at_[hidden]>
Date: Mon, 17 Apr 2023 14:36:08 +0300
On 4/17/23 14:11, Marcin Jaczewski via Std-Discussion wrote:
> pon., 17 kwi 2023 o 12:54 Andrey Semashev via Std-Discussion
> <std-discussion_at_[hidden]> napisał(a):
>> On 4/17/23 13:47, Marcin Jaczewski wrote:
>>> pon., 17 kwi 2023 o 12:35 Andrey Semashev via Std-Discussion
>>> <std-discussion_at_[hidden]> napisał(a):
>>>> On 4/17/23 13:27, Marcin Jaczewski via Std-Discussion wrote:
>>>>> Why are multiple destructors bad? Especially if they were handled like
>>>>> `const` overloads.
>>>>> Besides, if a new destructor is no-go, why not add a new operator that
>>>>> simply is called before the destructor?
>>>>> like `operator unwind()` that is called before a proper destructor is
>>>>> used in case of unwind.
>>>> It doesn't matter how you name it. If it doesn't work through other
>>>> types (e.g. if the scope guard is a member or accessed through a
>>>> pointer) then we have a problem with composability. If it does work,
>>>> then we're back to the status quo, since the operator will be called
>>>> under the same conditions as the destructor, and therefore it will need
>>>> to check for uncaught_exceptions().
>>> `operator unwind() = default;`?
>> And what would this mean? That it forwards or that it doesn't?
>> Regardless of how you answer, my previous reply applies.
> Apply same operator to all members.
>>> And if it's not a direct member then we have the same problem with
>>> composible as "copy",
>>> that I suppose is solved problem?
>> I'm not sure what you mean here. Default copy constructors/assignments
>> do forward, but there's no problem with that, so I don't see the relation.
> As the same logic applies in both cases, `T(const T&) = default` apply
> copy for each member.
> `operator throw_unwind() = default;` is equal to:
> ```
> x.operator throw_unwind();
> y.operator throw_unwind();
> z.operator throw_unwind();
> ```
> Of course if `x` is a pointer then this operator is noop. But we can
> easy work around this by composition like:
> ```
> struct mixing_unwind_deref
> {
> template<typename T>
> operator throw_unwind(this T& self) { if (self) (*self).operator
> throw_unwind(); }
> };
> struct MyPointer : std::unique_ptr<scope_gaurd>, mixing_unwind_deref
> {
> };
> ```

This means every smart pointer and container and any other user-defined
type that uses pointers as members would have to be updated for this to
work. This is a big ask.

>>> You miss an important thing `unwind` will be called only when an
>>> exception unwind code.
>> This doesn't help if you still need to check for uncaught_exceptions().
> But what would be the point for checking this? even more when its have
> wrong value
> when call in coroutine? `uncaught_exceptions` was needed to check if
> it was throw unwind.
> This operator gives this info directly. What exactly need I have for
> this function?

Assuming scope_fail invokes its action from within this `operator
unwind`, how would nested scope_fails work?

Received on 2023-04-17 11:36:32