0

There are several special functions which usually guarantee not to throw excpetions, e.g.:

  • Destructors
  • swap method

Consider the following swap implementation, as stated in this answer:

friend void swap(dumb_array& first, dumb_array& second)
{
    using std::swap; 

    swap(first.mSize, second.mSize);  
    swap(first.mArray, second.mArray);  // What if stack overlow occurs here?
}

It uses two swap functions - for integer and for pointer. What if the second function will cause stack overflow? Objects will become corrupted. I guess it is not an std::exception, it is some kind of system exception, like Win32-exception. But now we cannot guarantee no-throwing, since we're calling a function.

But all authoritative sources just use swap like it's ok, no exceptions will ever be thrown here. Why?

Community
  • 1
  • 1
Mikhail
  • 18,155
  • 5
  • 56
  • 129
  • Destructors don't guarantee not to throw exceptions. – Kerrek SB Apr 18 '13 at 10:25
  • Ok, they should normally do this, though they are not forced to it. – Mikhail Apr 18 '13 at 10:27
  • 1
    @KerrekSB: mine do, so I don't know what's wrong with yours if they don't ;-p – Steve Jessop Apr 18 '13 at 10:29
  • Stack overflow isn't a catchable error condition, so there isn't really anything you can do short of not using that much stack space. This hasn't anything to do with exceptions. – Kerrek SB Apr 18 '13 at 10:30
  • 2
    @SteveJessop: I don't want [`std::is_nothrow_destructible`](http://en.cppreference.com/w/cpp/types/is_destructible) to feel entirely useless :-) – Kerrek SB Apr 18 '13 at 10:31
  • @KerrekSB: interesting, the `noexcept()` condition for `std::swap` doesn't check that the destructor is nothrow. Suggests to me that the authors of `` consider `is_nothrow_destructible` entirely useless. Admittedly the object that the default `swap` implementation destroys has already been moved from, which is a special case. Maybe they figured they'd get false positives by asking whether the destructor is *always* nothrow. – Steve Jessop Apr 18 '13 at 10:42
  • Why do you think that a swap of two pointers might cause a stack overflow but not a swap of two integers? – CB Bailey Apr 18 '13 at 10:51
  • @CharlesBailey Because a) pointers might be bigger than integers, b) OS might have reduced stack size between these operations, c) there may be other swaps with bigger or user-defined types. Anyway, these operations doesn't look like 100% no-throwing, i.e. like nothing could possible go wrong. – Mikhail Apr 18 '13 at 10:53
  • 3
    @Mikhail: There's a huge difference between "throwing" and "going wrong". Exceptions are a well-defined part of program flow; running out of stack is a serious, unrecoverable error condition. Whatever happens in that situation, nothing should be thrown. – Mike Seymour Apr 18 '13 at 10:55
  • @Mikhail: OK, pointers - even if they are bigger than integers (in this case `std::size_t`) - aren't going to be *that* much bigger than integers and if you're so short of stack space that you don't have room for one extra stack allocated pointer you're likely to be doomed in any case. I'm not sure what other swaps with user-defined types you are referring to; in your example there aren't any - only two pointers are being swapped. – CB Bailey Apr 18 '13 at 10:57

2 Answers2

4

In general you cannot handle running out of stack. The standard doesn't say what happens if you run out of stack, neither does it talk about what the stack is, how much is available, etc. OSes may let you control it at the time the executable is built or when it is run, all of which is fairly irrelevant if you're writing library code, since you have no control of how much stack the process has, or how much has already been used before the user calls into your library.

You can assume that stack overflow results in the OS doing something external to your program. A very simple OS might just let it go weird (undefined behavior), a serious OS might blow the process away, or if you're really unlucky it throws some implementation-defined exception. I actually don't know whether Windows offers an SEH exception for stack overflow, but if it does then it's probably best not to enable it.

If you're concerned, you can mark your swap function as noexcept. Then in a conforming implementation, any exception that tries to leave the function will cause the program to terminate(). That is to say, it fulfils the noexcept contract at the cost of taking out your program.

Steve Jessop
  • 257,525
  • 32
  • 431
  • 672
2

What if the second function will cause stack overflow?

Then your program is in an unrecoverable faulted state, and there is no practical way to handle the situation. Hopefully, the overflow has already caused a segmenation fault and terminated the program.

But now we cannot guarantee no-throwing

I've never encountered an implementation that would throw an exception in that state, and I'd be rather scared if it did.

But all authoritative sources just use swap like it's ok, no exceptions will ever be thrown here. Why?

The authoritative sources I've read (like this one, for example) don't "just use it like it's OK"; they say that if you have (for example) a non-throwing swap function, and a non-throwing destructor, then you can provide exception-safety guarantees from functions that use them.

It's useful to categorise functions according to their exception guarantees:

  • Basic: exceptions leave everything in a valid but unspecified state
  • Strong: exceptions leave the state unchanged
  • No-throw: no exceptions will be thrown.

Than a common approach to providing the "strong" guarantee is:

  • do the work that might throw on a temporary copy of the state
  • swap that copy with the live state (requiring a non-throwing swap operation)
  • destroy the old state (requiring a non-throwing destructor)

If you don't have a no-throw guarantee from those operations, then it's more difficult, and perhaps impossible, to provide a strong guarantee.

Mike Seymour
  • 235,407
  • 25
  • 414
  • 617
  • But what if even I _have_ a non-throwing `swap`, but when I'm actually trying to _call_ it, I run out of stack? Should I read "no-throwing" like "no-throwing, if called"? – Mikhail Apr 18 '13 at 10:49
  • 2
    @Mikhail: If you run out of stack, then all bets are off. As I said in the first paragraph, that's an unrecoverable situation, and hopefully the program will immediately terminate. Throwing an exception in that state would be insane, and hopefully no implementation tries to. So you should read "no-throwing" as "no-throwing, ever". – Mike Seymour Apr 18 '13 at 10:52