From: Bo Persson on
Niels Dekker - no reply address wrote:
>>> 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?
>
> Martin replied:
>> 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

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


From: James Kanze on
On Dec 3, 6:17 pm, "Bo Persson" <b...(a)gmb.dk> wrote:
> Niels Dekker - no reply address wrote:
> >>> 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?

> > Martin replied:
> >> 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

> 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
From: Niels Dekker - no reply address on
>> Doesn't note 141 from the C++ Working Draft [N3000] 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."

Bo Persson wrote:
> 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.

James Kanze wrote:
> 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.


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


From: Martin B. on
Niels Dekker - no reply address wrote:
> Martin B. wrote:
>> 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.
>
>> Are you running the process under the debugger?
> .........
> 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!
>

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
From: Niels Dekker - no reply address on
Martin B. wrote:
> 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.

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
Bo's point of view:
>
> 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