Date: Mon, 17 Apr 2023 13:45:13 +0200
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)`.
> >>> 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;`
>
> --
> Std-Discussion mailing list
> Std-Discussion_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/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)`.
> >>> 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;`
>
> --
> Std-Discussion mailing list
> Std-Discussion_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion
Received on 2023-04-17 11:45:26