class SomeClass
{
public:
~SomeClass()
{
if(unwind)
{
//Special cleanup for exception.
}
}
private:
std::stack_unwinding unwind;
};
On 01/09/2013 01:10 AM, Nicol Bolas wrote:
> classSomeClass
> {
> public:
> ~SomeClass()
> {
> if(unwind)
> {
> //Special cleanup for exception.
> }
> }
> private:
> std::stack_unwinding unwind;
> };
>
> This would make it more clear to users reading the code what is
> actually going on. I'm not wedded to any of the names in this
> example; I just want an object that makes it more obvious what the
> code is detecting.
I'm sorry, I don't understand what you're aiming at.
Do you want such a class in the exposition in the paper, or
do you want such a class standardized?
Regarding the former, I've updated my paper to actually spell out
the "Transaction" example for the uncaught_exception_count() case,
which should do in terms of explanation.
Regarding the latter, I'm opposed on a few grounds:
- a base or class data member can't have a size of one bit
- This is a fringe feature:
* I want to inflict as little change to the standard as feasible
to get the feature.
* People wanting the feature should be able to wrap their head
around the uncaught_exception_count() logic, not needing and
not having to learn another fairly thin helper class.
Though that brings up a question: if the count can only ever be 0 or 1... why is it a count? Is that a kind of forward-compatibility thing (the idea that the committee might later allow more than one exception to be active), or is there something else to it?
The count can be more than 1, otherwise the already existing std::uncaught_exception would be enough. See the motivating example in the proposal.Though that brings up a question: if the count can only ever be 0 or 1... why is it a count? Is that a kind of forward-compatibility thing (the idea that the committee might later allow more than one exception to be active), or is there something else to it?
struct Transaction
{
bool currentException;
Transaction() : currentException
(std::uncaught_exception() ? true : false
)
~Transaction()
{
if (!currentException
&& std::uncaught_exception())
RollBack();
else
Commit();
}
};
U::~U()
{
try
{
Transaction t( /*...*/ );
// do work
}
catch( ... )
{
// clean up
}
}
The 1bit version relies to the fact if the count is odd or even.
I don't see how it's possible to have more than one uncaught exception.
I don't understand how that works. Let's assume that the number of uncaught exceptions can be more than 1. Then it's entirely possible that it was 0 on construction of the object, but 2 on destruction.
> If `U::~U` is being called due to stack unwinding, then
> `std::uncaught_exception()` will be a real value. Therefore,
> Transaction::currentException will be `true`. And therefore it will
> not roll anything back.
>
If the "do work" throws you are still committing, that is the case you
cannot deduce from this information. In this case you would have two
exceptions in progress, the original one that caused the ~U call and the
one thrown inside the ~U. This is all good as long as the ~U doesn't
leak the exception.
On Tuesday, January 8, 2013 10:54:31 PM UTC-8, Jens Maurer wrote:On 01/09/2013 01:10 AM, Nicol Bolas wrote:
> classSomeClass
> {
> public:
> ~SomeClass()
> {
> if(unwind)
> {
> //Special cleanup for exception.
> }
> }
> private:
> std::stack_unwinding unwind;
> };
>
> This would make it more clear to users reading the code what is
> actually going on. I'm not wedded to any of the names in this
> example; I just want an object that makes it more obvious what the
> code is detecting.
I'm sorry, I don't understand what you're aiming at.
Do you want such a class in the exposition in the paper, or
do you want such a class standardized?
I'm wanting the class in the actual proposal.
Nicol: It appears to me that it is intrinsically necessary for them to be both regular functions and indeed, explicitly callable. Consider what would happen in, say, std::vector's destructor- it would have to pass on the exception to the contained objects' destructors.
template<class U, class E>
void operator() (T*, E&) const;
T *t = new T;
t->~T();
delete reinterpret_cast<char *>(t);
Conceptually, ~T(...) would be fine for cases where you don't care what the exception was. Although I admit it would be strange for it to have different semantics to regular variadic functions, I don't think this is a serious problem, since destructors in general are a special case in virtually every scenario anyway, so it's hardly surprising to find that a destructor has a different rule. Since the argument is inaccessible anyway, it would be simplest to simply specify that if ~T(...) is resolved by overload resolution, then no argument is passed, which also sidesteps the issue of UB for complex types.
I'm not sure how that's "simple" this is. You're talking about making this work in a way that's completely foreign to how anything else in C++ works
But what are the use cases for wanting to do different things based on exceptions, rather than just asking, "am I being destroyed because of unwinding?"
More importantly, what happens if a class doesn't implement a destructor for a particular exception, yet std::vector wants to explicitly call it? Does it call `~T()`?
Even more importantly, consider std::unique_ptr. Do we want std::default_delete to be passed an exception? Does it need to have this? And how would you implement it? The `T` pointer is assumed to be allocated via `new T`; therefore, it must be deleted with `delete T`. You can't call its destructor directly, because there's no way to deallocate the memory correctly.
I'm not sure how that's "simple" this is. You're talking about making this work in a way that's completely foreign to how anything else in C++ worksVariadic arguments are used solely for the purpose of guiding overload resolution all the time in some SFINAE techniques. We also use it in *exactly* this fashion in catch(...), which is exactly what I had in mind when suggesting it.
But what are the use cases for wanting to do different things based on exceptions, rather than just asking, "am I being destroyed because of unwinding?"The use case I showed is being able to add additional information to the exception object, something which is currently impossible.
On Mar 24, 2013, at 6:08 PM, Nicol Bolas <jmck...@gmail.com> wrote:
> As much as I hate the stack unwinding count, it seems to be the only solution presented (thus far) that can cover all of the cases of forwarding the information about unwinding to all of its descendents, regardless of how they were allocated/deallocated.
Exposing the stack unwinding count, and nothing more, is currently where my support lies.
It is obviously do-able on all platforms. And it appears to enable the desired use cases, perhaps with extra convenience classes perhaps not. But this is an area where the existing practice consists only of std::uncaught_exception, and so I prefer to advance cautiously (lest we shoot ourselves in the foot yet again).
At the very least, I would like to see where exposing the count creates hardship.