C++Talk.NET Forum Index C++Talk.NET
C++ language newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Using std::string in exception classes
Goto page 1, 2  Next
 
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated)
View previous topic :: View next topic  
Author Message
Tinker
Guest





PostPosted: Tue Oct 12, 2004 10:33 am    Post subject: Using std::string in exception classes Reply with quote



I have been trying to get my head around acceptable exception
practices and have found (not surprisingly) conflicting information on
the web. At the moment, I am trying to understand what is allowed
(good) practice for the members declared in an exception object (an
object which is thrown).

I found the following reference to a particular implementation of
classes designed as the base of an exception class hierarchy:

http://www.paulgrenyer.co.uk/articles/custom_exception_classes.htm

In this link (and others I have found), the author claims that it is a
bad practice to use std::string as a member of an exception class due
to the potential that the std::string constructor could throw
(probably an allocation failure) resulting in a call to terminate (due
to a throw from within code handling a throw). The paper describes a
mechanism to avoid the use of std::string by malloc'ing a local char
array to hold an exception message and to support a static message
when the allocation fails.

Is it reasonable to guard against such a possibility in some manner as
the paper from the link above suggests? Is this just being anal about
exceptions? Or are we serious about protecting against allocation
failures when throwing?

My second question is about the following code which does not adhere
to this philosophy:
Is the following safe?

namespace std {

class logic_error : public exception {
string _what;
public:
logic_error(const string& what_arg): _what (what_arg) { }
virtual const char* what () const { return _what.c_str (); }
};

}

In other words, can an allocation failure in the copy constructor of
the _what string member result in a call to terminate?

Notice that this example is in fact one of the standard exception
class implementations that came with my compiler. If this is in fact
safe, please explain how that can be so. If this is an unsafe
practice, why is my library implemented this way? Actually, I have two
libraries from two different vendors implemented this way. (VCPP 7.1
and GCC 3.3.3)

The virtual "what()" function defined in std::exception returns a
"const char *" instead of a string, presumably to avoid the necessity
of allocation when the reason for the exception is queried. Given
that, why does the standard declare the constructors of all exception
classes which derive from std::exception to take a "const string &" as
a parameter? Based on the question above, an allocation problem in the
passed string may not cause terminate to be called, but it looks like
the standard library designers expected the derived exception class to
save a copy of the passed string as in the code above.

I know there would be a "who owns the memory" problem if the passed
parameter was a "const char*". If such an exception class could
"assume" the caller passed an immutable C string constant, a pointer
to it could be saved without the need to free it later, but this could
easily break with no compiler errors if an allocated buffer was passed
(perhaps from a std::string's c_str() function). If a "const char *"
was passed to an exception class, but the exception class saved a
copy, well, this is pretty much the same as saving a std::string copy.

I am also trying to understand when the compiler thinks it is "in
throw handling". In other words, at what point during a throw does the
compiler decide that a "new" throw constitutes a reason to call
terminate?

For example:

std::string aaa("Hello");
std::string bbb("World");
throw std::logic_error( aaa + " " + bbb + "!" );

Does the compiler consider the creation of the concatenated string to
be within the "throw handling"? In other words, if an exception is
thrown while creating the parameter to logic_error (before the
constructor is entered), will this cause terminate to be called? I
would think not, but I ask the experts.

Thanks in advance for your reply.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
David Abrahams
Guest





PostPosted: Tue Oct 12, 2004 10:03 pm    Post subject: Re: Using std::string in exception classes Reply with quote



[email]tonytinker2000 (AT) yahoo (DOT) com[/email] (Tinker) writes:

Quote:
I found the following reference to a particular implementation of
classes designed as the base of an exception class hierarchy:

http://www.paulgrenyer.co.uk/articles/custom_exception_classes.htm

In this link (and others I have found), the author claims that it is a
bad practice to use std::string as a member of an exception class due
to the potential that the std::string constructor could throw
(probably an allocation failure) resulting in a call to terminate (due
to a throw from within code handling a throw). The paper describes a
mechanism to avoid the use of std::string by malloc'ing a local char
array to hold an exception message and to support a static message
when the allocation fails.

Is it reasonable to guard against such a possibility in some manner as
the paper from the link above suggests?

Yes.

Quote:
Is this just being anal about exceptions?

Isn't error handling a good place to be anal? ;-)

Quote:
Or are we serious about protecting against allocation
failures when throwing?

If your code might ever run on a machine that can run out of memory
and throw an exception without crashing the program or thrashing
unacceptably (some systems with virtual memory can't do that), then
it's a good idea. If it _never_ will run in such an environment, you
don't need to worry about it... but doing the right thing is easy
enough that I don't see why you'd do otherwise.

Plus, the strings in exception objects are overrated in importance.
See "How Should I Design My Exception Classes?"
http://www.boost.org/more/error_handling.html

Quote:
My second question is about the following code which does not adhere
to this philosophy:

Is the following safe?

namespace std {

class logic_error : public exception {
string _what;
public:
logic_error(const string& what_arg): _what (what_arg) { }
virtual const char* what () const { return _what.c_str (); }
};

}

It's safe until you throw one ;-)

Quote:
In other words, can an allocation failure in the copy constructor of
the _what string member result in a call to terminate?

Yes, of course. Why not?

Quote:
Notice that this example is in fact one of the standard exception
class implementations that came with my compiler. If this is in fact
safe, please explain how that can be so. If this is an unsafe
practice, why is my library implemented this way? Actually, I have two
libraries from two different vendors implemented this way. (VCPP 7.1
and GCC 3.3.3)

The implementors are either ignorant of the problem or they don't
think low memory conditions are an important use case or they don't
expect anyone to use std::logic_error and don't throw it themselves.

Quote:
The virtual "what()" function defined in std::exception returns a
"const char *" instead of a string, presumably to avoid the necessity
of allocation when the reason for the exception is queried. Given
that, why does the standard declare the constructors of all exception
classes which derive from std::exception to take a "const string &" as
a parameter?

I don't think the designers of that part of the library were very
cognizant of the potential problems.

Quote:
Based on the question above, an allocation problem in the
passed string may not cause terminate to be called, but it looks like
the standard library designers expected the derived exception class to
save a copy of the passed string as in the code above.

I know there would be a "who owns the memory" problem if the passed
parameter was a "const char*". If such an exception class could
"assume" the caller passed an immutable C string constant, a pointer
to it could be saved without the need to free it later, but this could
easily break with no compiler errors if an allocated buffer was passed
(perhaps from a std::string's c_str() function). If a "const char *"
was passed to an exception class, but the exception class saved a
copy, well, this is pretty much the same as saving a std::string copy.

I am also trying to understand when the compiler thinks it is "in
throw handling". In other words, at what point during a throw does the
compiler decide that a "new" throw constitutes a reason to call
terminate?

For example:

std::string aaa("Hello");
std::string bbb("World");
throw std::logic_error( aaa + " " + bbb + "!" );

Does the compiler consider the creation of the concatenated string to
be within the "throw handling"?

Nope. That's just an argument to the throw. The throw happens after
its argument is computed, and "throw handling" continues until the
exception is caught or the program terminates.

Quote:
In other words, if an exception is thrown while creating the
parameter to logic_error (before the constructor is entered), will
this cause terminate to be called? I would think not, but I ask the
experts.

Thanks in advance for your reply.

HTH,

--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Alberto Barbati
Guest





PostPosted: Tue Oct 12, 2004 10:08 pm    Post subject: Re: Using std::string in exception classes Reply with quote



Tinker wrote:
Quote:
In this link (and others I have found), the author claims that it is a
bad practice to use std::string as a member of an exception class due
to the potential that the std::string constructor could throw
(probably an allocation failure) resulting in a call to terminate (due
to a throw from within code handling a throw).

That's not completely exact. When a throw is processed, first the
expression is evaluated and then the result is thrown. If an exception
is thrown during evaluation, terminate() is *not* called. In the case of
a string expression, if the string constructor throws an exception then
such exception is simply propagated withough terminate() being called.
For example:

struct MyFaultyException
{
MyFaultyException() { throw std::bad_alloc(); }
};

void test()
{
try
{
throw MyFaultyException();
}
catch(MyFaultyException)
{
// the exception is NOT caught here...
}
catch(std::bad_alloc)
{
// ...but here!
}
}

However, once the expression has been successfully evaluated, the
implementation may need to copy the exception object being thrown in the
catch handler's stack. If *this* copy operation throws an exception,
then terminate() is called.

Quote:
Is it reasonable to guard against such a possibility in some manner as
the paper from the link above suggests? Is this just being anal about
exceptions? Or are we serious about protecting against allocation
failures when throwing?

The answer, of course, is "it depends". On a big computer with plenty of
virtual memory, I would not expect a std::string to throw even in
near-extreme situations. As long as I don't write mission critical
software and no human life depends on my applications, I won't care
about those extreme cases. On an embedded platform the possibility is
much higher and could indeed be a problem. Your mileage may vary. You
decide.

Quote:
My second question is about the following code which does not adhere
to this philosophy:
Is the following safe?

namespace std {

class logic_error : public exception {
string _what;
public:
logic_error(const string& what_arg): _what (what_arg) { }
virtual const char* what () const { return _what.c_str (); }
};

}

In other words, can an allocation failure in the copy constructor of
the _what string member result in a call to terminate?

Potentially, yes.

Quote:
The virtual "what()" function defined in std::exception returns a
"const char *" instead of a string, presumably to avoid the necessity
of allocation when the reason for the exception is queried.

No, what() returns a const char* because it's a design choice that
std::exception should not depend on std::string. One good reason for
this is that it allows the low-level exceptions such as bad_alloc,
bad_cast, etc. to be designed without requiring any allocation at all
(see? they thought about this issue already!).

Quote:
Given
that, why does the standard declare the constructors of all exception
classes which derive from std::exception to take a "const string &" as
a parameter? Based on the question above, an allocation problem in the
passed string may not cause terminate to be called, but it looks like
the standard library designers expected the derived exception class to
save a copy of the passed string as in the code above.

logic_error and runtime_error are considered "higher-level" exceptions,
that are not meant to be thrown in those extreme situations where
allocating a string can be proibitive.

Quote:
I am also trying to understand when the compiler thinks it is "in
throw handling". In other words, at what point during a throw does the
compiler decide that a "new" throw constitutes a reason to call
terminate?

For example:

std::string aaa("Hello");
std::string bbb("World");
throw std::logic_error( aaa + " " + bbb + "!" );

Does the compiler consider the creation of the concatenated string to
be within the "throw handling"? In other words, if an exception is
thrown while creating the parameter to logic_error (before the
constructor is entered), will this cause terminate to be called? I
would think not, but I ask the experts.

As I already explained, the "throw handling" begin as soon as the throw
expression is fully evaluated, not before. The entire logic_error object
needs to be fully constructed before processing the "throw". So even if
the logic_error constructor throws, terminate() is not called. See
15.5.1 in the standard for details.

Hope it helps,

Alberto

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


Back to top
Tom Widmer
Guest





PostPosted: Tue Oct 12, 2004 10:09 pm    Post subject: Re: Using std::string in exception classes Reply with quote

On 12 Oct 2004 06:33:40 -0400, [email]tonytinker2000 (AT) yahoo (DOT) com[/email] (Tinker)
wrote:

Quote:
I have been trying to get my head around acceptable exception
practices and have found (not surprisingly) conflicting information on
the web. At the moment, I am trying to understand what is allowed
(good) practice for the members declared in an exception object (an
object which is thrown).

I found the following reference to a particular implementation of
classes designed as the base of an exception class hierarchy:

http://www.paulgrenyer.co.uk/articles/custom_exception_classes.htm

In this link (and others I have found), the author claims that it is a
bad practice to use std::string as a member of an exception class due
to the potential that the std::string constructor could throw
(probably an allocation failure) resulting in a call to terminate (due
to a throw from within code handling a throw).

Basically, exceptions ought to have non-throwing copy constructors.
Other constructors can throw without causing termination, but if they
do, the constructor's thrown exception will be the one that your throw
statement ends up throwing.

The paper describes a
Quote:
mechanism to avoid the use of std::string by malloc'ing a local char
array to hold an exception message and to support a static message
when the allocation fails.

That's one way of doing it. I think a solution using reference
counting would be better.

Quote:
Is it reasonable to guard against such a possibility in some manner as
the paper from the link above suggests? Is this just being anal about
exceptions? Or are we serious about protecting against allocation
failures when throwing?

That depends upon whether you are happy for your application to
terminate when it runs out of memory, rather than "gracefully" exit,
or even obtain memory from somewhere else.

Quote:
My second question is about the following code which does not adhere
to this philosophy:
Is the following safe?

namespace std {

class logic_error : public exception {
string _what;
public:
logic_error(const string& what_arg): _what (what_arg) { }
virtual const char* what () const { return _what.c_str (); }
};

}

In other words, can an allocation failure in the copy constructor of
the _what string member result in a call to terminate?

The copy constructors of standard exception types are meant to be
non-throwing. In theory, the implementation should be doing something
to prevent it from throwing - the obvious solution is to use a
reference counted std::string implementation (which will have a
non-throwing copy constructor).

Quote:
Notice that this example is in fact one of the standard exception
class implementations that came with my compiler. If this is in fact
safe, please explain how that can be so. If this is an unsafe
practice, why is my library implemented this way? Actually, I have two
libraries from two different vendors implemented this way. (VCPP 7.1
and GCC 3.3.3)

GCC uses a reference counted string IIRC. VC++ 7.1 probably has a bug!

Quote:
The virtual "what()" function defined in std::exception returns a
"const char *" instead of a string, presumably to avoid the necessity
of allocation when the reason for the exception is queried. Given
that, why does the standard declare the constructors of all exception
classes which derive from std::exception to take a "const string &" as
a parameter? Based on the question above, an allocation problem in the
passed string may not cause terminate to be called, but it looks like
the standard library designers expected the derived exception class to
save a copy of the passed string as in the code above.

It's probably a good time to give you this link:
http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#254
Basically, it's a known "issue" that isn't considered serious, since
the standard seems to require that standard exception copy
constructors don't throw.

Quote:
I know there would be a "who owns the memory" problem if the passed
parameter was a "const char*". If such an exception class could
"assume" the caller passed an immutable C string constant, a pointer
to it could be saved without the need to free it later, but this could
easily break with no compiler errors if an allocated buffer was passed
(perhaps from a std::string's c_str() function). If a "const char *"
was passed to an exception class, but the exception class saved a
copy, well, this is pretty much the same as saving a std::string copy.

There's no problem with the exception constructor allocating memory.
e.g.

myexception::myexception(const char* error)
:m_sharedArray(new char[strlen(error) + 1]) //may throw, but so what
{
strcpy(m_sharedArray.get(), error);
}

//now copy constructor is no-throw.

Quote:
I am also trying to understand when the compiler thinks it is "in
throw handling". In other words, at what point during a throw does the
compiler decide that a "new" throw constitutes a reason to call
terminate?

For example:

std::string aaa("Hello");
std::string bbb("World");
throw std::logic_error( aaa + " " + bbb + "!" );

Does the compiler consider the creation of the concatenated string to
be within the "throw handling"? In other words, if an exception is
thrown while creating the parameter to logic_error (before the
constructor is entered), will this cause terminate to be called? I
would think not, but I ask the experts.

The only time an exception will cause terminate to be called, is if it
is thrown during stack unwinding. That means, during initializing the
special temporary the holds the exception being thrown - this
initialization is always done using a copy constructor, but may be
elided if the thrown object is a temporary anyway. On most
implementations, even where std::string's copy constructor can throw,

throw std::logic_error("foo"); //won't call terminate

since the logic_error copy constructor is never actually invoked.

Tom

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
kanze@gabi-soft.fr
Guest





PostPosted: Wed Oct 13, 2004 9:34 pm    Post subject: Re: Using std::string in exception classes Reply with quote

Tom Widmer <tom_usenet (AT) hotmail (DOT) com> wrote

Quote:
On 12 Oct 2004 06:33:40 -0400, [email]tonytinker2000 (AT) yahoo (DOT) com[/email] (Tinker)
wrote:

I have been trying to get my head around acceptable exception
practices and have found (not surprisingly) conflicting information
on the web. At the moment, I am trying to understand what is allowed
(good) practice for the members declared in an exception object (an
object which is thrown).

I found the following reference to a particular implementation of
classes designed as the base of an exception class hierarchy:

http://www.paulgrenyer.co.uk/articles/custom_exception_classes.htm

In this link (and others I have found), the author claims that it is
a bad practice to use std::string as a member of an exception class
due to the potential that the std::string constructor could throw
(probably an allocation failure) resulting in a call to terminate
(due to a throw from within code handling a throw).

Basically, exceptions ought to have non-throwing copy constructors.

One could argue that. The standard explicitly says otherwise.

Quote:
Other constructors can throw without causing termination, but if they
do, the constructor's thrown exception will be the one that your throw
statement ends up throwing.

The paper describes a
mechanism to avoid the use of std::string by malloc'ing a local char
array to hold an exception message and to support a static message
when the allocation fails.

That's one way of doing it. I think a solution using reference
counting would be better.

Reference counting what? You have to be very careful with reference
counting in a multithreaded environment.

Quote:
Is it reasonable to guard against such a possibility in some manner
as the paper from the link above suggests? Is this just being anal
about exceptions? Or are we serious about protecting against
allocation failures when throwing?

That depends upon whether you are happy for your application to
terminate when it runs out of memory, rather than "gracefully" exit,
or even obtain memory from somewhere else.

My second question is about the following code which does not adhere
to this philosophy:
Is the following safe?

namespace std {

class logic_error : public exception {
string _what;
public:
logic_error(const string& what_arg): _what (what_arg) { }
virtual const char* what () const { return _what.c_str (); }
};

}

In other words, can an allocation failure in the copy constructor of
the _what string member result in a call to terminate?

The copy constructors of standard exception types are meant to be
non-throwing.

That's the second time you've said that. Where does it even suggest it
in the standard. The way logic_error is documented in the standard, in
fact, I would expect that its copy constructor could throw exceptions.

Quote:
In theory, the implementation should be doing something to prevent it
from throwing - the obvious solution is to use a reference counted
std::string implementation (which will have a non-throwing copy
constructor).

In practice, no one to date has succeeded in a standard conformant
reference counted implementation of std::string that is also thread
safe.

Quote:
Notice that this example is in fact one of the standard exception
class implementations that came with my compiler. If this is in fact
safe, please explain how that can be so. If this is an unsafe
practice, why is my library implemented this way? Actually, I have
two libraries from two different vendors implemented this way. (VCPP
7.1 and GCC 3.3.3)

GCC uses a reference counted string IIRC.

It did the last time I looked. It's implementation wasn't conformant
the last time I looked. And it wasn't thread safe, at least for the
platforms which interest me, according the the standard definition of
thread safety there.

Quote:
VC++ 7.1 probably has a bug!

Or simply they trade on the fact that you can't effectively recover from
memory errors anyway. At least in some configurations -- the one time I
tried exhausting memory under Windows NT, when I ran out, I got a pop-up
window telling me to kill some other applications so that the program
could continue.

For better or for worse (mostly worse, IMHO), there are a number of
systems which do not allow proper recovery, or sometimes even detection,
of out of memory conditions. Linux, obviously, and Windows NT, at least
in some configurations. Older versions of AIX -- in fact, very old
versions of Solaris (2.2 and earlier) as well. In the case of Solaris,
it wasn't intentional; in the other cases, I believe it was.

Quote:
The virtual "what()" function defined in std::exception returns a
"const char *" instead of a string, presumably to avoid the necessity
of allocation when the reason for the exception is queried. Given
that, why does the standard declare the constructors of all exception
classes which derive from std::exception to take a "const string &"
as a parameter? Based on the question above, an allocation problem in
the passed string may not cause terminate to be called, but it looks
like the standard library designers expected the derived exception
class to save a copy of the passed string as in the code above.

It's probably a good time to give you this link:
http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#254
Basically, it's a known "issue" that isn't considered serious, since
the standard seems to require that standard exception copy
constructors don't throw.

I don't see where in the issue anyone said that it wasn't considered
serious. And there was only one person who seemed to consider that
exception copy constructors don't throw, and he didn't say why (in the
DR -- I presume that he did justify himself in the meeting).

Quote:
I know there would be a "who owns the memory" problem if the passed
parameter was a "const char*".

Normally, I can't think of too many cases where it makes sense to pass
the constructor anything but a string literal. It's certainly foolish
to think that you can pass it text that will be displayed to the user.
About the best you can do is pass a token, and let the code which
catches the exception handle the display, using the token to find the
correct message, etc.

--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Howard Hinnant
Guest





PostPosted: Wed Oct 13, 2004 11:07 pm    Post subject: Re: Using std::string in exception classes Reply with quote

In article <d6652001.0410130630.389ade09 (AT) posting (DOT) google.com>,
[email]kanze (AT) gabi-soft (DOT) fr[/email] wrote:

Quote:
The copy constructors of standard exception types are meant to be
non-throwing.

That's the second time you've said that. Where does it even suggest it
in the standard. The way logic_error is documented in the standard, in
fact, I would expect that its copy constructor could throw exceptions.


Basically, it's a known "issue" that isn't considered serious, since
the standard seems to require that standard exception copy
constructors don't throw.

I don't see where in the issue anyone said that it wasn't considered
serious. And there was only one person who seemed to consider that
exception copy constructors don't throw, and he didn't say why (in the
DR -- I presume that he did justify himself in the meeting).

<nod> I did.

Look at 18.6.1:

Quote:
class exception {
public:
exception() throw();
exception(const exception&) throw();
exception& operator=(const exception&) throw();
virtual ~exception() throw();
virtual const char* what() const throw();
};

Note that the copy constructor has a throw() spec.

Now look at 19.1.1 (just as an example):

class logic_error : public exception {
public:
explicit logic_error(const string& what_arg );
};

Note an implicit copy constructor.

Now look at 17.3.2.2/1:

Quote:
For the sake of exposition, clauses lib.language.support through
lib.input.output do not describe copy constructors, assignment operators, or
(non-virtual) destructors with the same apparent semantics as those that can
be generated by default (class.ctor, class.dtor, class.copy).

I.e. logic_error's copy constructor has the semantics of the compiler
generated copy constructor.

Now look at 12.8/7:

Quote:
Note: an implicitly-declared copy constructor has an exception-specification
(except.spec).

Finally, follow the except.spec link and look at 15.4/13:

Quote:
An implicitly declared special member function (clause special) shall have an
exception-specification. If f is an implicitly declared default constructor,
copy constructor, destructor, or copy assignment operator, its implicit
exception-specification specifies the type-id T if and only if T is allowed
by the exception-specification of a function directly invoked by f's implicit
definition; f shall allow all exceptions if any function it directly invokes
allows all exceptions, and f shall allow no exceptions if every function it
directly invokes allows no exceptions.

In summary, logic_error's copy constructor has a throw() spec on it
because its base class's copy constructor has a throw() spec on it.
Therefore logic_error's copy constructor can not throw an exception. It
still may fail via unexpected() though.

I requested that 254 be reopened because I believe that the intent of an
std::exception-derived class's copy constructor is that it can not fail
(as opposed to can not throw an exception). But we have no way in C++03
to say that. I hope that we do in C++0X.

-Howard

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Tinker
Guest





PostPosted: Thu Oct 14, 2004 10:20 am    Post subject: Re: Using std::string in exception classes Reply with quote

Thank you all for your replies. This was very helpful.

Since my OP, I discovered on my own (through some experimentation)
that an exception thrown during the construction of an exception
object can in fact be handled (caught) without terminate being called.
My test was very similar to the code posted by Alberto Barbati. At
first I thought this meant "no problem, let the constructor throw". I
forgot about the copy constructor. I guess once you allocate, you're
going to have to allow a copy, and the copy constructor cannot throw
(without causing termination) as you have all said.

I have already come across the reference pointed to by David Abrahams
(Dave's paper). In fact I considered including it with my OP. I was in
the process of implementing a base class for an exception hierarchy
based on that paper and the paper in my OP when I realized the issue
with the standard exceptions (thanks to Tom Widmer for the reference
relating to my question). Now I am reworking my class based on your
replies.

I am working on a library that will be used in embedded system
development, so a proper balance of space, time, and exception safety
are important to me. I want to build an exception base class that is
easy to use (derive from) and exception safe (will never call
terminate if used properly). One of the proposals in Dave's paper is
to delay formatting of the "what" message until someone asks (calls)
for it. It seems to me that this could be the best tradeoff for my
library.

Some of the exceptions that my library will throw can be handled in
middle layer code within my library. These exceptions will be caught
and processed without the "what" function ever being called. In some
cases, these exceptions may propagate out of the library. During
development this may occur by accident (programmer error) in which
case they may propagate all the way up to main (or main thread
function) which might just log them and/or abort. (Actually, in VCPP
if you dump "what" and rethrow the original exception in debug mode,
VCPP leaves you in the debugger at the original throw point, much like
an assert would do. Anyway, I digress.) Where I was going with this is
that if an exception does not need to be displayed (when caught and
handled by a mid layer), the delayed format (which is never done)
saves some memory and cycles. I intend to use a try/catch in the
"what" function to prevent an exception from escaping as pointed out
in Dave's paper. I like this approach.

My intention is to implement "what" in my base class by calling a
"doWhat" pure virtual function that is allowed to throw. The "doWhat"
function will format a std::string member using a local ostringstream
in the base class "what" function. (See question on default
constructor for std::string below). If the "doWhat" function throws,
the "what" function will catch and set a default C string for
returning to the caller. Otherwise, the value of "c_str()" from the
saved member string is returned.

Exception objects derived from the base can simply store exception
information in the derived class (PODs and a special helper class for
storing dynamic strings) and implement the "doWhat" function to format
the message when needed. As mentioned above, if the library catches
and handles the exception, "what" will never be called and all of this
overhead is avoided.

Do you think this approach make sense? Does the formatting of the
message in the manner described above fit the spirit of the papers
mentioned in this thread?

Thanks again.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Tinker
Guest





PostPosted: Thu Oct 14, 2004 10:22 am    Post subject: Re: Using std::string in exception classes Reply with quote

Quote:
I found the following reference to a particular implementation of
classes designed as the base of an exception class hierarchy:

http://www.paulgrenyer.co.uk/articles/custom_exception_classes.htm

Until I read your response to my post, I thought I was beginning to
understand how to write an exception class hierarchy that could both
include useful information and also be "safe" by preventing terminate
from being called. I don't understand your position on this. Do you
disagree with the approach in this paper above or in the paper
suggested by David Abrahams? David's approach seems quite useful to
me. Do you disagree?

Quote:

In this link (and others I have found), the author claims that it is
a bad practice to use std::string as a member of an exception class
due to the potential that the std::string constructor could throw
(probably an allocation failure) resulting in a call to terminate
(due to a throw from within code handling a throw).

Basically, exceptions ought to have non-throwing copy constructors.

One could argue that. The standard explicitly says otherwise.

Are you saying that terminate will not be called in this case, or are
you saying that the standard simply doesn't care if you do something
that causes terminate to be called?

Quote:

The copy constructors of standard exception types are meant to be
non-throwing.

That's the second time you've said that. Where does it even suggest it
in the standard. The way logic_error is documented in the standard, in
fact, I would expect that its copy constructor could throw exceptions.

What do you mean by this? Would this cause terminate to be called? If
so, why would you expect such behavior? Do you have a different take
on how this all relates to terminate, or are you just saying that the
standard is lacking in this area?

Quote:
It's probably a good time to give you this link:
http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#254
Basically, it's a known "issue" that isn't considered serious, since
the standard seems to require that standard exception copy
constructors don't throw.

I don't see where in the issue anyone said that it wasn't considered
serious. And there was only one person who seemed to consider that
exception copy constructors don't throw, and he didn't say why (in the
DR -- I presume that he did justify himself in the meeting).

For the document under "Further discussion, from Redmond:"

The copy constructor is a more serious problem, becuase failure during
stack unwinding invokes terminate. The copy constructor must be
nothrow.

Isn't the reason clear from this? Do you disagree with his analysis?

Quote:
I know there would be a "who owns the memory" problem if the passed
parameter was a "const char*".

Normally, I can't think of too many cases where it makes sense to pass
the constructor anything but a string literal. It's certainly foolish
to think that you can pass it text that will be displayed to the user.
About the best you can do is pass a token, and let the code which
catches the exception handle the display, using the token to find the
correct message, etc.

When implementing embedded systems, often you only get a glimpse of a
problem that occurs infrequently, especially when the problem gets
into the field. In my line of work (designing telecommunications
equipment and the software control systems that run them), the
customers are not to keen on letting you in their labs to try and
reproduce such problems. This is especially true when the problem may
affect their customer's service. In these cases, sometimes all you
have to go on is the message in an assert or a logged exception that
was not caught and handled. Providing a way to include useful, context
information in these cases is vital. Such information cannot be
included in a simple string literal. This is why I was looking at the
approach described in David Abrahams paper and the paper in my
original post.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Gerhard Wesp
Guest





PostPosted: Thu Oct 14, 2004 8:34 pm    Post subject: Re: Using std::string in exception classes Reply with quote

Quote:
Normally, I can't think of too many cases where it makes sense to pass
the constructor anything but a string literal. It's certainly foolish

Passing an exception constructor a variable string? I do this
routinely, the string is the error message which will finally be
displayed by the user. Sometimes, an exception is caught, some
additional context information prepended to the error message string,
and re-throw. Gives e.g.

foo: cannot initialize module bar: couldn't open config file bar.conf: Permission denied.

Quote:
to think that you can pass it text that will be displayed to the user.

So, am I being foolish? (Serious question!)

I have to add perhaps that out of memory situations are not relevant in
my code.

Cheers
-Gerhard
--
Gerhard Wesp o o Tel.: +41 (0) 43 5347636
Bachtobelstrasse 56 | http://www.cosy.sbg.ac.at/~gwesp/
CH-8045 Zuerich _/ See homepage for email address!

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Tom Widmer
Guest





PostPosted: Thu Oct 14, 2004 8:35 pm    Post subject: Re: Using std::string in exception classes Reply with quote

On 13 Oct 2004 17:34:21 -0400, [email]kanze (AT) gabi-soft (DOT) fr[/email] wrote:

Quote:
That's one way of doing it. I think a solution using reference
counting would be better.

Reference counting what? You have to be very careful with reference
counting in a multithreaded environment.

I don't think that exception objects need be thread safe. In any case,
since the exception object's "what" string will be immutable, it's a
non-issue, since there's no COW stuff to worry about. You just need to
use atomic reference count operations to make sure it is destroyed
correctly.

Quote:
The copy constructors of standard exception types are meant to be
non-throwing.

That's the second time you've said that. Where does it even suggest it
in the standard. The way logic_error is documented in the standard, in
fact, I would expect that its copy constructor could throw exceptions.

The first comment was a general one about exceptions, that you should
avoid having them throw when copied. The above statement is about the
standard exceptions, which Howard has covered (although I agree the
standard is not clear on the issue).

Quote:

In theory, the implementation should be doing something to prevent it
from throwing - the obvious solution is to use a reference counted
std::string implementation (which will have a non-throwing copy
constructor).

In practice, no one to date has succeeded in a standard conformant
reference counted implementation of std::string that is also thread
safe.

I thought it was ok as long as all non-const calls are properly
serialized by the user, and such calls unshare the representation?

Quote:
Notice that this example is in fact one of the standard exception
class implementations that came with my compiler. If this is in fact
safe, please explain how that can be so. If this is an unsafe
practice, why is my library implemented this way? Actually, I have
two libraries from two different vendors implemented this way. (VCPP
7.1 and GCC 3.3.3)

GCC uses a reference counted string IIRC.

It did the last time I looked. It's implementation wasn't conformant
the last time I looked. And it wasn't thread safe, at least for the
platforms which interest me, according the the standard definition of
thread safety there.

What are the problems with it regarding conformancy and thread safety?

Quote:
VC++ 7.1 probably has a bug!

Or simply they trade on the fact that you can't effectively recover from
memory errors anyway. At least in some configurations -- the one time I
tried exhausting memory under Windows NT, when I ran out, I got a pop-up
window telling me to kill some other applications so that the program
could continue.

What if ::operator new has been replaced with one that only allows the
process to use a limited amount of memory?

What if virtual memory has been disabled?

Quote:
For better or for worse (mostly worse, IMHO), there are a number of
systems which do not allow proper recovery, or sometimes even detection,
of out of memory conditions. Linux, obviously, and Windows NT, at least
in some configurations. Older versions of AIX -- in fact, very old
versions of Solaris (2.2 and earlier) as well. In the case of Solaris,
it wasn't intentional; in the other cases, I believe it was.

Out of memory conditions don't come directly from the OS, instead they
come from the allocator, which can be replaced in a number of ways. A
robust application might choose to do so, to avoid the problems you
are talking about. Such an allocator might let the user decide the
maximum amount of memory they want the application to use.

Tom

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Chris Frey
Guest





PostPosted: Thu Oct 14, 2004 8:38 pm    Post subject: Re: Using std::string in exception classes Reply with quote

Tinker <tonytinker2000 (AT) yahoo (DOT) com> wrote:
Quote:
Since my OP, I discovered on my own (through some experimentation)
that an exception thrown during the construction of an exception
object can in fact be handled (caught) without terminate being called.
My test was very similar to the code posted by Alberto Barbati. At
first I thought this meant "no problem, let the constructor throw". I
forgot about the copy constructor. I guess once you allocate, you're
going to have to allow a copy, and the copy constructor cannot throw
(without causing termination) as you have all said.

Question for the gurus:

Is there something wrong with catching by reference?

try {
throw SomeException();
}
catch(const SomeException &e) {
// do something
}

This would eliminate any copy constructor needed, as far as I can see.

- Chris


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


Back to top
David Abrahams
Guest





PostPosted: Thu Oct 14, 2004 10:08 pm    Post subject: Re: Using std::string in exception classes Reply with quote

Chris Frey <cdfrey (AT) sentex (DOT) ca> writes:

Quote:
Tinker <tonytinker2000 (AT) yahoo (DOT) com> wrote:
Since my OP, I discovered on my own (through some experimentation)
that an exception thrown during the construction of an exception
object can in fact be handled (caught) without terminate being called.
My test was very similar to the code posted by Alberto Barbati. At
first I thought this meant "no problem, let the constructor throw". I
forgot about the copy constructor. I guess once you allocate, you're
going to have to allow a copy, and the copy constructor cannot throw
(without causing termination) as you have all said.

Question for the gurus:

Is there something wrong with catching by reference?

No, it's preferred.

Quote:
try {
throw SomeException();
}
catch(const SomeException &e) {
// do something
}

This would eliminate any copy constructor needed, as far as I can see.

A copy is needed by the language at the point of the throw,
regardless. The compiler is free not to use the copy ctor, but it is
required to be accessible, and in portable code it may be called.

--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


Back to top
Ron Natalie
Guest





PostPosted: Thu Oct 14, 2004 10:09 pm    Post subject: Re: Using std::string in exception classes Reply with quote

Chris Frey wrote:

Quote:
Question for the gurus:

Is there something wrong with catching by reference?

try {
throw SomeException();
}
catch(const SomeException &e) {
// do something
}

This would eliminate any copy constructor needed, as far as I can see.

You can catch by reference, but that doesn't obviate the need for
the copy constructor. The operand of the throw ceases to be at the
end of the throw statement. The thrown object is copied to some
undisclosed location during the throw. Even if the compiler optimizes
away the actual temporary shown above (by creating the SomeException()
directly in the undisclosed location), the copy constructor access is
still required.

All catching by reference does is avoid a SECOND copy at the beginning
of the catch block.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Francis Glassborow
Guest





PostPosted: Fri Oct 15, 2004 10:07 pm    Post subject: Re: Using std::string in exception classes Reply with quote

In article <416e7db9 (AT) news (DOT) sentex.net>, Chris Frey <cdfrey (AT) sentex (DOT) ca>
writes
Quote:
Question for the gurus:

Is there something wrong with catching by reference?

try {
throw SomeException();
}
catch(const SomeException &e) {
// do something
}

This would eliminate any copy constructor needed, as far as I can see.

No, throwing is a special case where a copy ctor must always be
available even if you catch by reference. To understand this you need to
understand that at the point of throw the exception object must be
copied to a 'safe' location.


--
Francis Glassborow ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
kanze@gabi-soft.fr
Guest





PostPosted: Fri Oct 15, 2004 10:22 pm    Post subject: Re: Using std::string in exception classes Reply with quote

Howard Hinnant <hinnant (AT) metrowerks (DOT) com> wrote

Quote:
In article <d6652001.0410130630.389ade09 (AT) posting (DOT) google.com>,
[email]kanze (AT) gabi-soft (DOT) fr[/email] wrote:

The copy constructors of standard exception types are meant to be
non-throwing.

That's the second time you've said that. Where does it even suggest
it in the standard. The way logic_error is documented in the
standard, in fact, I would expect that its copy constructor could
throw exceptions.

Basically, it's a known "issue" that isn't considered serious,
since the standard seems to require that standard exception copy
constructors don't throw.

I don't see where in the issue anyone said that it wasn't considered
serious. And there was only one person who seemed to consider that
exception copy constructors don't throw, and he didn't say why (in
the DR -- I presume that he did justify himself in the meeting).

nod> I did.

I expected no less.

[...]
Quote:
In summary, logic_error's copy constructor has a throw() spec on it
because its base class's copy constructor has a throw() spec on it.

And because the standard mentions no other members explicitly.

It's an interesting idea, but I'm not sure that the logic holds. I
think that the standard does allow an implementation to add private
members of class type to logic_error (or any standard class). In which
case, the exception specification of the copy constructor for
logic_error would include any exceptions which could be thrown by the
copy constructor of that member.

That's for what the standard actually says. I agree with you that the
nothrow exception specification on the copy constructor is a very strong
indication of intent, regardless of how the actual text can be
interpreted. I also agree that it is a good idea, and that
independantly of the standard, a quality implementation will not throw.

Quote:
Therefore logic_error's copy constructor can not throw an exception.
It still may fail via unexpected() though.

And it may exceed resource limits, and cause undefined behavior:-).

In practice, it shouldn't be too difficult for an implementation to make
the standard exceptions no throw. After all, the implementation already
needs some sort of separately managed dynamic memory into which it can
copy the exception anyway. It shouldn't be that difficult to
instantiate a basic_string so that it also acquires its memory from that
area. And of course, when the memory in that area is exhausted,
resource limits are exceeded, and the implementation is off the hook.
(Practically, I don't see much else it could do but abort.)

It's a bit more difficult for a user exception, since the user has no
knowledge of this special area. Unless, of course, the user exception
derives from a standard exception, and packs anything that needs dynamic
allocation into the string.

--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Display posts from previous:   
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated) All times are GMT
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.