C++ Logo

std-discussion

Advanced search

Re: Some feedback on scope guards

From: Marcin Jaczewski <marcinjaczewski86_at_[hidden]>
Date: Mon, 17 Apr 2023 14:05:33 +0200
pon., 17 kwi 2023 o 13:53 Andrey Semashev via Std-Discussion
<std-discussion_at_[hidden]> napisał(a):
>
> On 4/17/23 14:45, Marcin Jaczewski via Std-Discussion wrote:
> > pon., 17 kwi 2023 o 13:36 Andrey Semashev via Std-Discussion
> > <std-discussion_at_[hidden]> napisał(a):
> >>
> >> 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.
> >
> > I already show how this could be this easy workaround:
> > ```
> > struct MyPointer : std::unique_ptr<scope_gaurd>, mixing_unwind_deref
> > ```
> > And another mixing that propagates this operator to `for (auto& p : *this)`.
>
> That's a modification nonetheless. And yes, however minor it may be,
> when you have thousands of classes in your project, this is a problem.
>
> Also, I don't think mixing_unwind_deref works the way you're describing.
> The type T in the `operator throw_unwind` would be deduced as a possibly
> cv-qualified mixing_unwind_deref.
>

Ok, this could be possible, but mixing something like this could work for `+=`.
Probably it work if it could "steal" main call to operator like is
done for mixing examples:
```
struct Mixng
{
    template<typename T>
    int operator +=(this T& t, int i) { return t.i + i; }
};
struct F : Mixng
{
    int i = 6;
};

int main() {
    F f;
    return f += 1;
}
```



> >>>>> 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?
> >
> > What exactly nesting you are describing?
> > If this `scope_fails` is a member of type then it should be called automatically
> > by default implementation of `throw_unwind`. Type can be even plain struct like:
> > ```
> > struct Foo
> > {
> > scope_guard x;
> > };
> > ```
> > as all types will have predefined `operator throw_unwind() = default;`
>
> No, I mean this:
>
> scope_fail sg1([]
> {
> scope_fail sg2([]
> {
> puts("Still a failure?");
> });
> });
>
> throw 1;

no, as normal unwid is called for `sg2` as this scope does not have
any exception.
Why should there be considered an expedition unwind?

>
> --
> Std-Discussion mailing list
> Std-Discussion_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion

Received on 2023-04-17 12:05:46