Differentiating destructor invocation contexts (updated)

166 views
Skip to first unread message

Jens Maurer

unread,
Jan 8, 2013, 5:29:06 PM1/8/13
to std-pr...@isocpp.org

Thanks for all the helpful input. I've thought about the issue a bit more,
and it seems that specifying a std::uncaught_exception_count() function
in the standard would be the least disruptive way of providing the fringe
feature I'm looking for. Plus, it's already available in major
implementations.

Updated paper is attached, further feedback is welcome.

Jens

destructor-unwinding.html

Daniel Krügler

unread,
Jan 8, 2013, 5:39:10 PM1/8/13
to std-pr...@isocpp.org
2013/1/8 Jens Maurer <Jens....@gmx.net>:
No concrete opinion yet, but a question:

Shouldn't "count" named in the default constructor in the example starting with:

"The memory footprint can be made as small as one bit:"

be "latch"?

Thanks,

- Daniel

> Jens
>
> --
>
>
>

Ville Voutilainen

unread,
Jan 8, 2013, 6:02:18 PM1/8/13
to std-pr...@isocpp.org
I find it a very pleasant surprise that uncaught_exception_count() is
already available
on the aforementioned major implementations, and I like that option a lot.

Beman Dawes

unread,
Jan 8, 2013, 6:34:04 PM1/8/13
to std-pr...@isocpp.org
When Jens says "Implemented on MSVC, GCC, Clang", I assume he mean "an
implementation has been tested with these compilers" rather than "The
libraries shipping with these compilers already implement
uncaught_exception_count()". Jens?

Am I the only one OD'ed on new core language features, and willing to
accept even a pretty good library feature rather than a complexifying
core language feature?

--Beman

Nicol Bolas

unread,
Jan 8, 2013, 7:10:02 PM1/8/13
to std-pr...@isocpp.org

Given `std::uncaught_exception_count` (BTW: what versions of those compilers provide this extension? Just out of curiosity), is it possible to have some object that makes this a bit more intuitive? Perhaps something like this:

class SomeClass
{
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.

Alberto Ganesh Barbati

unread,
Jan 8, 2013, 7:24:30 PM1/8/13
to std-pr...@isocpp.org

Il giorno 09/gen/2013, alle ore 00:34, Beman Dawes <bda...@acm.org> ha scritto:

> On Tue, Jan 8, 2013 at 6:02 PM, Ville Voutilainen
> <ville.vo...@gmail.com> wrote:
>> On 9 January 2013 00:29, Jens Maurer <Jens....@gmx.net> wrote:
>>> Thanks for all the helpful input. I've thought about the issue a bit more,
>>> and it seems that specifying a std::uncaught_exception_count() function
>>> in the standard would be the least disruptive way of providing the fringe
>>> feature I'm looking for. Plus, it's already available in major
>>> implementations.
>>> Updated paper is attached, further feedback is welcome.
>>
>> I find it a very pleasant surprise that uncaught_exception_count() is
>> already available
>> on the aforementioned major implementations, and I like that option a lot.
>
> When Jens says "Implemented on MSVC, GCC, Clang", I assume he mean "an
> implementation has been tested with these compilers" rather than "The
> libraries shipping with these compilers already implement
> uncaught_exception_count()". Jens?

As for Clang, function uncaught_exception() is eventually implemented as globals->uncaughtExceptions != 0. Implementing uncaught_exception_count() could be implemented as easily as just returning globals->uncaughtExceptions, as its value happens to be exactly what is needed. It is my understanding that GCC shows a similar situation.

> Am I the only one OD'ed on new core language features, and willing to
> accept even a pretty good library feature rather than a complexifying
> core language feature?

Count me in.

Ganesh

Jens Maurer

unread,
Jan 9, 2013, 1:45:21 AM1/9/13
to std-pr...@isocpp.org
On 01/09/2013 12:34 AM, Beman Dawes wrote:
> On Tue, Jan 8, 2013 at 6:02 PM, Ville Voutilainen
> <ville.vo...@gmail.com> wrote:
>> On 9 January 2013 00:29, Jens Maurer <Jens....@gmx.net> wrote:
>>> Thanks for all the helpful input. I've thought about the issue a bit more,
>>> and it seems that specifying a std::uncaught_exception_count() function
>>> in the standard would be the least disruptive way of providing the fringe
>>> feature I'm looking for. Plus, it's already available in major
>>> implementations.
>>> Updated paper is attached, further feedback is welcome.
>>
>> I find it a very pleasant surprise that uncaught_exception_count() is
>> already available
>> on the aforementioned major implementations, and I like that option a lot.
>
> When Jens says "Implemented on MSVC, GCC, Clang", I assume he mean "an
> implementation has been tested with these compilers" rather than "The
> libraries shipping with these compilers already implement
> uncaught_exception_count()". Jens?

Right, it's the former. See https://github.com/panaseleus/stack_unwinding
for the implementation.

> Am I the only one OD'ed on new core language features, and willing to
> accept even a pretty good library feature rather than a complexifying
> core language feature?

Sorry, I'm lost in the acronyms here. What's "OD"? And the use with "even"
seems to imply that "pretty good" means "not stellar"? Do you have
any specific suggestions for improvement of the library feature?

Thanks,
Jens

Jens Maurer

unread,
Jan 9, 2013, 1:54:31 AM1/9/13
to std-pr...@isocpp.org
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.
- The standard text is not a tutorial.

See http://jmaurer.awardspace.info/wg21/destructor-unwinding.html
for the latest version of the paper.

Jens

Nicol Bolas

unread,
Jan 9, 2013, 2:45:35 AM1/9/13
to std-pr...@isocpp.org


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.

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

I'm not saying that the class would replace the direct use of `std::uncaught_exception_count`. It is an alternative, nothing more. So if you really need that space, you can directly use it. If you're like most people, they're not going to need those 7 bits that badly, so they'll take code readability over 7 bits of storage.

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?
 
 - This is a fringe feature:
     * I want to inflict as little change to the standard as feasible
       to get the feature.

I rather doubt the presence or absence of a helper class would adversely affect the acceptance of 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.

As others have pointed out in other threads, people read code more often than they write it. A small helper class that makes code more legible and more easily digestible is hardly something burdensome to learn. You just read what it's called; that tells you what you need to know. It's certainly easier to learn than idiomatic use of `std::uncaught_exception_count` for this purpose. It's easier to learn to write and it's easier to learn to read.

Also, making assumptions about the skill level of those who would and would not be using the feature is... not wise. You shouldn't want to make a feature hard to use on the assumption that only experts will want to use it. Features should be as simple to use as possible without sacrificing anything of value. And the class is simpler to use for the task of detecting this sort of thing.

Yes, you'll still want the low-level mechanism. But the class is an obvious bit of abstraction that makes it easier for all involved. Just like we have std::thread, but also std::async as a quick-and-dirty means of throwing out a task.

Mikael Kilpeläinen

unread,
Jan 9, 2013, 4:30:20 AM1/9/13
to std-pr...@isocpp.org

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.
The 1bit version relies to the fact if the count is odd or even.


Mikael

Nicol Bolas

unread,
Jan 9, 2013, 5:30:18 AM1/9/13
to std-pr...@isocpp.org


On Wednesday, January 9, 2013 1:30:20 AM UTC-8, Mikael Kilpeläinen wrote:

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.

I fail to see how. The motivating example could work if it were rewritten like this:

struct Transaction
{
 
bool currentException;

 
Transaction() : currentException(std::uncaught_exception() ? true : false)
 
~Transaction()
 
{
   
if (!currentException && std::uncaught_exception())
     
RollBack();
   
else
     
Commit();
 
}
};

This should work:

U::~U()
{
  try
 
{
   
Transaction t( /*...*/ );
   
// do work
  }
  catch( ... )
  {
    // clean up
  }
}

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.

However, if `U::~U` was being called just from falling off the stack or an explicit destructor call, then `Transaction::currentException` will be `false`. If `do work` throws an exception, when `Transaction::~Transaction` is called, then `std::uncaught_exception()` will be a value. And therefore the conditions for a rollback are needed. Whereas if `do work` ends normally, `std::uncaught_exception()` will not have anything in it, and everyone will be fine.

I don't see how it's possible to have more than one uncaught exception. So I don't see why we need a count when a boolean will do. Indeed, I'm not sure why we can't just use `std::uncaught_exception()` as I demonstrated to detect all of this.

If that code is too ugly, then wrap it in a nice class, as I explained.

The 1bit version relies to the fact if the count is odd or even.

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. So just checking odd vs. even won't be sufficient.

Jean-Marc Bourguet

unread,
Jan 9, 2013, 5:52:01 AM1/9/13
to std-pr...@isocpp.org

> 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. So
> just checking odd vs. even won't be sufficient.


If

try {
Foo f;
throw 42;
} catch (int) }
}

is executed in a destructor, whatever the number of uncought exceptions
is when constructing f, it will be one more when its destructor is
executed.

You can't construct similar cases where it would be two more because
std::terminate() will be called before.

Yours,

--
Jean-Marc

Mikael Kilpeläinen

unread,
Jan 9, 2013, 5:52:49 AM1/9/13
to std-pr...@isocpp.org

> 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.

>
> 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. So
> just checking odd vs. even won't be sufficient.
The idea is.. The count can be arbitrary but since we cannot allow
destructor to leak the exception (which would terminate) the count can
change only by one in this stack frame.


Mikael

Nicol Bolas

unread,
Jan 9, 2013, 6:06:03 AM1/9/13
to std-pr...@isocpp.org

If you cannot construct cases where it would be two more than it was before, then you cannot construct cases where it will be two at all. You throw an exception, the counter gets bumped. Once the stack unwinding finishes, the counter goes back down.

Unless `std::uncaught_exception_count` is intended to be some kind of global (or at least thread-local) counter, where every time an exception is thrown, it is bumped and it never goes down. If so, then the proposal should be updated, because "The exception implementation maintains a count of exceptions currently causing stack unwinding," is very misleading in this regard.

If stack unwinding is happening, it will only ever be due to one exception. So the count will be either 0 or 1.

Nikolay Ivchenkov

unread,
Jan 9, 2013, 6:11:02 AM1/9/13
to std-pr...@isocpp.org
On Wednesday, January 9, 2013 2:30:18 PM UTC+4, Nicol Bolas wrote:

I don't see how it's possible to have more than one uncaught exception.

There may be nested destructor calls:
http://liveworkspace.org/code/2WPjRQ$0
 
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.

That's impossible if by "on destruction" you mean "when the object's destructor is just entered".

Nicol Bolas

unread,
Jan 9, 2013, 6:14:34 AM1/9/13
to std-pr...@isocpp.org


On Wednesday, January 9, 2013 2:52:49 AM UTC-8, Mikael Kilpeläinen wrote:

> 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.

I forgot that you could get multiple unwindings without std::terminate as long as the destructor doesn't leak. So you're right, we do need a count. And it would seem that, because the exceptions can't leak, that the counter will only ever be either the current count or the current count - 1. So the 1-bit version does work.

Beman Dawes

unread,
Jan 9, 2013, 8:30:31 AM1/9/13
to std-pr...@isocpp.org
On Wed, Jan 9, 2013 at 1:45 AM, Jens Maurer <Jens....@gmx.net> wrote:
> On 01/09/2013 12:34 AM, Beman Dawes wrote:
...
>
>> Am I the only one OD'ed on new core language features, and willing to
>> accept even a pretty good library feature rather than a complexifying
>> core language feature?
>
> Sorry, I'm lost in the acronyms here. What's "OD"?

"Overdose". Apologies for using an obscure Americanism on a mailing
list with worldwide readership.

> And the use with "even"
> seems to imply that "pretty good" means "not stellar"?

What I should have written was "Am I the only one overdosed on new
core language features, and willing to accept a pretty good library
feature rather than a stellar but compexifying core language feature?"
or even better "I like the library approach".

> Do you have
> any specific suggestions for improvement of the library feature?

No, I leave that in your capable hands:-)

--Beman

Alberto Ganesh Barbati

unread,
Jan 15, 2013, 4:18:08 AM1/15/13
to std-pr...@isocpp.org

Il giorno 09/gen/2013, alle ore 08:45, Nicol Bolas <jmck...@gmail.com> ha scritto:



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.

I believe the class approach has its merits. The interface is clearer and more readable than a pair of calls to uncaught_exception_count(). Moreover, a static code analyzer might easily detect the intended pattern and provide a warning message if class SomeClass is ever allocated on the heap. Given that static code analyzers are becoming more popular every day, the value of this should not be underestimated.

Ganesh

Sebastian Gesemann

unread,
Jan 15, 2013, 4:34:25 AM1/15/13
to std-pr...@isocpp.org
On Tue, Jan 15, 2013 at 10:18 AM, Alberto Ganesh Barbati wrote:
>
>> On 01/09/2013 01:10 AM, Nicol Bolas wrote:
>> > class SomeClass
>> > {
>> > 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 believe the class approach has its merits. The interface is clearer and
> more readable than a pair of calls to uncaught_exception_count(). Moreover,
> a static code analyzer might easily detect the intended pattern and provide
> a warning message if class SomeClass is ever allocated on the heap. Given
> that static code analyzers are becoming more popular every day, the value of
> this should not be underestimated.

I have trouble predicting how a stack-allocated vector<SomeClass>
behaves during unwinding. But I guess it would just work and that
there is no need to warn about heap allocation in this case. So, even
though some object is heap-allocated, its life-time might still be
bounded by the life-time of a stack-allocated object.

Alberto Ganesh Barbati

unread,
Jan 15, 2013, 5:04:51 AM1/15/13
to std-pr...@isocpp.org
The accuracy of the warning is a QOI issue. A static analyzer might or might not be smart enough to avoid false positives as the one you describe. In any case, it is a not a good reason against providing a tool that allows an analyzer to make a choice.

Ganesh

DeadMG

unread,
Mar 24, 2013, 9:23:57 AM3/24/13
to std-pr...@isocpp.org
I believe that the simpler approach is simply to pass the exception object into the destructor, if it is the cause of the destructor being called. Consider:

struct Transaction {
    ~Transaction() { commit(); }
    ~Transaction(std::runtime_error& e) { rollback(); }
};

This would also permit other use cases designs, like

struct layered_exception {
     std::vector<std::string> messages;
};

struct ExceptionInfo {
    std::string data;
    ~ExceptionInfo() {}
    ~ExceptionInfo(layered_exception& e) { e.messages.push_back(data); }
};

Implicitly ~T(...) simply calls ~T().

Nicol Bolas

unread,
Mar 24, 2013, 4:43:21 PM3/24/13
to std-pr...@isocpp.org

So what do you do if you don't care what the exception was, like 90+% of the use cases? It'd be strange if `~T(...)` was not a variadic function and instead had some other meaning. It would also be strange if you couldn't explicitly call one of these destructor. Yet doing so represents a problem; you shouldn't be able to invoke this code unless actual exception-based stack unwinding is happening.

DeadMG

unread,
Mar 24, 2013, 4:53:19 PM3/24/13
to std-pr...@isocpp.org
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.

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.

Nicol Bolas

unread,
Mar 24, 2013, 6:08:20 PM3/24/13
to std-pr...@isocpp.org
On Sunday, March 24, 2013 1:53:19 PM UTC-7, DeadMG wrote:
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.

Right but... why do we want that? If all I want is to know whether my destructor is being called due to stack unwinding, do I really want to declare a whole new function just to know that? If there was a real need to be able to differentiate this stuff, then that might be fine. 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()`? You can't really call it a "regular function" if calling it doesn't work regularly, if calling it with parameters can just ignore those parameters and call a completely different overload without any implicit conversion or anything.

Even more importantly, consider std::unique_ptr. Do we want std::default_delete to be passed an exception? Does it need to have this?

template<class U, class E>
void operator() (T*, E&) const;

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 fairly sure C++ doesn't protect this:

T *t = new T;
t
->~T();
delete reinterpret_cast<char *>(t);

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.

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. I'm not saying you can't do it, but it isn't "simple" if people have to learn entirely new rules for how parameters work.

DeadMG

unread,
Mar 24, 2013, 6:51:26 PM3/24/13
to std-pr...@isocpp.org
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

Variadic 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. 

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()`?

~T(...) behaves just like a regular function w.r.t. overload resolution, so ~T(...) will be called. The implicit definition simply calls ~T(), so there's no need for any user code to update as a result of this if they don't need this functionality.

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.

These points I had not considered. It would, indeed, require std::default_delete to be passed an exception (if one occurred) and it would also require essentially new syntax for `delete` to call the appropriate destructor- perhaps `delete T(except);`. 

Conceptually, it offers something more complete than the count-based proposal, which is that you can inspect or mutate exceptions without actually having to throw and catch them, and you don't have to deal with the count. But it definitely also seems to be more of a challenge to specify and implement.

It might be simpler instead to simply build on the previously-suggested std::stack_unwind by adding a method which could be used to get the current exception object- perhaps something like get_exception_as<T>() returning T*.

Ville Voutilainen

unread,
Mar 24, 2013, 6:55:09 PM3/24/13
to std-pr...@isocpp.org
On 25 March 2013 00:51, DeadMG <wolfei...@gmail.com> wrote:
> It might be simpler instead to simply build on the previously-suggested
> std::stack_unwind by adding a method which could be used to get the current
> exception object- perhaps something like get_exception_as<T>() returning T*.

Yep - the existing current_exception() has the limitation that it
won't return an exception
until a handler has been activated. With the stack_unwind, we know
that there's an exception,
but there's no way to get any information about that exception.

Nicol Bolas

unread,
Mar 24, 2013, 8:30:23 PM3/24/13
to std-pr...@isocpp.org


On Sunday, March 24, 2013 3:51:26 PM UTC-7, DeadMG wrote:
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

Variadic 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.

Right, but `catch` is a keyword, not a function. So `keyword(...)` can have be given an arbitrary. We already know what `meaning.functionName(...)` is supposed to mean, so it's confusing to infuse it with a new, arbitrary meaning.

Especially if you say that you can't use the variadic macros to get at the argument.
 

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. 

I would consider that a feature, not a bug. Right now, exception handling is direct communication between the thrower and the catcher. What you suggest would allow someone, who may not be easily visible, to interfere in this communication pathway and theoretically corrupt the exception.

Howard Hinnant

unread,
Mar 24, 2013, 8:48:30 PM3/24/13
to std-pr...@isocpp.org
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. The use of it in the examples I've seen so far looks extremely easy:

class Transaction
{
unsigned on_entry_;
public:
Transaction()
: on_entry_(std::uncaught_exception_count()) {}

~Transaction();
};

Transaction::~Transaction()
{
if( on_entry_ != std::uncaught_exception_count() )
Rollback();
}

Howard

Nicol Bolas

unread,
Mar 24, 2013, 9:50:10 PM3/24/13
to std-pr...@isocpp.org


On Sunday, March 24, 2013 5:48:30 PM UTC-7, Howard Hinnant wrote:
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.

I think, if we're going this route, we should have some reasonable helper objects in addition to the raw count. A simple "unwinding" type who's constructor fetches the count, with an `explicit operator bool` to test to see if it's unwinding, would be a reasonable helper.

I never think we should see this in code: `if(on_entry_ != std::uncaught_exception_count())`. Or at least, not outside of some very specific needs, like someone who absolutely must pack as much data in as small a size as possible. The use of this feature should be as obvious and clear as to what's going on as possible.
 
 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.

It's not so much that it creates hardship as that it's non-trivial. It's intrusive. You can't just put something in the destructor to track it; you have to put it in the object. And therefore, it must go into every object that cares to tell the difference.

It just feels like a hack solution. It doesn't feel like information that should be a part of the object; it instead feels like something that could be tracked externally. It feels like there must be some better, easier, non-intrusive way to do this. Granted, no such method has presented itself thus far. But I think this is something that bears investigation before giving this solution the blessing of standardization.
Reply all
Reply to author
Forward
0 new messages