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! ]
|
|
0
|
|
|
|
Reply
|
tonytinker2000
|
10/12/2004 10:33:40 AM |
|
tonytinker2000@yahoo.com (Tinker) writes:
> 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.
> Is this just being anal about exceptions?
Isn't error handling a good place to be anal? ;-)
> 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
> 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 ;-)
> 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?
> 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.
> 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.
> 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.
> 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! ]
|
|
0
|
|
|
|
Reply
|
David
|
10/12/2004 10:03:13 PM
|
|
Tinker wrote:
> 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.
> 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.
> 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.
> 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!).
> 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.
> 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! ]
|
|
0
|
|
|
|
Reply
|
Alberto
|
10/12/2004 10:08:44 PM
|
|
On 12 Oct 2004 06:33:40 -0400, tonytinker2000@yahoo.com (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.
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.
>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. 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).
>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!
>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 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.
>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! ]
|
|
0
|
|
|
|
Reply
|
Tom
|
10/12/2004 10:09:52 PM
|
|
Tom Widmer <tom_usenet@hotmail.com> wrote in message
news:<mhgnm05gshcf466vkrsjh62s2di27qjjqf@4ax.com>...
> On 12 Oct 2004 06:33:40 -0400, tonytinker2000@yahoo.com (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.
> 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.
> >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.
> 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.
> >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.
> 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.
> >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).
> >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! ]
|
|
0
|
|
|
|
Reply
|
kanze
|
10/13/2004 9:34:21 PM
|
|
In article <d6652001.0410130630.389ade09@posting.google.com>,
kanze@gabi-soft.fr 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.
Look at 18.6.1:
> 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:
> 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:
> Note: an implicitly-declared copy constructor has an exception-specification
> (except.spec).
Finally, follow the except.spec link and look at 15.4/13:
> 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! ]
|
|
0
|
|
|
|
Reply
|
Howard
|
10/13/2004 11:07:15 PM
|
|
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! ]
|
|
0
|
|
|
|
Reply
|
tonytinker2000
|
10/14/2004 10:20:21 AM
|
|
> > >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?
>
> > >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?
>
> > 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?
> > 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?
> > >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! ]
|
|
0
|
|
|
|
Reply
|
tonytinker2000
|
10/14/2004 10:22:29 AM
|
|
> 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.
> 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! ]
|
|
0
|
|
|
|
Reply
|
Gerhard
|
10/14/2004 8:34:42 PM
|
|
On 13 Oct 2004 17:34:21 -0400, kanze@gabi-soft.fr wrote:
>> 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.
>> 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).
>
>> 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?
>> >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?
>> 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?
>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! ]
|
|
0
|
|
|
|
Reply
|
Tom
|
10/14/2004 8:35:25 PM
|
|
Tinker <tonytinker2000@yahoo.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?
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! ]
|
|
0
|
|
|
|
Reply
|
Chris
|
10/14/2004 8:38:28 PM
|
|
Chris Frey <cdfrey@sentex.ca> writes:
> Tinker <tonytinker2000@yahoo.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.
> 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! ]
|
|
0
|
|
|
|
Reply
|
David
|
10/14/2004 10:08:52 PM
|
|
Chris Frey wrote:
> 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! ]
|
|
0
|
|
|
|
Reply
|
Ron
|
10/14/2004 10:09:24 PM
|
|
In article <416e7db9@news.sentex.net>, Chris Frey <cdfrey@sentex.ca>
writes
>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! ]
|
|
0
|
|
|
|
Reply
|
Francis
|
10/15/2004 10:07:13 PM
|
|
Howard Hinnant <hinnant@metrowerks.com> wrote in message
news:<hinnant-51B921.18341813102004@syrcnyrdrs-02-ge0.nyroc.rr.com>...
> In article <d6652001.0410130630.389ade09@posting.google.com>,
> kanze@gabi-soft.fr 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.
[...]
> 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.
> 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! ]
|
|
0
|
|
|
|
Reply
|
kanze
|
10/15/2004 10:22:19 PM
|
|
tonytinker2000@yahoo.com (Tinker) wrote in message
news:<945eed36.0410132208.523e77f1@posting.google.com>...
> > > >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?
I don't see anything in my posting which disagrees with what they say.
> > > >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?
No. I'm saying that the standard doesn't require exceptions to have non
throwing copy constructors, and that in fact, it doesn't even require
that the exceptions in <stdexcept> have non throwing copy constructors.
Of course, if the copy constructor does throw, terminate will be called,
so there is a quality of implementation issue to be considered.
> > > 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?
That the standard makes no requirement that the copy constructor of
logic_error not throw.
> Would this cause terminate to be called?
If it throws, terminate should be called.
> If so, why would you expect such behavior?
Because the standard allows it, and in the past, I've found that some
implementations will often do just the minimum to meet the requirements.
> 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?
I think that the standard could require a lot more in this area.
> > > 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?
I think that it is clear from a quality of implementation point of view
that the copy constructor should not throw. I do not agree that the
current wording in the standard requires this.
> > > >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 the discussions at the address David Abrahams posted. I agree that
you want a maximum of information. I don't think that a formatted
string is the solution. You have a distinct type for each type of
error. You copy any additional information in in its most primitive
form, e.g. int's, etc. Or a pointer to a string literal.
When the exception is caught, you may decide to format a text message,
using the information available. When the exception is caught, but not
before.
--
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! ]
|
|
0
|
|
|
|
Reply
|
kanze
|
10/15/2004 10:42:30 PM
|
|
Gerhard Wesp <gwesp@cosy.sbg.ac.at> wrote in message
news:<ckllt5$6ju$1@esel.cosy.sbg.ac.at>...
> > 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.
> > to think that you can pass it text that will be displayed to the user.
> So, am I being foolish? (Serious question!)
You're definitly limiting the users options, or at least making it very
difficult for him to do anything other than display the message you
decided.
The whole point about exceptions is that they can't be handled locally.
If you know that all that is needed is a specific message, then just
output it there, and forget about throwing an exception.
The discussions at http://www.boost.org/more/error_handling.html are,
IMHO, very pertinant.
> I have to add perhaps that out of memory situations are not relevant
> in my code.
It's not just, nor even mainly, a question of out of memory. It's a
question about deferring as much as possible to the catch site, where
the author's presumably know the application, and what they should do
about the error for that application.
--
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! ]
|
|
0
|
|
|
|
Reply
|
kanze
|
10/15/2004 10:43:35 PM
|
|
Tom Widmer <tom_usenet@hotmail.com> wrote in message
news:<dtesm0ht63nv61gn7rbr99r3f41njg8b46@4ax.com>...
[I've treated most of the other issues you raise in answers to other
postings...]
> >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?
There was a recent thread about this. In fact, I do know of one thread
safe COW implementation of std::string -- it grabs a lock for every
access, and releases it after the access, and is too slow to be
pratical. All other implementations I've seen, which attempt to use
threads safe primitive operations, fail in certain specific situations.
(Note that this claim only concerns implementations which fully comply
to the standard. It's quite possible to implement a high quality string
thread safe string using COW if you have a reasonable interface for it,
e.g. if you allow operator[] to return a proxy, or if you make it
immutable.)
> >> >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?
Under certain (rare) cases, it frees memory twice, or uses already freed
memory.
Again, refer to the preceding thread for details. A lot has to do with
how you define thread safety. If all accesses to a string are
serialized, even if the string is never modified, then the g++
implementation works too. The "standard" requirement, however, at least
under Unix, is that serialization is only needed if at least one thread
modifies.
> >> 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?
I don't know in the case of VC++. I don't normally develop for
Windows. All I know is that I tried exhausting the memory once, and got
a pop-up, with my program suspended. I sort of supposed that this came
from the system, and not the library, but I could be wrong. I sort of
suspect that there is some way to configure this behavior -- Windows IS
sold as a server, and it is a totally ridiculous behavior for a server
with no one in front of the screen. But I don't know enough about
Windows to say.
> >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.
That is, of course, a possibility. Combined with exception handling,
the constructor for an exception could even turn this limiting off. I
was talking about the general situation. (Of course, even artificially
limiting the memory doesn't guarantee anything. Other processes might
have taken too much before you start. On the other hand, at least with
Linux, if you do allocate all you need at the beginning, and hit at
least one byte in each page, you should be safe for the rest of the
run.)
--
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! ]
|
|
0
|
|
|
|
Reply
|
kanze
|
10/15/2004 10:45:23 PM
|
|
I think I now understand your point of view. Thank you for your
clarifications.
With regard to:
>
> See the discussions at the address David Abrahams posted. I agree that
> you want a maximum of information. I don't think that a formatted
> string is the solution. You have a distinct type for each type of
> error. You copy any additional information in in its most primitive
> form, e.g. int's, etc. Or a pointer to a string literal.
Copying ints and such are great, but sometimes you may also need to
capture a dynamic string such as a file name as well.
> When the exception is caught, you may decide to format a text message,
> using the information available. When the exception is caught, but not
> before.
I couldn't agree more. This is where I started with my original post.
The article I included with my post described an exception object
implementation that guaranteed that the copy constructor didn't throw,
but it also formatted its "what" message at the time of the throw. I
was in the process of implementing my own exception object base class
that used the "format only when you need to" principle in David
Abrahams paper. I looked at the standard exception object
implementations for ideas, which lead me to the questions in my post
regarding string arguments to standard exception objects.
To handle dynamic strings in exception objects, I was thinking of
creating an exception string class using a technique similar to the
one in the paper I mentioned. This class would hold a pointer to a
fixed string and a dynamic string. When constructed with two "const
char *" parameters, it would save one as the fixed string (expecting a
string constant parameter) and attempt to allocate and copy the second
dynamic string. A "get" function would return the dynamic string if
the allocation/copy succeeded, or the fixed string if not.
This exception string class would be used as a member of an exception
object (that is actually thrown) to hold a file name from a file open
error for example. If the file name could not be copied, at least the
exception could still be caught and the "what" string could be
generated, even if some of the information was missing
("file-name-unavailable"). The exception string would be saved along
with other information such as the error code, etc. The "what" string
would dynamically create a human readable string for the
developer/user from the saved information.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
tonytinker2000
|
10/16/2004 10:12:22 AM
|
|
kanze@gabi-soft.fr wrote:
> difficult for him to do anything other than display the message you
> decided.
In fact, that's all I want. All my exceptions are lethal for the
program, it will display a message and then terminate. Note that I
don't do any GUI development, there I concede that matters may be very
different.
> If you know that all that is needed is a specific message, then just
> output it there, and forget about throwing an exception.
This is not an option for a library. The library's user might want to
write it to std::cerr, to a log file or display it in a dialog box.
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! ]
|
|
0
|
|
|
|
Reply
|
Gerhard
|
10/18/2004 6:24:43 PM
|
|
Gerhard Wesp <gwesp@cosy.sbg.ac.at> wrote in message
news:<cl03hn$elf$1@esel.cosy.sbg.ac.at>...
> kanze@gabi-soft.fr wrote:
> > difficult for him to do anything other than display the message you
> > decided.
> In fact, that's all I want. All my exceptions are lethal for the
> program, it will display a message and then terminate. Note that I
> don't do any GUI development, there I concede that matters may be very
> different.
In that case, why throw an exception. Just output the message and exit.
> > If you know that all that is needed is a specific message, then just
> > output it there, and forget about throwing an exception.
> This is not an option for a library. The library's user might want to
> write it to std::cerr, to a log file or display it in a dialog box.
The problem is that by throwing an exception, you are implying that it
is acceptable to catch it and continue.
And of course, if you don't know where he is writing it, you can hardly
know what should be written. On many applications I've worked on, log
files must be in a standard language (usually English), where as a
dialog box will be in the language chosen by the user.
--
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! ]
|
|
0
|
|
|
|
Reply
|
kanze
|
10/19/2004 6:00:58 PM
|
|
On 15 Oct 2004 18:45:23 -0400, kanze@gabi-soft.fr wrote:
>> I thought it was ok as long as all non-const calls are properly
>> serialized by the user, and such calls unshare the representation?
>
>There was a recent thread about this.
I read at least some of it.
> In fact, I do know of one thread
>safe COW implementation of std::string -- it grabs a lock for every
>access, and releases it after the access, and is too slow to be
>pratical. All other implementations I've seen, which attempt to use
>threads safe primitive operations, fail in certain specific situations.
>(Note that this claim only concerns implementations which fully comply
>to the standard. It's quite possible to implement a high quality string
>thread safe string using COW if you have a reasonable interface for it,
>e.g. if you allow operator[] to return a proxy, or if you make it
>immutable.)
If you call non-const operator[] on a std::string that is shared
between threads without locking, isn't your code is wrong regardless
of whether it actually modifies the string? Surely you must call the
const version of string::operator[] if you don't want to lock.
However, certainly an immutable string class would be a valuable
addition to C++.
>> What are the problems with it regarding conformancy and thread safety?
>
>Under certain (rare) cases, it frees memory twice, or uses already freed
>memory.
>
>Again, refer to the preceding thread for details. A lot has to do with
>how you define thread safety. If all accesses to a string are
>serialized, even if the string is never modified, then the g++
>implementation works too.
You don't need to serialize accesses, you just have to avoid calling
non-const members, don't you?
> The "standard" requirement, however, at least
>under Unix, is that serialization is only needed if at least one thread
>modifies.
The calling of a non-const member function on an object could be
defined as a modifying operation (why else is the method declared
non-const?) - I assume the "standard" you are referring to doesn't
talk about C++.
Tom
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Tom
|
10/21/2004 7:39:39 PM
|
|
Tom Widmer <tom_usenet@hotmail.com> wrote in message
news:<ht6fn09d31vjqej8a7265urhaudm39ga12@4ax.com>...
> On 15 Oct 2004 18:45:23 -0400, kanze@gabi-soft.fr wrote:
> >> I thought it was ok as long as all non-const calls are properly
> >> serialized by the user, and such calls unshare the representation?
> >There was a recent thread about this.
> I read at least some of it.
> > In fact, I do know of one thread safe COW implementation of
> >std::string -- it grabs a lock for every access, and releases it
> >after the access, and is too slow to be pratical. All other
> >implementations I've seen, which attempt to use threads safe
> >primitive operations, fail in certain specific situations. (Note
> >that this claim only concerns implementations which fully comply to
> >the standard. It's quite possible to implement a high quality string
> >thread safe string using COW if you have a reasonable interface for
> >it, e.g. if you allow operator[] to return a proxy, or if you make it
> >immutable.)
> If you call non-const operator[] on a std::string that is shared
> between threads without locking, isn't your code is wrong regardless
> of whether it actually modifies the string?
Why? The Posix standard says that as long as I don't modify an object,
I can use it from multiple threads without serializing access.
> Surely you must call the const version of string::operator[] if you
> don't want to lock.
The version of the function which is called is determined by the
compiler, according to what I could legally do with the object. Thread
safety is not based on what I could legally do, but what I actually do.
> However, certainly an immutable string class would be a valuable
> addition to C++.
> >> What are the problems with it regarding conformancy and thread
> >> safety?
> >Under certain (rare) cases, it frees memory twice, or uses already
> >freed memory.
> >Again, refer to the preceding thread for details. A lot has to do
> >with how you define thread safety. If all accesses to a string are
> >serialized, even if the string is never modified, then the g++
> >implementation works too.
> You don't need to serialize accesses, you just have to avoid calling
> non-const members, don't you?
The problem is, as I stated above, that when two functions are
overloaded on const, the compiler chooses which one gets called, not
you. And the compiler chooses according to what you could potentially
do within the rules of the language, not what you actually do according
to the rules of the application. It is a serious and unacceptable
design error for functions overloaded on const to have different
semantics if the user doesn't actually modify the object -- it violates
the whole purpose of overloading.
> > The "standard" requirement, however, at least under Unix, is that
> >serialization is only needed if at least one thread modifies.
> The calling of a non-const member function on an object could be
> defined as a modifying operation (why else is the method declared
> non-const?) - I assume the "standard" you are referring to doesn't
> talk about C++.
The Posix standard only mentions C++ indirectly (when talking about
thread cancellation, for example -- and g++ is non-conform in this as
well). It does speak about objects, however. And the notion of const
is present in C as well as in C++ -- by the same logic, if I pass a
non const-pointer to an object to a function which doesn't modify it, I
should still need a lock.
If it were a question of a non-const function in general, I would have
no objections. But when a function is overloaded on const, then it is
no longer I who choose whether to call the const or the non-const
function, according to what I intend to do, but the compiler who decides
on purely formal criteria. Thus, the two *must* have the same
semantics.
Note that in his article, Herb Sutter even argues (or at least suggests)
that when overloading on const, you should support casting away const on
the results of the const function, if the original object is not const.
While I wouldn't go that far, there is some support for that in the way
const works for built-in types. And if you are overloading an operator,
then the semantics of the overload should correspond in some way to the
semantics of the operator on a built-in type.
--
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! ]
|
|
0
|
|
|
|
Reply
|
kanze
|
10/23/2004 2:29:21 PM
|
|
tonytinker2000@yahoo.com (Tinker) writes:
> > When the exception is caught, you may decide to format a text message,
> > using the information available. When the exception is caught, but not
> > before.
>
> I couldn't agree more. This is where I started with my original post.
> The article I included with my post described an exception object
> implementation that guaranteed that the copy constructor didn't throw,
> but it also formatted its "what" message at the time of the throw. I
> was in the process of implementing my own exception object base class
> that used the "format only when you need to" principle in David
> Abrahams paper. I looked at the standard exception object
> implementations for ideas, which lead me to the questions in my post
> regarding string arguments to standard exception objects.
>
> To handle dynamic strings in exception objects, I was thinking of
> creating an exception string class using a technique similar to the
> one in the paper I mentioned. This class would hold a pointer to a
> fixed string and a dynamic string. When constructed with two "const
> char *" parameters, it would save one as the fixed string (expecting a
> string constant parameter) and attempt to allocate and copy the second
> dynamic string. A "get" function would return the dynamic string if
> the allocation/copy succeeded, or the fixed string if not.
You could do that, but it seems like a lot of work for what could be
handled nearly as well with a shared_ptr<std::string>.
--
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! ]
|
|
0
|
|
|
|
Reply
|
David
|
11/1/2004 12:22:38 PM
|
|
|
23 Replies
223 Views
(page loaded in 0.257 seconds)
Similiar Articles: catching std::exception by value - comp.lang.c++.moderated ...Should it pass a std::string to std::exception? > Where does the memory for that string ... last handler would catch any exception ... derived from this std::exception class ... std::exception::what() - comp.lang.c++.moderatedUsing an std::string would solve this issue; however, building std::strings while you ... std::exception class Reference - GCC, the GNU Compiler Collection ... Detailed ... Object (de)serialization - comp.lang.c++... 36.8: #include <map> #include <string> #include <iostream> using namespace std; class ... std::map clear throws an exception - comp.lang.c++.moderated ... Object ... Converting number to std::string ("itoa()" ) - comp.lang.c++ ...You can use streams: std::string itoa(int i ... stream<>& operator<<(std::basic_stream<>&, T t) for your own classes you can use ... std::exception::what() - comp.lang.c++ ... How to tell if an exception has currently been thrown and is being ...Using std::string in exception classes - comp.lang.c++.moderated ..... is processed, first the expression is evaluated and then the result is thrown. from std::string to std::istream? - comp.lang.c++std::exception::what() - comp.lang.c++.moderated Using an std::string would solve this issue; however, building std ... object cin is an instantiation of this class. The class ... std::map clear throws an exception - comp.lang.c++.moderated ...Converting number to std::string ("itoa()" ) - comp.lang.c++ ... converting double to long - comp.lang.c std::map clear throws an exception - comp.lang ... std::map< MyString, MyString > comparison operator? - comp.lang ...But hopefully I'm just making some simple > mistake in these specific areas.... > > - Imagine XString as a class with std::string _container as a private > member ... Undefined reference to public class method? - comp.lang.c++ ...For the following classes, just assume I ... defining it, add ' #include <string>' and ' using namespace std ... that it is worthy of an exception to the "don't use inline ... inserting class objects into maps - comp.lang.c++.moderated ...How do i insert a class into my map object along with a string as a key... ... lang.c++.moderated ... std::map clear throws an exception ... Insert into an STL queue using std ... catch rethrown exception in gtest (using google's gtest), - comp ...... of class A, defined in A.cpp. But while creating object of A, an exception is ... avoided by using strings ... in gtest (using google's gtest ... Re: catching std::exception ... Bad use of stringstream temporary? - comp.lang.c++... ss; template<class T> any2str& operator<<(T const& x) {ss<<x; return *this;} operator std::string ... C++ Truths: Use of std::bad_exception... can make ... bad pointer exception - comp.lang.c++In your code, on your system (or your "class" of ... or pointers), keeping an eye on ... and string to unsigned long conversion ... C++ Truths: Use of std::bad_exception ... cannot write std::map via ostream_iterator? - comp.lang.c++ ...... include <fstream> #include <iterator> #include <algorithm> #include <map> #include <string> using namespace std; typedef map<string,string> map_ss; class ... nitializing a static vector <> of integers (this static vectorstd::strings when ::c_str ()'d only produce the const ... signal_handlers_ to NSIG null pointers, use std ... is now way to restrict an instance of a class generated from std ... c++ - How to avoid putting std::string in exception class? - Stack ...class Win32Failure : public std::exception { public: Win32Failure( char const* win32_function ... think of a way to implement the what() override without using std::string ... exception Class (C++ Std Lib)Remarks. Specifically, this base class is the root of the standard exception classes defined in <stdexcept>. The C string value returned by what is left unspecified ... 7/13/2012 2:14:21 PM
|