Question about exceptions.

L

LR

Is this code valid? And if so, what should the output be?


#include <iostream>
#include <exception>

int divide(const int a) {
std::cout << "1 divide(" << a << ")" << std::endl;
if(a == 0)
throw std::exception();
std::cout << "2 divide(" << a << ")" << std::endl;
return 1/a;
}

class X {
int k;
public:
X(const int a)
try : k(divide(a))
{
std::cout << "X(" << a << ")" << std::endl;
}
catch(const std::exception &) {
std::cout << "Caught by X(" << a << ")" << std::endl;
}
};

void test1() {
try {
X a(1);
X b(0);
}
catch(const std::exception &) {
std::cout << "Caught by test1" << std::endl;
}
}

void test2() {
X a(1);
X b(0);
}


int main() {
test1();
test2();
}

TIA.

LR
 
B

BobR

LR wrote in message...
Is this code valid? And if so, what should the output be?

#include <iostream>
#include <exception>

int divide(const int a) {
std::cout << "1 divide(" << a << ")" << std::endl;
if(a == 0)
throw std::exception();
std::cout << "2 divide(" << a << ")" << std::endl;
return 1/a;
}

class X {
int k;
public:
X(const int a) try : k(divide(a)){
std::cout << "X(" << a << ")" << std::endl;
}
catch(const std::exception &) {
std::cout << "Caught by X(" << a << ")" << std::endl;

// throw; // add this (after first test run)
}
};

void test1() {
try {
X a(1);
X b(0);
}
catch(const std::exception &) {
std::cout << "Caught by test1" << std::endl;

// throw; // add this (after 2nd test run, for main() )
}
}

void test2() {
X a(1);
X b(0);
}

int main() {
test1();
test2();
}

TIA. LR

Going through it by hand (my TestBench is temp off-line (busy with another
project, different libraries) ).

// void test1() { try { X a(1); ....
1 divide(1)
2 divide(1)
X(1)
// .... X b(0);
1 divide(0)
Caught by X(0) // X b invalid
// void test2() { X a(1); .....
1 divide(1)
2 divide(1)
X(1)
// .... X b(0);
1 divide(0)
Caught by X(0) // X b invalid

If you want to see the "Caught by test1" message, you should re-throw in the
catch() in X Ctor (shown above).

**But I'm guessing.**
To be sure run the code with this main() as a safety net:

int main() try {
test1();
test2();
return 0;
} // main()
catch( std::exception const &) {
std::cout<< "main caught exception"<<std::endl;
return 1;
} // main()
catch( ... ) {
std::cout<<"caught unknown"<<std::endl;
return 1;
} // main()

See "Thinknog in C++" vol.2
"Part 1: Building Stable Systems, 1: Exception Handling, Function-level
try blocks" for more.

--
Bob R
POVrookie
Get "Thinking in C++", 2nd ed. Volume 1&2 by Bruce Eckel
(available for free here. You can buy it in hardcopy too.):
http://www.mindview.net/Books/TICPP/ThinkingInCPP2e.html
 
L

LR

BobR said:
LR wrote in message...
[snipped code I originally posted, that included some changes suggested
by BobR]

Ok, I snipped BobR's guess as to what the code would do, but it seemed
like what I would expect.


If you want to see the "Caught by test1" message, you should re-throw in the
catch() in X Ctor (shown above).

No. I put it in because it seemed like it was somehow being re-thrown
in my code without my having to re-throw. Please see more below.

**But I'm guessing.**

;)

[snipped code suggestions for main]


I made some but not all of the changes you suggested. Here's the newer code.

#include <iostream>
#include <exception>

int divide(const int a) {
std::cout << "1 divide(" << a << ")" << std::endl;
if(a == 0)
throw std::exception();
std::cout << "2 divide(" << a << ")" << std::endl;
return 1/a;
}

class X {
int k;
public:
X(const int a)
try : k(divide(a))
{
std::cout << "X(" << a << ")" << std::endl;
}
catch(const std::exception &) {
std::cout << "Caught by X(" << a << ")" << std::endl;
}
};

void test1() {
std::cout << "1 test1()" << std::endl;
try {
X a(1);
X b(0);
}
catch(const std::exception &) {
std::cout << "Caught by test1" << std::endl;
}
std::cout << "2 test1()" << std::endl;
}

void test2() {
std::cout << "1 test2()" << std::endl;
X a(1);
X b(0);
std::cout << "2 test2()" << std::endl;
}


int main() try {
test1();
test2();
}
catch(const std::exception &) {
std::cout << "Caught std::exception & in main" << std::endl;
}
catch(...) {
std::cout << "Caught unknown in main" << std::endl;
}

Here's the output with a comment added for the line I don't quite
understand.
-----------------------------------
1 test1()
1 divide(1)
2 divide(1)
X(1)
1 divide(0)
Caught by X(0)
Caught by test1 // I don't quite get how we got caught a second time
2 test1()
1 test2()
1 divide(1)
2 divide(1)
X(1)
1 divide(0)
Caught by X(0)
Caught std::exception & in main // this is a little confusing too.
--------------------------------------

It's the second catch during test1() that I'm confused by. Is that some
compiler magic? A bug? Undefined behavior?
See "Thinknog in C++" vol.2
"Part 1: Building Stable Systems, 1: Exception Handling, Function-level
try blocks" for more.

Thanks for the suggestion. This source (although I may be using a less
recent copy of this text) says:

"If an exception does occur, the contained object is not constructed, so
it makes no sense to return to the code that created it. For this
reason, the only sensible thing to do is to throw an exception in the
function-level catch clause."

That makes sense, because:
SomeObject x; // If this throws, then...
x.someMethod(); // ...what happens here?

But the standard says in 15 note 3 (I copied this from the 2007 draft,
but the text appears to be the same as the text in the standard.)

"A function-try-block associates a handler-seq with the
ctor-initializer, if present, and the function-body. An exception thrown
during the execution of the initializer expressions in the
ctor-initializer or during the execution of the functionbody transfers
control to a handler in a function-try-block in the same way as an
exception thrown during the execution of a try-block transfers control
to other handlers."

From my understanding, then we get caught and continue on. Perhaps
there's another place in the standard that covers this situation?

So I guess what my example code is doing is a good thing, but is that
what the standard says should happen?

Get "Thinking in C++", 2nd ed. Volume 1&2 by Bruce Eckel
(available for free here. You can buy it in hardcopy too.):
http://www.mindview.net/Books/TICPP/ThinkingInCPP2e.html


LR
 
B

BobR

LR wrote in message...
[snipped code suggestions for main]

I made some but not all of the changes you suggested. Here's the newer code.

#include <iostream>
#include <exception>

int divide(const int a) {
std::cout << "1 divide(" << a << ")" << std::endl;
if(a == 0)
throw std::exception();
std::cout << "2 divide(" << a << ")" << std::endl;
return 1/a;
}

class X {
int k;
public:
X(const int a)
try : k(divide(a))
{
std::cout << "X(" << a << ")" << std::endl;
}
catch(const std::exception &) {
std::cout << "Caught by X(" << a << ")" << std::endl;
}
};

void test1() {
std::cout << "1 test1()" << std::endl;
try {
X a(1);
X b(0);
}
catch(const std::exception &) {
std::cout << "Caught by test1" << std::endl;
}
std::cout << "2 test1()" << std::endl;
}

void test2() {
std::cout << "1 test2()" << std::endl;
X a(1);
X b(0);
std::cout << "2 test2()" << std::endl;
}

int main() try {
test1();
test2();
}
catch(const std::exception &) {
std::cout << "Caught std::exception & in main" << std::endl;
}
catch(...) {
std::cout << "Caught unknown in main" << std::endl;
}

Here's the output with a comment added for the line I don't quite
understand.
-----------------------------------
1 test1()
1 divide(1)
2 divide(1)
X(1)
1 divide(0)
Caught by X(0)
Caught by test1 // I don't quite get how we got caught a second time

Not sure. I *think* it's because X is not constructed, and you can't return
from an handler [1], the exception needs to follow the chain back to an
handler.

[1]
class X {
// ......
catch( std::exception const &Se) {
// return;
// error: cannot return from a handler of a function-try-block of a
constructor
}
2 test1()

It got to here. The exception was handled by 'test1()' (not passed back to
'main()').
1 test2()
1 divide(1)
2 divide(1)
X(1)
1 divide(0)
Caught by X(0)
Caught std::exception & in main // this is a little confusing too.

There was no handler in 'test2()', so it looks for the next handler, which
it finds in 'main()'.

Unless some expert comes by, we may both hang in darkness for the rest of
our C++ lives! said:
"If an exception does occur, the contained object is not constructed, so
it makes no sense to return to the code that created it. For this
reason, the only sensible thing to do is to throw an exception in the
function-level catch clause."

Some light!
That makes sense, because:
SomeObject x; // If this throws, then...
x.someMethod(); // ...what happens here?

But the standard says in 15 note 3 (I copied this from the 2007 draft,
but the text appears to be the same as the text in the standard.)

"A function-try-block associates a handler-seq with the
ctor-initializer, if present, and the function-body. An exception thrown
during the execution of the initializer expressions in the
ctor-initializer or during the execution of the functionbody transfers
control to a handler in a function-try-block in the same way as an
exception thrown during the execution of a try-block transfers control
to other handlers."

From my understanding, then we get caught and continue on. Perhaps
there's another place in the standard that covers this situation?

Or, they left it to the implementation. Freedom.
So I guess what my example code is doing is a good thing, but is that
what the standard says should happen?

I always thought that once caught, the exception ceased, unless re-thrown.
This seems to be an exceptional (pun?) situation. It kind-of makes sense.
The X object failed to construct, so, the exception can't get fully handled
there.
..
 
J

James Kanze

LR wrote in message...

[...]
// throw; // add this (after first test run)

The throw isn't necessary; it's the default behavior here. See
§15.3 ([Handling an Exception), para. 15: "The currently handled
exception is rethrown if control reaches the end of a handler of
the function-try-block of a constructor or destructor.
Otherwise, a function returns when control reaches the end of a
handler for the functiontryblock. Flowing off the end of a
function-try-block is equivalent to a return with no value; this
results in undefined behavior in a value-returning function."
 
L

LR

BobR said:
LR wrote in message...
I always thought that once caught, the exception ceased, unless re-thrown.

That's what I thought too.

This seems to be an exceptional (pun?) situation.

LOL. Thinknog in C++! ;)

It kind-of makes sense.
The X object failed to construct, so, the exception can't get fully handled
there.

I'll almost agree there, I think it might depend on what code comes
afterward. For example, if the unconstructed object is never used
again, then maybe it's ok, and I think that might be possible, if for
example the object is created using placement new. But I wish someone
who knows for sure would say. Or maybe the standard needs a change?

Thanks for your responses. Much appreciated.

LR
 
L

LR

James said:
LR wrote in message...
Is this code valid? And if so, what should the output be?
[...]
class X {
int k;
public:
X(const int a) try : k(divide(a)){
std::cout << "X(" << a << ")" << std::endl;
}
catch(const std::exception &) {
std::cout << "Caught by X(" << a << ")" << std::endl;
// throw; // add this (after first test run)

The throw isn't necessary; it's the default behavior here. See
§15.3 ([Handling an Exception), para. 15: "The currently handled
exception is rethrown if control reaches the end of a handler of
the function-try-block of a constructor or destructor.
Otherwise, a function returns when control reaches the end of a
handler for the functiontryblock. Flowing off the end of a
function-try-block is equivalent to a return with no value; this
results in undefined behavior in a value-returning function."

Ah. Ok, thanks very much for that. Appreciated.

LR
 
B

BobR

/* """

LR wrote in message...
Is this code valid? And if so, what should the output be? [...]
class X {
int k;
public:
X(const int a) try : k(divide(a)){
std::cout << "X(" << a << ")" << std::endl;
}
catch(const std::exception &) {
std::cout << "Caught by X(" << a << ")" << std::endl;
// throw; // add this (after first test run)

The throw isn't necessary; it's the default behavior here. See
§15.3 ([Handling an Exception), para. 15: "The currently handled
exception is rethrown if control reaches the end of a handler of
the function-try-block of a constructor or destructor.
Otherwise, a function returns when control reaches the end of a
handler for the functiontryblock. Flowing off the end of a
function-try-block is equivalent to a return with no value; this
results in undefined behavior in a value-returning function."

""" */

Yep. When I re-set my directories to normal, fired up my TestBench and
plugged in LR's code, I realized what was happening (re-throw not needed).

[ In the one place I do use such code, I re-throw my own runtime_error
derived class, which informs, then shuts down the app. I think that's why I
suggested that re-throw to LR when I was 'guessing'.]

Thanks James.

Good to have you back. :-}
[ Good vacation? ]
 
B

BobR

LR wrote in message...
I'll almost agree there, I think it might depend on what code comes
afterward. For example, if the unconstructed object is never used
again, then maybe it's ok, and I think that might be possible, if for
example the object is created using placement new.

From 'TiCpp', vol.1 (chap.13, "placement new & delete"):

"There's also a placement operator delete that is only called if a
constructor for a placement new expression throws an exception (so that the
memory is automatically cleaned up during the exception). The placement
operator delete has an argument list that corresponds to the placement
operator new that is called before the constructor throws the exception.
This topic will be explored in the exception handling chapter in Volume 2. "
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top