Discussion:
An exception that throws while it is being thrown, no terminate?
(too old to reply)
Niels Dekker - no reply address
2009-12-02 19:52:33 UTC
Permalink
When the copy-constructor of an object throws an exception while the object
itself is being thrown, I'd expect std::terminate to be called. But it
doesn't seem to happen! At least, not when I use Visual C++. I've tried
both VC 2008 and VC 2010 Beta 1. Is that a known compiler bug? I can't find
it at https://connect.microsoft.com/VisualStudio

Typical example: suppose I have an exception class, MyException, containing
an std::string. This is bad practice, of course, because std::string may
itself throw an exception, while it is being copied. Instead of waiting for
std::string to throw, I simply added "throw std::bad_alloc()" to the
copy-constructor of MyException, for the sake of the test. Please have a
look:

//////////////////////////////////////////////////
#include <cstdlib>
#include <stdexcept>
#include <string>

class MyException: public std::logic_error
{
std::string m_string;
public:
MyException(const std::string& arg)
:
std::logic_error(arg), m_string(arg) {}

// A throwing copy-constructor!
MyException(const MyException& arg)
:
std::logic_error(arg), m_string(arg.m_string)
{
// Triggering std::terminate...?
throw std::bad_alloc();
}

~MyException() throw() { }
};

int main()
{
const MyException constException("what");
try
{
// Here the copy-constructor is called:
throw constException;
}
catch( std::bad_alloc & )
{
// Here is where we get!
return EXIT_FAILURE;
}
}
//////////////////////////////////////////////////

Shouldn't the above test program call std::terminate, instead of catching
the std::bad_alloc?


Kind regards,

Niels
--
Niels Dekker
http://www.xs4all.nl/~nd/dekkerware
Scientific programmer at LKEB, Leiden University Medical Center
Martin B.
2009-12-03 14:07:42 UTC
Permalink
Post by Niels Dekker - no reply address
When the copy-constructor of an object throws an exception while the object
itself is being thrown, I'd expect std::terminate to be called. But it
doesn't seem to happen! At least, not when I use Visual C++. I've tried
both VC 2008 and VC 2010 Beta 1. Is that a known compiler bug? I can't find
it at https://connect.microsoft.com/VisualStudio
Typical example: suppose I have an exception class, MyException, containing
an std::string. This is bad practice, of course, because std::string may
itself throw an exception, while it is being copied. Instead of waiting for
std::string to throw, I simply added "throw std::bad_alloc()" to the
copy-constructor of MyException, for the sake of the test. Please have a
//////////////////////////////////////////////////
#include <cstdlib>
#include <stdexcept>
#include <string>
class MyException: public std::logic_error
{
std::string m_string;
MyException(const std::string& arg)
std::logic_error(arg), m_string(arg) {}
// A throwing copy-constructor!
MyException(const MyException& arg)
std::logic_error(arg), m_string(arg.m_string)
{
// Triggering std::terminate...?
throw std::bad_alloc();
}
~MyException() throw() { }
};
int main()
{
const MyException constException("what");
try
{
throw constException;
}
catch( std::bad_alloc & )
{
// Here is where we get!
return EXIT_FAILURE;
}
}
//////////////////////////////////////////////////
Shouldn't the above test program call std::terminate, instead of catching
the std::bad_alloc?
AFAIK, the implementation is *allowed* to copy the exception object but
is not required to, so it may just be that at runtime the copy-ctor is
never called.

br,
Martin
Niels Dekker - no reply address
2009-12-03 14:57:11 UTC
Permalink
Post by Martin B.
Post by Niels Dekker - no reply address
class MyException: public std::logic_error
{
std::string m_string;
MyException(const std::string& arg)
std::logic_error(arg), m_string(arg) {}
// A throwing copy-constructor!
MyException(const MyException& arg)
std::logic_error(arg), m_string(arg.m_string)
{
// Triggering std::terminate...?
throw std::bad_alloc();
}
~MyException() throw() { }
};
int main()
{
const MyException constException("what");
try
{
throw constException;
}
catch( std::bad_alloc & )
{
// Here is where we get!
return EXIT_FAILURE;
}
}
Shouldn't the above test program call std::terminate, instead of
catching the std::bad_alloc?
AFAIK, the implementation is *allowed* to copy the exception object
but is not required to, so it may just be that at runtime the
copy-ctor is never called.
Thanks, Martin. In some cases, the compiler is indeed allowed to omit the
copy-constructor call. But in this case, I don't think it is allowed. The
test program certainly *does* call the copy-constructor of MyException, when
compiled by VC. So don't you think it should trigger a call to
std::terminate?

Doesn't note 141 from the C++ Working Draft apply here? As follows:

"For example, if the object being thrown is of a class with a copy
constructor, std::terminate() will be called if that copy constructor exits
with an exception during a throw."

See also: Working Draft, Standard for Programming Language C++, section
15.5.1, The std::terminate() function, [except.terminate],
www.open-std.org/JTC1/sc22/WG21/docs/papers/2009/n3000.pdf

Kind regards,

Niels
Martin B.
2009-12-03 16:13:33 UTC
Permalink
Post by Niels Dekker - no reply address
Post by Martin B.
Post by Niels Dekker - no reply address
class MyException: public std::logic_error
{
std::string m_string;
MyException(const std::string& arg)
std::logic_error(arg), m_string(arg) {}
// A throwing copy-constructor!
MyException(const MyException& arg)
std::logic_error(arg), m_string(arg.m_string)
{
// Triggering std::terminate...?
throw std::bad_alloc();
}
~MyException() throw() { }
};
int main()
{
const MyException constException("what");
try
{
throw constException;
}
catch( std::bad_alloc & )
{
// Here is where we get!
return EXIT_FAILURE;
}
}
Shouldn't the above test program call std::terminate, instead of
catching the std::bad_alloc?
AFAIK, the implementation is *allowed* to copy the exception object
but is not required to, so it may just be that at runtime the
copy-ctor is never called.
Thanks, Martin. In some cases, the compiler is indeed allowed to omit the
copy-constructor call. But in this case, I don't think it is allowed. The
test program certainly *does* call the copy-constructor of MyException, when
compiled by VC. So don't you think it should trigger a call to
std::terminate?
How do you verify that the copy-ctor is called?
Have you added some tracing, do you set a breakpoint?
Are you running the process under the debugger? release/debug
compilation? What operating system are you using exactly?

br,
Martin
Niels Dekker - no reply address
2009-12-03 17:32:29 UTC
Permalink
Post by Martin B.
How do you verify that the copy-ctor is called?
Have you added some tracing, do you set a breakpoint?
Yes, when I run within Visual Studio 2008, having a breakpoint in the
copy-constructor, it gets there.
Post by Martin B.
Are you running the process under the debugger?
release/debug compilation?
I tried both debug and compilation. Neither of them appear to do an
std::terminate. Both compilations produce a similar output:

First-chance exception at 0x... in MyTest.exe: Microsoft C++ exception:
std::bad_alloc at memory location 0x...
The program '[...] MyTest.exe: Native' has exited with code 1 (0x1).
Post by Martin B.
What operating system are you using exactly?
Windows XP Professional.

Are you able to reproduce it? Maybe a slightly extended main() function
could be helpful, as follows:

//////////////////////////////////////////////////
int main()
{
const MyException constException("what");
try
{
// Here the copy-constructor is called:
throw constException;
}
catch( MyException & referenceToMyException )
{
// Modify the exception. So this must be a copy!
// Note that we don't actually get here.
referenceToMyException = MyException("");
}
catch( std::bad_alloc & )
{
// Here is where we get!
return EXIT_FAILURE;
}
// This line is never reached.
return 0;
}

//////////////////////////////////////////////////

Here I hope you see more clearly that constException *must* be copied, in
order to support the /potential/ assignment to referenceToMyException. (Note
that the original exception object is "const".) Also I added "return 0" at
the end, for clarity. Although it is never reached, because the program
always returns EXIT_FAILURE, when compiled on VC. Please let me know if you
agree that std::terminate should be called instead!

Kind regards,

Niels
--
Niels Dekker
http://www.xs4all.nl/~nd/dekkerware
Scientific programmer at LKEB, Leiden University Medical Center
Martin B.
2009-12-07 12:05:38 UTC
Permalink
Post by Niels Dekker - no reply address
Post by Martin B.
How do you verify that the copy-ctor is called?
Have you added some tracing, do you set a breakpoint?
Yes, when I run within Visual Studio 2008, having a breakpoint in the
copy-constructor, it gets there.
Post by Martin B.
Are you running the process under the debugger?
.........
Are you able to reproduce it? Maybe a slightly extended main() function
//////////////////////////////////////////////////
int main()
{
const MyException constException("what");
try
{
throw constException;
}
catch( MyException & referenceToMyException )
{
// Modify the exception. So this must be a copy!
// Note that we don't actually get here.
referenceToMyException = MyException("");
}
catch( std::bad_alloc & )
{
// Here is where we get!
return EXIT_FAILURE;
}
// This line is never reached.
return 0;
}
//////////////////////////////////////////////////
Here I hope you see more clearly that constException *must* be copied, in
order to support the /potential/ assignment to referenceToMyException. (Note
that the original exception object is "const".) Also I added "return 0" at
the end, for clarity. Although it is never reached, because the program
always returns EXIT_FAILURE, when compiled on VC. Please let me know if you
agree that std::terminate should be called instead!
I've tried this with VS2005 (VC8) and I can confirm the behaviour,
however I would like to add the following:

a) throw MyException("anonymous") + catch-by-ref = This will *not*
invoke the copy-ctor and so will correctly catch MyException by reference

b) throw MyException("anonymous") + catch-by-value = This needs a
copy-ctor call at the catch site, and it *will* correctly call
std::terminate

c) const MyException cExcObj("named const") + throw cExcObj + catch-by-*
= This requires a copy-ctor call, but the copy-ctor call is done
*before* the throw and thus it will just raise the bad_alloc.
From what James wrote, I guess this is non std compliant.

br,
Martin
Niels Dekker - no reply address
2009-12-07 12:41:23 UTC
Permalink
Post by Martin B.
I've tried this with VS2005 (VC8) and I can confirm the behaviour,
a) throw MyException("anonymous") + catch-by-ref = This will *not*
invoke the copy-ctor and so will correctly catch MyException by reference
b) throw MyException("anonymous") + catch-by-value = This needs a
copy-ctor call at the catch site, and it *will* correctly call
std::terminate
c) const MyException cExcObj("named const") + throw cExcObj +
catch-by-* = This requires a copy-ctor call, but the copy-ctor call
is done *before* the throw and thus it will just raise the bad_alloc.
From what James wrote, I guess this is non std compliant.
Thanks Martin. That's also according to my observation.

However, the notes from April, 2006 at
www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#475 appear to support
Post by Martin B.
The CWG agreed with the position that std::uncaught_exception() should
return false during the copy to the exception object and that
std::terminate() should not be called if that constructor exits with an
exception. The issue was returned to "drafting" status for rewording to
reflect this position.
(Thanks to Daniel Kruegler for the link!)

I'm still considering to submit a bug report to Microsoft, mainly to have
this behavior documented...


Kind regards,

Niels
--
Niels Dekker
http://www.xs4all.nl/~nd/dekkerware
Scientific programmer at LKEB, Leiden University Medical Center
Niels Dekker - no reply address
2009-12-07 17:39:24 UTC
Permalink
Post by Niels Dekker - no reply address
I'm still considering to submit a bug report to Microsoft, mainly to have
this behavior documented...
Please have a look at the bug report I have just submitted: "No
std::terminate() when an exception throws while being thrown",
https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=518603

Please let me know if you have any comment. (I can still edit the report, if
necessary.) It would also be helpful if you can indicate on the web page
that you can reproduce the bug, by clicking "I can too". And you can vote
for the issue, if you want to.


Kind regards,

Niels
--
Niels Dekker
http://www.xs4all.nl/~nd/dekkerware
Scientific programmer at LKEB, Leiden University Medical Center
Bo Persson
2009-12-03 18:17:30 UTC
Permalink
Post by Niels Dekker - no reply address
Post by Martin B.
Post by Niels Dekker - no reply address
class MyException: public std::logic_error
{
std::string m_string;
MyException(const std::string& arg)
std::logic_error(arg), m_string(arg) {}
// A throwing copy-constructor!
MyException(const MyException& arg)
std::logic_error(arg), m_string(arg.m_string)
{
// Triggering std::terminate...?
throw std::bad_alloc();
}
~MyException() throw() { }
};
int main()
{
const MyException constException("what");
try
{
throw constException;
}
catch( std::bad_alloc & )
{
// Here is where we get!
return EXIT_FAILURE;
}
}
Shouldn't the above test program call std::terminate, instead of
catching the std::bad_alloc?
AFAIK, the implementation is *allowed* to copy the exception object
but is not required to, so it may just be that at runtime the
copy-ctor is never called.
Thanks, Martin. In some cases, the compiler is indeed allowed to
omit the copy-constructor call. But in this case, I don't think it
is allowed. The test program certainly *does* call the
copy-constructor of MyException, when compiled by VC. So don't you
think it should trigger a call to std::terminate?
"For example, if the object being thrown is of a class with a copy
constructor, std::terminate() will be called if that copy
constructor exits with an exception during a throw."
See also: Working Draft, Standard for Programming Language C++,
section 15.5.1, The std::terminate() function, [except.terminate],
www.open-std.org/JTC1/sc22/WG21/docs/papers/2009/n3000.pdf
Kind regards,
Niels
No, it doesn't really apply because the std::bad_alloc appears before
and not during the throw! :-)

You throw by value, and I believe the implementation is allowed to
create a copy *before* the copy is thrown. This is similar to
evaluating all parameters before calling a function.


Bo Persson
James Kanze
2009-12-04 10:20:14 UTC
Permalink
Post by Bo Persson
Post by Niels Dekker - no reply address
Post by Martin B.
Post by Niels Dekker - no reply address
class MyException: public std::logic_error
{
std::string m_string;
MyException(const std::string& arg)
std::logic_error(arg), m_string(arg) {}
// A throwing copy-constructor!
MyException(const MyException& arg)
std::logic_error(arg), m_string(arg.m_string)
{
// Triggering std::terminate...?
throw std::bad_alloc();
}
~MyException() throw() { }
};
int main()
{
const MyException constException("what");
try
{
throw constException;
}
catch( std::bad_alloc & )
{
// Here is where we get!
return EXIT_FAILURE;
}
}
Shouldn't the above test program call std::terminate,
instead of catching the std::bad_alloc?
AFAIK, the implementation is *allowed* to copy the
exception object but is not required to, so it may just be
that at runtime the copy-ctor is never called.
Thanks, Martin. In some cases, the compiler is indeed
allowed to omit the copy-constructor call. But in this case,
I don't think it is allowed. The test program certainly
*does* call the copy-constructor of MyException, when
compiled by VC. So don't you think it should trigger a call
to std::terminate?
Doesn't note 141 from the C++ Working Draft apply here? As
"For example, if the object being thrown is of a class with
a copy constructor, std::terminate() will be called if that
copy constructor exits with an exception during a throw."
See also: Working Draft, Standard for Programming Language C++,
section 15.5.1, The std::terminate() function, [except.terminate],
www.open-std.org/JTC1/sc22/WG21/docs/papers/2009/n3000.pdf
No, it doesn't really apply because the std::bad_alloc appears
before and not during the throw! :-)
You throw by value, and I believe the implementation is
allowed to create a copy *before* the copy is thrown. This is
similar to evaluating all parameters before calling a
function.
I don't see where you get this. There are two parts of a throw
expression: the expression which defines what is being thrown,
and the throw itself. In this case, the expression which
defines what is being thrown is simply constExpression, an
lvalue of type MyException. I don't see any place in the
standard that allows any copies here. Then we enter into the
throw itself, which does copy. But here, 15.5.1 is clear:

In the following situations exception handling must be
abandoned for less subtle error handling techniques:

-- when the exception handling mechanism, after completing
evaluation of the expression to be thrown but before the
exception is caught (15.1), calls a user function that
exits via an uncaught exception,

A user defined copy constructor is in fact about the only I can
think of which could be called, and it is, in fact, mentionned
in a footnote---non normative, but a very clear indication of
intent.

--
James Kanze
Niels Dekker - no reply address
2009-12-04 19:29:55 UTC
Permalink
Post by Bo Persson
Doesn't note 141 from the C++ Working Draft [N3000] apply here?
"For example, if the object being thrown is of a class with a
copy constructor, std::terminate() will be called if that
copy constructor exits with an exception during a throw."
No, it doesn't really apply because the std::bad_alloc appears
before and not during the throw! :-)
You throw by value, and I believe the implementation is
allowed to create a copy *before* the copy is thrown. This is
similar to evaluating all parameters before calling a
function.
I don't see where you get this. There are two parts of a throw
expression: the expression which defines what is being thrown,
and the throw itself. In this case, the expression which
defines what is being thrown is simply constExpression, an
lvalue of type MyException. I don't see any place in the
standard that allows any copies here. Then we enter into the
In the following situations exception handling must be
-- when the exception handling mechanism, after completing
evaluation of the expression to be thrown but before the
exception is caught (15.1), calls a user function that
exits via an uncaught exception,
A user defined copy constructor is in fact about the only I can
think of which could be called, and it is, in fact, mentionned
in a footnote---non normative, but a very clear indication of
intent.
Thanks! I think I'll submit a bug report to connect.microsoft.com this
weekend. Unless I can still get convinced that it's not a compiler bug, of
course...


Kind regards,

Niels
--
Niels Dekker
http://www.xs4all.nl/~nd/dekkerware
Scientific programmer at LKEB, Leiden University Medical Center
Loading...