Troubles with exception::what()

D

Dario

The following simple program behaves differently
in Windows and Linux .

#include <stdexcept>
#include <iostream>
#include <string>
using namespace std;
class LogicError : public logic_error {
public:
string desc;
explicit LogicError(string desc)
: logic_error("parent description"), desc(desc) {
}
virtual const char * what() const throw() {
return desc.c_str();
}
};
void f() {
throw LogicError("child description");
}
int main() {
try {
f();
} catch(exception e) {
cout << "e.what=[" << e.what() << "]" << endl;
}
try {
f();
} catch(exception & e) {
cout << "e.what=[" << e.what() << "]" << endl;
}
return 0;
}

When I run it on Windows (Visual Studio 6.0) I obtain:
e.what=[]
e.what=[child description]

When I run it on Windows (Visual Studio 7.1) I obtain:
e.what=[Unknown exception]
e.what=[child description]

On Linux I (obtain:
e.what=[9exception]
e.what=[child description]

Two questions to the C++ gurus:
<1> Which is the expected ANSI-C++ behaviour?
<2> Why. in each run, the first output row
is different from the second one?

Thanks for the help.

- Dario
 
D

Dario

The following simple program behaves differently
in Windows and Linux .

#include <stdexcept>
#include <iostream>
#include <string>
using namespace std;
class LogicError : public logic_error {
public:
string desc;
explicit LogicError(string desc)
: logic_error("parent description"), desc(desc) {
}
virtual const char * what() const throw() {
return desc.c_str();
}
};
void f() {
throw LogicError("child description");
}
int main() {
try {
f();
} catch(exception e) {
cout << "e.what=[" << e.what() << "]" << endl;
}
try {
f();
} catch(exception & e) {
cout << "e.what=[" << e.what() << "]" << endl;
}
return 0;
}

When I run it on Windows (Visual Studio 6.0) I obtain:
e.what=[]
e.what=[child description]

When I run it on Windows (Visual Studio 7.1) I obtain:
e.what=[Unknown exception]
e.what=[child description]

On Linux I (obtain:
e.what=[9exception]
e.what=[child description]

Two questions to the C++ gurus:
<1> Which is the expected ANSI-C++ behaviour?
<2> Why. in each run, the first output row
is different from the second one?

Thanks for the help.

- Dario

No-one is able to give me an help ?

- Dario
 
G

Gianni Mariani

Dario said:
The following simple program behaves differently
in Windows and Linux .
#include <stdexcept>
#include <iostream>
#include <string>
using namespace std;
class LogicError : public logic_error {
public:
string desc;
explicit LogicError(string desc)
: logic_error("parent description"), desc(desc) {
}
virtual const char * what() const throw() {
return desc.c_str();
}

This is overriding the base "what" method.

Incidently, gcc 3.3.1 needed this:

~LogicError() throw() {};

It seems to make sense since the destructor of the base class also
specified throw().
};
void f() {
throw LogicError("child description");
}
int main() {
try {
f();
} catch(exception e) {

Exception caught by value - a copy of "exception" is made here.

Well this means that you're not using the method what above.

cout << "e.what=[" << e.what() << "]" << endl;
}
try {
f();
} catch(exception & e) {

Exception caught by reference - cool, now you will get the right what
becuase you are actually using a LogicError object and not an
"exception" object copied.
cout << "e.what=[" << e.what() << "]" << endl;
}
return 0;
}

When I run it on Windows (Visual Studio 6.0) I obtain:
e.what=[]
e.what=[child description]

When I run it on Windows (Visual Studio 7.1) I obtain:
e.what=[Unknown exception]
e.what=[child description]

On Linux I (obtain:
e.what=[9exception]
e.what=[child description]

Two questions to the C++ gurus:
<1> Which is the expected ANSI-C++ behaviour?

All of them. What happens on the first catch is undefined.
<2> Why. in each run, the first output row
is different from the second one?

Because they are different objects.
 
B

Bob Hairgrove

He also should have a *virtual* destructor. It might be a better idea
to derive the class from std::exception and not from one of its
derived classes because not every implementation of the STL uses a
virtual destructor in the derived classes. In some of them, it will
work; in others, maybe not.
This is overriding the base "what" method.

Incidently, gcc 3.3.1 needed this:

~LogicError() throw() {};

It seems to make sense since the destructor of the base class also
specified throw().
};
void f() {
throw LogicError("child description");
}
int main() {
try {
f();
} catch(exception e) {

Exception caught by value - a copy of "exception" is made here.

Well this means that you're not using the method what above.

cout << "e.what=[" << e.what() << "]" << endl;
}
try {
f();
} catch(exception & e) {

Exception caught by reference - cool, now you will get the right what
becuase you are actually using a LogicError object and not an
"exception" object copied.
cout << "e.what=[" << e.what() << "]" << endl;
}
return 0;
}

When I run it on Windows (Visual Studio 6.0) I obtain:
e.what=[]
e.what=[child description]

When I run it on Windows (Visual Studio 7.1) I obtain:
e.what=[Unknown exception]
e.what=[child description]

On Linux I (obtain:
e.what=[9exception]
e.what=[child description]

Two questions to the C++ gurus:
<1> Which is the expected ANSI-C++ behaviour?

All of them. What happens on the first catch is undefined.
<2> Why. in each run, the first output row
is different from the second one?

Because they are different objects.
Thanks for the help.
 
D

Dario

Bob said:
BTW, one shouldn't ever put "using namespace ...<anything>" in a
header file... for toy programs such as this, it is probably OK.

I use "using namespace ... said:
He also should have a *virtual* destructor.

Yes, in my "actual" code I have it.
It might be a better idea to derive the class from std::exception and not from one of its
derived classes because not every implementation of the STL uses a
virtual destructor in the derived classes. In some of them, it will
work; in others, maybe not.

But my LogicError is a logic_error and not a generic exception.
So I will continue to derive from logic_error.
Incidently, gcc 3.3.1 needed this:

~LogicError() throw() {};

It seems to make sense since the destructor of the base class also
specified throw().
OK.
void f() {
throw LogicError("child description");
}
int main() {
try {
f();
} catch(exception e) {

Exception caught by value - a copy of "exception" is made here.

Well this means that you're not using the method what above.
cout << "e.what=[" << e.what() << "]" << endl;
}
try {
f();
} catch(exception & e) {

Exception caught by reference - cool, now you will get the right what
becuase you are actually using a LogicError object and not an
"exception" object copied.
OK.
All of them. What happens on the first catch is undefined.

OK. Understood.

OK. Understood.

Thanks.

- Dario
 
J

Jerry Coffin

[ ... ]
BTW, one shouldn't ever put "using namespace ...<anything>" in a
header file...

....at namespace scope. Something like this:

#include <iostream>

class X {
using namespace std;

// declarations including "ostream" instead of "std::eek:stream".
};

is perfectly reasonable. The using declaration follows scope rules, so
after the end of the declaration of 'X', the using declaration no longer
has any effect.

[ ... ]
He also should have a *virtual* destructor.

He does. He's deriving indirectly from class exception, which is
required to have a virtual destructor ($18.6.1). Since its destructor
is virtual, the destructors of all derived classes are also virtual
($12.4/7). Since he hasn't declared a dtor explicitly, it's declared
implicitly ($12.4/3). Since he hasn't defined it explicitly either, it
will be defined implicitly ($12.4/5).
It might be a better idea
to derive the class from std::exception and not from one of its
derived classes because not every implementation of the STL uses a
virtual destructor in the derived classes. In some of them, it will
work; in others, maybe not.

If the base class destructor is virtual, the destructors of _all_
derived classes are virtual. This is an absolute requirement of the
language, and does not depend on the implementation of the standard
library -- any compiler that makes a derived dtor non-virtual when the
base dtor is virtual is simply _horribly_ broken -- to the point that I
don't think it's worth discussing in the context of C++ at all.
 
R

Ron Natalie

Dario said:
} catch(exception e) {
cout << "e.what=[" << e.what() << "]" << endl;
}
When I run it on Windows (Visual Studio 6.0) I obtain:
e.what=[]
e.what=[child description]

When I run it on Windows (Visual Studio 7.1) I obtain:
e.what=[Unknown exception]
e.what=[child description]

This is to be expected. In the first try/catch you have slice your
thrown exception into an "exception". The dynamic type and
static type of e are the same. So in this case whatever implementation
defined string exception::what() prints is what you get.

In your second try/catch, you catch byreferece, therefore e still has
it's dynamic type of LogicError. The virtual what() function then
runs LogicError::what().
 
R

Ron Natalie

Bob Hairgrove said:
He also should have a *virtual* destructor.

The destructor is already virtual. std::exception's destructor is virtual so all
of it's children also have virtual destructors.
 
R

Ron Natalie

Dario said:
No-one is able to give me an help ?
I thought I did. Maybe we misunderstood your question.
The program behaves as expected. When you convert a derived class (LogicError)
a base class (exception), the object you create is the base class (exception). It's
not polymorphic, it is the base class initialzed with pieces of your derived class.

The reason it behaves differently between Linux an WIndows, is that exception::what()
returns diferent things on those machines. If you wrote:

int main() {
exception e;
cout << e.what() << endl;
}

and ran it on both machiens, you'd get the same thing you see for your first print in your
program. The standard just says the string is "implementation defined." In the case of
VC++ 6.0 it appears to just be an empty string. In VC++ 7 it is "unkonwn exception".
On Linux, it's "exception." All of these are technically correct.

When you use a reference to the base class instead, you have a polymorphic reference
to the derived object with the static type of the base class. In this case, the virtual function
what() returns your "child description."

Does this help? If not, you'll have to ask a more detailed question and we'll be glad to
explain further.

-Ron
 
B

Bob Hairgrove

The destructor is already virtual. std::exception's destructor is virtual so all
of it's children also have virtual destructors.

He is deriving his class from std::logic_error, not std::exception. We
had problems deriving from std::invalid_argument when we ported an
application from BCB5 using RogueWave STL to BCB6 using STLPort. It
wouldn't compile because STLPort's implementation of
std::invalid_argument does not have a virtual destructor (our class
did). At least, that was the error message we received. I assumed that
std::logic_error might suffer under the same symptom. Now, maybe
STLPort is broken? I don't know what the standard says, but I know
that we had problems because of the different implementations of the
STL, and that is why I gave that pieve of advice.
 
K

Kevin Goodsell

Bob said:
He is deriving his class from std::logic_error, not std::exception. We
had problems deriving from std::invalid_argument when we ported an
application from BCB5 using RogueWave STL to BCB6 using STLPort. It
wouldn't compile because STLPort's implementation of
std::invalid_argument does not have a virtual destructor (our class
did). At least, that was the error message we received. I assumed that
std::logic_error might suffer under the same symptom. Now, maybe
STLPort is broken? I don't know what the standard says,
<snip>

10.3 Virtual functions [class.virtual]

2 If a virtual member function vf is declared in a class Base and in a
class Derived, derived directly or indirectly from Base, a member
function vf with the same name and same parameter list as Base::vf is
declared, then Derived::vf is also virtual (whether or not it is so
declared)

-Kevin
 
R

Ron Natalie

Bob Hairgrove said:
He is deriving his class from std::logic_error, not std::exception.

And logic_error is dervied from exeception. As I said, std::exception
and EVERYTHING DERIVED FROM IT has a virtual destructor.
We
had problems deriving from std::invalid_argument when we ported an
application from BCB5 using RogueWave STL to BCB6 using STLPort. It
wouldn't compile because STLPort's implementation of
std::invalid_argument does not have a virtual destructor (our class
did).

Something else is wrong. All of these classes are derived from a class
with a virtual destructor. That makes their destructors virtual. If you had
problems, it was because something was seriously wrokng with your impelemntation.
 

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,983
Messages
2,570,187
Members
46,747
Latest member
jojoBizaroo

Latest Threads

Top