Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

C++ Error Reporting - State of the Art?

386 views
Skip to first unread message

Andy Glew

unread,
Dec 14, 1999, 3:00:00 AM12/14/99
to
Brief
=====

I started out with a few questions about what is standard practice for
error reporting in C++, and about tradeoffs and interactions between
error messages and exception handling. As is not unusual, the process
of writing this questioning post helped show me the shape of the best
solution.

Nevertheless, my questions about whether there are some reasonable
standards in this area this stand. If there are existing standards,
I'd like to follow them.

What I want is an error reporting mechanism that

(a) allows error exceptions to be caught, but which can nonetheless
guarantee that an error message will be printed, instead of
silently abort()'ing and dumping core on an uncaught exception.

options: exception::what(), global with destructor, set_terminate

(b) layered error messages: some place to accumulate error messages
from the throw-site, as well as higher up the call tree

(c) debuggability: an error reporting mechanism that allows useful
core dumps to be optionally taken in case of error. Unfortunately,
I know of no cheap way to get both layered error messages and
useful core dumps.

(d) user ability to select throwing or aborting or exiting on an error

(e) error message formatting

(f) error message boilerplate such as __FILE__ or __LINE__

At the end I decribe a strawman that looks like

int main() {
try { level1(); }
catch( ... ) {
OERRSTREAM << "uncaught exception\n";
exit(1);
}
}
void level1() {
...
try{ level2();}
catch(...) {
OERRSTREAM << "error in level1 doing blah\n";
throw_error(1);
}
}
void level2() {
if( ...error... ) {
OERRSTREAM << "error in level2 doing blip\n";
throw_error(2);
}
}

I'd appreciate email responses, in addition to replies
posted to the newsgroup; I will be out of news contact for
a week, but will still have email.


Detail
======

Error Messages and/or Exceptions?
---------------------------------

The basic issue is, in a library routine, when an error is detected,
how do you report it? Do you throw an exception, hoping that there is
a handler somewhere up above, or do you print an error message and
die?

Of course, the right C++ answer is to throw an exception with
meaningful information in the exception object. Someone in the calling
hierarchy will catch the exception, inspect the exception object, and
print a more meaningful error message, or perhaps even continue
operating.

Unfortunately, frequently this does not occur. For example, there may
not be an exception handler anywhere in the hierarchy. On the C++
systems I have used, in such cases the program then simply aborts,
without any meaningful error message. This loses compared to having
the library routine print an error message out itself:

void libfoo() {
if( error ) {
cerr << "error in libfoo" << endl;
abort();
}
}

Summing this up: If exception handling is done properly, with an
exception handler for every possible exception type, it may lead to
better, more meaningful, error messages. However, for ad-hoc programs
where there may be no exception handling, using exception handling to
report errors may result in *LESS* meaningful error messages than
old-fashioned C-style "print an error message and die".

Q: what I am looking for is a *standard* way of getting both: using
exceptions, for all of the good things they bring (some of which I
will describe below), but with a way of guaranteeing that an error
message is produced if the exception is not caught.

Now, I am looking for a *standard* way. I can fairly easily
cons up something off the top of my head.

For example, below I place an error message in a global. I arrange
for main to catch everything, and exit "cleanly" so that the
destructor of the global is called, which prints the error message.
Therefore, if the exception is not explicitly handled, an error
message will be printed. If the exception is handled, it can arrange
to cancel the pending error message.

#include <string>

class error_report_c {
string data;
public:
error_report_c& operator=( string s ) {
this->data = s;
return *this;
}
bool operator==(string s) {
return this->data == s;
}
bool operator!=(string s) {
return !(*this == s);
}
operator string() { return this->data; }
~error_report_c() {
if( *this != "" ) {
cerr << "error report: " << this->data << endl;
}
}
} global_error_report;

void foo() {
global_error_report = string("foo");
throw(string("foo"));
}

int
main()
{
try {
foo();
}
catch(...) {
cerr << "error: exception: " << endl;
exit(1);
}
}

(The above code has some problems: for example, I'm not sure how to
guarantee that cerr has not been destroyed before the
global_error_report. Also, if the error message is uncaught, the stack
will have unwound all of the way to main(), preventing a useful core
dump. See [Debuggability and Core Dumps], below.)

What I am hoping is that there is some sort of standard way of
accomplishing the above. I'll adhere to standards if I know about
them. Of course, I don't mean an ANSI C++ standard; just some way
more common than others, that has a reasonably good chance of being
compatible with other libraries, and with applications that may use my
code.

I can imagine three possible ways of always getting an error message
if the exception is uncaught:

(1) Deriving all exception objects from a base class like exception,
and printing their what() messages in a catch(exception) clause
in main().

(2) The global destructor trick, described above.

(3) set_terminate.

I describe these below, in [Which is Better?]


Layered Error Messages
----------------------

"Layered Error Messages" refers to providing layers of error messages,
each providing more context. For example, the place where the actual
error was detected might report:

"memory allocator out of space: 2.3GB heap, 400MB stack, 600MB bss"

Some parent in the call stack might provide additional information:

"allocating memory to hold the database index"

and some parent still higher up might report

"trying to make a copy of your Inbox mail folder".

Each layer provides more context. Some systems print all of the
layered error messages out at once; others display a dialog box with
the topmost, most abstract, error message (above, something like
"error trying to make a copy of your Inbox mail folder), with a "more
detail" button that reveals more precise, and less user friendly,
error messages.

I like layered error messages. They can make locating problems really
easy, since they report many levels of abstraction, not just something
meaningful only to the innermost routine.

One of the main reasons that I am writing this long post is to try to
find out how best to implement layered error messages in C++.

For example, one could implement layered error messages by throwing a
stack of strings, or perhaps even just a string, as an exception
object:

int main() {
try { level1(); }
catch( string s ) {
cerr << s;
exit(1);
}
}
void level1() {
...
try{ level2();}
catch( string s ) {
throw(s + "error in level1 doing blah\n");
}
}
void level2() {
if( ...error... ) {
throw("error in level2 doing blip\n");
}
}

One might try to do the same thing with standard exception objects,
adding stuff to their what() strings. Is this possible?

One could put the layered error message in a global
error_message_stack, and force it printing either via its destructor
or via set_terminate, as described above.


Or, what can implement layered error messages without using exceptions
at all, an approach that has significant advantages for debuggability,
as described below.


My question, as usual, is: is there anything vaguely resembling a
state of the art or best known method for layered error reporting in
C++?


Standard Exception Object Methods
---------------------------------

I was going to ask what de-facto standards there are for exception
objects and their methods: for example, is there a standard way of
generating a printable message from any exception object?

Perusal of the ANSI C++ standard reveals that clas "exception" can
almost be recommended as a universal base class (like in SmallTalk)
for exception objects, with a virtual const char* what(). For
<stdexcept> errors what() returns a user defined error string,
although for more basic errors such as bad_alloc what() seems to be
implementation defined - i.e. useless, for strictly standards
conforming code. {It would be more useful if they would say that
what() must return a null-terminated string, intended to be indicative
of the error. As it is now, what() for some of these exceptions seems
to be in nasal demons territory.} However, I'm not so worried
about being strictly standards cinforming, so I'll take the hint
and define what() methods for any exception objects, and save the
error messages away inside.

While we're here: I'm assuming that it is safe to derive
user classes from class exception?

Which is Better?
----------------

Another question: which is better, i.e. more likely to be useful with
a wide variety of code and applications, as an almost universal way of
providing error messages for all exceptions:

(1) Having main() catch all exceptions derived from base class exception,
and printing their what(),

e.g.
int main() {
try {
... do stuff ...
}
catch( exception& e ) {
cerr << "uncaught exception " << e.what() << endl;
exit(1);
}
catch(...) {
cerr << "unknown exception" << endl;
};
}

This is only useful if some large fraction of all exceptions
are derived from base class exception, and have meaningful
what() messages.

(2) Printing out the error message via destruction of a global,
as described above?

This has the possible advantage of being independent of
a class hierarchy for

(3) Or, is it simply best to force the printing of such desired
error messages via code called in the uncaught exceptions
handler, set by set_terminate()?

Or, is some combination of these advisable - perhaps
methods (1) and (3)

set_terminate() protocol
------------------------

Is there a standard protocol for using set_terminate() to install a
handler for uncaught exceptions?

For example, one could save the old value of the handler, returned by
set_terminate(), and then have your handler call that after it does
its work.


Debuggability and Core Dumps
----------------------------

In most C++ systems I have used, uncaught exceptions abort at throw
site - that's good, I can use a debugger on the core file, and get a
backtrace all of the way to the throw site. Some, however, wind the
stack back up, so that even if a core dump is made, it doesn't show
where the problem occurred. That's bad.

Stroustrup says [C++PL3, p. 381]: "It is implementation defined
whether destructors are invoked when a program is terminated because
of an uncaught exception. On some systems it is essential that the
destructors are not called so that the program can be resumed from
the debugger. On other systems, it is architecturally vlose to
impossible not to invoke the destructors when searching for a
handler."

Well, I guess that says it all. :-( It probably means that my next
question must be answered in the negative:

So much for uncaught exceptions. However, in the "layered error
messages" model, sometimes you want to print all of the layers of
error messages, but still abort and take a core dump that you can
inspect in a debugger.

Unfortunately, if you use exceptions to implement layered error
messages, then your stack is unwound, and the core dump is useless.
:-( :-(

I *think* that this means that, if I want to implement layered error
messages and still get useful core dumps, that I will need to create
the error message layers a priori. I.e. instead of

int main() {
try{ level1();}
catch(...) {
cerr << "level1 failed doing blah";
throw;
}
}
void level1() {
try{ level2();}
catch(...) {
cerr << "level2 failed doing blip";
throw;
}
}
...

I could do

stack<string> context_for_error;

int main() {
context_for_error.push("level1 failed doing blah");
level1();
context_for_error.pop();
}
void level1() {
context_for_error.push("level2 failed doing blah");
level1();
context_for_error.pop();
}
...
void levelN() {
if( error ) {
cerr << "Error inside LevelN with the following context:\n";
for( stack<string>::iterator i = context_for_error.begin();
i != context_for_error.end();
i++ ) {
cerr << *i << endl;
}
abort();
}

This approach gives me both core dumps and layered error messages.
But, it costs run-time -- you pay for it even if no errors occur.

Sigh. On other, non-C++, exception handling schemes, such as VMS's,
you can get the best of both worlds, because you have the additional
exception handling option of returning to the point where the
exception was thrown. That seems rather heavyweight, but it is the
only way I can see to get layered error messages, paying for them only
in case of an error, and still getting useful core dumps without
stack unwinding. If there are any other techniques to accomplish the
same thing, I sure would like to hear about them.

exit_or_abort_or_throw
----------------------

Q: are there any standard ways of hooking different "error
hook" strategies in place?

For example, when an error is detected, you may want to
a) exit without dropping a core dump
b) abort with a core dump
c) throw an exception (e.g. for layered error messages)

In some situations I go further: some of my applications
run for weeks, but also tend to have rather big core dumps
that caus
e discomfort if they are saved. Therefore, the
"error hook" for these does something like:
1) sends me mail describing the error
(layered error messages help here)
2) spins for a few days, to give me a chance
to decide if I want to debug it,
or just let it exit without a core dump.

Naturally enough, I call this "wait_to_exit_or_abort()".

But, explicitly naming it loses in case one of the library routines
*I* call explicitly calls exit() or abort(). (Or, for that matter,
throws an error.) I therefore need to hook onto "standard" exit() and
abort(). I've done this in the past by linker trickery - providing my
own exit() and abort() routines, that call the appropriate raw syscalls
like _exit(). Or, when I have the library source code, via preprocessor
trickery: #define exit exit_or_abort, etc.

This sucks. I'd like a better, more standard way, if it exists.

Note above that sometimes the libraries I call use exit(), sometimes
abort(), {sometimes they send KILL signals to themselves, gack!,} and
sometimes they throw exceptions. This last puts me in the funny
position of having to *AVOID* exception handling: I can catch an
uncaught exception via set_terminate(), and cause a core dump without
stack rewinding for debuggability.

Damn! All I want is one clean error reporting mechanism...


Error Message Formatting
------------------------

Unmentioned above is the issue of formatting error messages. I am old
enough to remember how bad UNIX's error messages could be, formed of
static strings, e.g. "Disk full", as opposed to "Disk full, writing
file /local/tmp/ctm989711" which at least tells you *which* disk is
full, and hints that it might be a temporary file, e.g. in a
preprocessor. {Of course, I'd like to even more info: "Disk full,
writing file /local/tmp/ctm989711, cpp preprocessor temporary file".}

In the bad old days of C, many of us had printf-like routines such as
panicf("disk full, writing file %s, in compilation phase %s",
temp_file_name, compiler_phase_name);

Other popular incarnations include eprintf(char* fmt,...), exitf(int
errcode,char* fmt, ...), some of which just fprintf'ed to stderr,
others of which called abort, exit with a specified error code,
panic, syslog, etc.

The biggest problem with all of these in C was the great variety.
E.g. I am now working with some legacy C code from different sources
that calls all of the above. I haven't wanted to edit them all to
have a uniform strategy, but, I can tell you, it gets pretty
confusing,

Q: is there a "best practice" equivalent for C++?

I suspect that it is not just
cerr << "error " << additional_info << endl;
exit(1);
or
istringstream error_msg;
error_msg << "error " << additional_info << endl;
throw(error_msg);
since I have seen some popular books (Strosstrup? Meyer?)
use things like "error(string)".

Again: is there something approaching a standard?

Similarly, error messages often have boiler plate. For example, it is
often convenient to automatically include __FILE__ and __LINE__ in an
error message. Usually, this has to be done by a C preprocessor
#define macro, as in assert().

{Aside: Beman Dawes of the BOOST mailing list has argued that people
using modern programming environments don't need such file/linenumber
information, since their programming environment provide it for them,
when he was trying to justify a non-preprocessor aproach to
assert(). My response is that I use G++ under EMACS, so my programming
environment is not advanced enough, and that I frequently want such
file/number info in error messages when I am not running under a
debugger. Layered error messages hide the annoying details from
tender users.}

The boiler plate changes. E.g. I want
file.cc:15 error in this file at this line
for ease of use with GNU EMACS' compilation modes;
assert on my LINUX system produces
program file.cc:15 error in this program in this file at this line
which is useful for errors from backgrounded programs; still others
want thread ids placed in the error message.

It is convenient to have a hook to define such boilerplate.

Q: again, is there anything remotely resembling a standard practice
here for C++?


Strawman
========

Here's a strawman that might solve many of these issues with
one mechanism:

** define an ostream derived class, call it oerrstream,
that has the following properties:

a) it can be set to output directly to cerr
b) it can be set to accumulate data sent to it
and to print this out when finally destroyed.

Use standard C++ streams formatting
on oerrstream.

** Implement layered error messages using the exception technique
(I can't stomach the overhead of a priori context)
(give up on getting both layered error messages and debuggability)

Have each layer output to oerrstream, which accumulates the
layered error messages.

Throw some inconsequential exception object.

** Wrap oerrstream in a macro
#ifndef OERRSTREAM
#define OERRSTREAM (oerrstream << __FILE__ << ":" << __LINE__ << " ")
#endif
to get some of the boilerplate from the preprocessor.
Make this macro overrideable.

Possibly make OERRSTREAM into a stream that prints standard
indentation, includig this boilerplate, on every line.

** #define throw_error(e)
as throw(e),
or exit(e),
or abort(),
or wait_to_abort()
or ... as desired.

Therefore, canonical error reporting might look like

int main() {
try { level1(); }
catch( ... ) {
OERRSTREAM << "uncaught exception\n";
exit(1);
}
}
void level1() {
...
try{ level2();}
catch(...) {
OERRSTREAM << "error in level1 doing blah\n";
throw_error(1);
}
}
void level2() {
if( ...error... ) {
OERRSTREAM << "error in level2 doing blip\n";
throw_error(2);
}
}

If an exception is actually handled, and execution proceeds,
oerrstream would have to be reset and all pending messages erased.


Conclusion
==========

Although I've arrived at my own strawman, I'd still like to know if
there are any other pseudo-standard error reporting mechanisms that
can be used in libraries.

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]


Andy Glew

unread,
Dec 15, 1999, 3:00:00 AM12/15/99
to
By the way, I should add the following:

I am aware of issues such as use of classes that
may perform dynamic memory allocation in
exception handlers. E.g. if you have just exhausted
memory in new, using a string to report an error
may not be a good idea.

Side note: are regular ofstreams, or cout/cerr safe
for memory exhausted exceptions in this regard?
I would imagine that an istringstream that expands
is not, but that an istrstream that writes into a char
buffer of fixed, preallocated, size is.

My usual question: what are common idioms, standard
conventions, in this regard?

Allow me to note that, while I am sympathetic to this
issue, I do not want to let the rather infrequent issue
of memory exhaustion hinder good error reporting for
other cases. Although even here, I am aware of the
problems that occur with nested exceptions: e.g. you
might not have run out of memory when a first error
is reported via a nested exception, but if you run out
of memory while handling the first exception, death.

Hendrik Schober

unread,
Dec 15, 1999, 3:00:00 AM12/15/99
to

Andy Glew <gl...@cs.wisc.edu> wrote:
> [ a really _huge_ amount of text ;-) ]

Hi Andy,

First: your article is much to long for me to go through it and
addressing all your points individually. Still: For I'm involved
with exceptions and error handling for quite a few years now I
want to summarize some of my ideas and solutions to the problems
you mentioned.
Over the time I have collected a few technologies to develop a
quite complete error handling scheme. Here's a summary of what
I came up with:

I do use exceptions exclusively for reporting errors, but I also
have a logging mechanism that allows to log usefull debbuging
information which uses some of the same types/ideas/techonolies
as my exceptions do.

I do use my own exception base class. It is not derived from
'std::excepion' because that doesn't help me to achieve what I
need. I really like sophisticated exception class hierarchies,
using MI, for that allows many ways to catch errors (by using
different base classes). But regarding 'std::excepion', if you
have a class like 'MyOutOfRange', and you would want to be able
to catch this as 'std::out_of_range', this is impossible if it
is also derived from 'std::excepion' via your own exception base
class. The reason is that all exceptions in namespace 'std'
derive non-virtual from 'std::excepion', so that when using MI
it isn't a so called "unambiguous public base class" anymore. I
don't see any good reason for deriving non-virtual from an
exception base class, but that's the way it is.

My exception base class automatically registers/unregisters
itself with an exception registry (which is just a vector of
pointers to exception objects). One can walk through the entries
of the registry and thus get all the information from all currently
living exception objects. This could for example be accessed
from exception handlers in 'main()' or from a terminate handler
(which takes care of throwing dtors of global objects that are
destructed after 'main()'). You can also register observers
that will be informed when exceptions are instanciated/destructed.
(A popular one plugs into my logging system to log this, see
below.)

Each exception object contains what I call an error origin
information, an error message, and a call stack information
object. Only the latter might be empty.

Error origins store the results of '__FILE__', '__LINE__',
'__DATE__', and '__TIME__' (the latter for a simple version
information). Providing, passing and declaring these arguments
is done using macros that bundle them. (I used to have a switch
that would turn off the '__DATE__' and '__TIME__' macros. I
removed it only recently because I never used it.)

Error messages are kept in a class. It stores either a message
string or a message ID from which to build the string. The string
can name parameters using a special syntax. These can be passed
or added to the message object and will be inserted at the
approriate places when you retrieve the string. Error messages
are parameters themself and can thus be used as parameters for
other messages. (This addresses the layering problem. You would
catch an exception and pass its message as parameter to another
message which in turn is passed as arg to a new exception object.
I do that very frequently.)

Throwing an exception is done like this:

if( (result=func()) != kResultOK )
throw XSomeNastyError( X_ORIGIN_HERE,
ErrMsg(IDERR_SOME_NASTY_ERRROR)
+=ErrPar("nfuncResult",result)
+=ErrPar("sfuncResult",getString(result)) );

Assuming that 'IDERR_SOME_NASTY_ERRROR' expands to the string
"Some nasty error occured. 'func()' returned @(nfuncresult)
(\"@(sfuncresult)\")."
I can produce an error message like
>> Some nasty error occured. 'func()' returned 5 ("Cannot open"). <<
For debugging/support this can be accompanied by
>> "myfile.cpp" #275 (99/12/15, 11:18) <<
If you would wrap your code with macros like this
X_STACKSTRACE_BEGIN
// ...
mightThrow();
// ...
X_STACKSTRACE_END
they will add call stack information to the exception object which
include the source file and the lines of two macros as well as the
compilation date/time. (BTW, there's 'DEBUG' versions for code that
can't effort additional 'try'/'catch' statements) This way developers
can even get information about the call stack:
>> "myfile1.cpp" #275 (99/12/15, 11:18)
"myfile3.cpp" #815 (99/12/15, 11:19)
"myfile34.cpp" #72 (99/12/12, 23:58)
"myfile19.cpp" #65 (99/10/27, 14:32) <<
The stack trace macros also catch 'std::exception's and throw my
own exceptions instead, passing the information obtained from
the 'what()' member of 'std::exception'. (Also I frequently add
catch clauses for proprietary exception classes.) Therfor I usually
write catch clauses only for my exceptions (and 'catch(...)', of
course.

Finally I do have a rather sophisticated logging system which
mainly uses error origins and error messages to produce logging
output. Logging can be turned on/off at run time. Again I do
have calls to the log system that stay in release code and those
that don't. You can pass a function object to the logging system
that will produce a message prefix for each message. this way
you can sneak additional information into your logging (like
time, thread IDs etc.).
The logging system doesn't know about log files, 'stderr' and
the like. Instead it invokes log targets which you have to pass
to it. These the write into a file or to your favorite error
console. One that still remains to be implemented would write
into a circular buffer and flush this buffer only in case of a
real error. Another great target would one that automatically
mails fatal errors (did I say already that error messages have
priorities like "log", "error", "fatal" etc.?) to our support
e-mail account (probably along with the last n log messages
as output by the aforementioned circular buffer).

Comments on this would be apreciated.

Schobi

Hendrik Schober

unread,
Dec 15, 1999, 3:00:00 AM12/15/99
to
Andy Glew <gl...@cs.wisc.edu> wrote:
> By the way, I should add the following:
> [...]

> My usual question: what are common idioms, standard
> conventions, in this regard?

I've seen a static 'std::bad_alloc' object to be thrown
from the standard new handler.

> Allow me to note that, while I am sympathetic to this
> issue, I do not want to let the rather infrequent issue
> of memory exhaustion hinder good error reporting for
> other cases. Although even here, I am aware of the
> problems that occur with nested exceptions: e.g. you
> might not have run out of memory when a first error
> is reported via a nested exception, but if you run out
> of memory while handling the first exception, death.

I pass most of my information as message IDs (i.e. numbers,
see my other post) which are expanded into strings only
at the catcher's side. I simply assume that after unwinding
the stack there enough room to keep a few strings for error
messages, which IMO is a plausible assumption.
If you don't have even this, there's no point in trying to
prevent abortion since to me aborting seems to be the
best you can do in this case anyway.
Besides most of the exceptions thrown in my applications
report violations of class invariants or pre/postconditions
(I do have an assertion exception) or failure of some third
party code (OS file system errors, input file parsing
errors etc.). These usually don't interfer with error
message generation. (Exhaustion of UI ressources would do,
though. But I guess a good UI should always have some spare
ressources to show an error message anyway.)

Schobi

Todd Greer

unread,
Dec 15, 1999, 3:00:00 AM12/15/99
to
"Andy Glew" <gl...@cs.wisc.edu> writes:

> I am aware of issues such as use of classes that
> may perform dynamic memory allocation in
> exception handlers. E.g. if you have just exhausted
> memory in new, using a string to report an error
> may not be a good idea.

OTOH, it might be OK, since the stack has unwound, and you may have
freed some memory. In a program I'm working on, when the catch is
reached, memory is probably no longer a problem.

Of course, you can run out of memory at any time, so my code tries to
make a string to report the error, but if this fails, it will make a
zero-length string, and the user just won't get a nice error message.
It's not a std::string, and for related reasons, I can't just hold
onto a static string for such an occasion.

--
Todd Greer <tgr...@acm.org>

J.Barfurth

unread,
Dec 16, 1999, 3:00:00 AM12/16/99
to

Andy Glew <gl...@cs.wisc.edu> schrieb in im Newsbeitrag:
835up0$g...@spool.cs.wisc.edu...

> By the way, I should add the following:

I didn't manage to read the original post yet, but maybe this one can be
treated independently.

> I am aware of issues such as use of classes that
> may perform dynamic memory allocation in
> exception handlers. E.g. if you have just exhausted
> memory in new, using a string to report an error
> may not be a good idea.

I think this concern is given too much attention - see below.

> Side note: are regular ofstreams, or cout/cerr safe
> for memory exhausted exceptions in this regard?
> I would imagine that an istringstream that expands
> is not, but that an istrstream that writes into a char
> buffer of fixed, preallocated, size is.
>

> My usual question: what are common idioms, standard
> conventions, in this regard?

There are actually different situations in which memory allocations can
fail:
(1) You request a large amount of memory, but can't get it because of
the size requested.
(2) Some program has exhausted memory, so even requests for
comparatively small amounts of memory can fail. The culprit might be
your own program, but it might be any other program as well if you are
running on a multitasking OS.

Case (2) is difficult to deal with, whether in standardized ways or not.
On many OSs the whole machine will be unresponsive and OS behaviour may
be wierd long before that. In such situations there may be no option
other than to try to notify the user or log the error. After that: if
you can't exit regularly, your program might just as well terminate.
The best one can do usually is to just try - as you noticed even
outputting an error might not work.

If you have allocated large amounts of memory yourself, you might try to
discard some of it and restart the operations that used it. Whether that
is acceptable depends on the task at hand.

More generally: How and in which ways such situations can be handled
strongly depends on the platform you work on and the purpose and
requirements of your application.

If the program is required to behave in specified ways in this
situation, even that requirement can only be stated in
platform-dependant ways (IMHO) - at least with the necessary precision.
For such cases you may need to have preallocated buffers to work on etc.
If some library functions also can't acquire the memory they need you
will be stuck.
If another program is grabbing all the memory it can get, free a reserve
buffer won't help you.

IOW: Unless you absolutely must - don't sacrifice your general, stable,
reusable error handling scheme (supposing you have designed one) just
for this case.

Case (1) is a different story: In that case you haven't "exhausted
memory in new". Instead new failed and did not reserve that memory. In
this case your ability to handle the error is not restricted by the
non-availability of memory. Your usual scheme should work.

In most cases the error should be dealt with locally: You should have a
fallback algorithm that needs less memory (maybe even from a
preallocated buffer) to accomplish the task.

There may be an intermediate case:
You do allocate a large amount of memory in a short span of time, but
you allocate it in small chunks.

Locally (for the single new failing) this amounts to case (2).
Globally (for the client of the module that does all the allocations) it
should look like case (1) - the module reports failure to acquire the
necessary memory after rolling back the allocations. If this is the case
you may need specialized error handling in a well-defined part of the
program, but from a global design perspective it doesn't matter that
much.

Just my $0.02
-- Jörg Barfurth

Andy Glew

unread,
Dec 16, 1999, 3:00:00 AM12/16/99
to
Some alternatives to what I presented in my original post,
mainly by syntax of use:

a) From original post
------------------------------

Error stream that dumps in destructor,
macro to decide whether to exit/abort/throw:

class oerrstream : ... { ....
public:
~oerrstream() { cerr << ...buffered-error-messages... }
...
} errorstream;

#define ERRORSTREAM (errorstream << __FILE__ << ":" << __LINE__ << " ")

throw_error(int errcode) {
switch(error_handling_strategy) {
case THROW: throw( exception_wrapper(errcode) );
case EXIT: exit(errcode);
case ABORT: abort();
case USER_DEFINED: ...
}

Use example

level_N() {
if( error ) {
ERROR_STREAM << "most primitive error message"
throw_error( 1 );
}
}
level_J() {
try { level_Jplus1(); }
catch( exception_wrapper e) {
ERROR_STREAM << "another layer of error message";
throw;
}

b) Augment error stream with manipulators to throw


level_N() {
if( error ) {
ERROR_STREAM << "most primitive error message" << throw_error(1);
}
}

Doesn't seem to change intermediate layered error handlers.
May be a little bit smaller code.

c) Throw the error stream itself...

level_N() {
if( error ) {
BOILERPLATE(errorstream) << "most primitive error message" <<
throw_error(errorstream);
}
}
level_J() {
try { level_Jplus1(); }
catch( oerrstream e) {
BOILERPLATE(e) << "another layer of error message";
throw;
}

I'm not sure if this accomplishes anything, although it looks neat.

===

Another issue is that there probably needs to be a way
of resetting the error stream, flushing away any buffered errors,
in case one of the upper level exception handlers actually handles
the error message rather than just adding another layer of error message.

This flushing can be done in two places:
In the upper level handler,
and where the error message starts.
The latter is especially convenient and robust:

#define NEW_ERROR(es) ( es << clear() << __FILE__ << ":" << __LINE__ << "
" )
#define ADD_ERROR_INFO(es) ( es << __FILE__ << ":" << __LINE__ << " " )

level_N() {
if( error ) {
NEW_ERROR(errorstream) << "most primitive error message" <<
throw_error(errorstream);
}
}
level_J() {
try { level_Jplus1(); }
catch( oerrstream e) {
ADD_ERROR_INFO(e) << "another layer of error message";
throw;

Hendrik Schober

unread,
Dec 16, 1999, 3:00:00 AM12/16/99
to
J.Barfurth <tech...@bfk-net.de> wrote:
> There are actually different situations in which memory allocations can
> fail:
> (1) You request a large amount of memory, but can't get it because of
> the size requested.
> (2) Some program has exhausted memory, so even requests for
> comparatively small amounts of memory can fail. The culprit might be
> your own program, but it might be any other program as well if you are
> running on a multitasking OS.
>
> Case (2) is difficult to deal with, whether in standardized ways or not.
> On many OSs the whole machine will be unresponsive and OS behaviour may
> be wierd long before that. In such situations there may be no option
> other than to try to notify the user or log the error. After that: if
> you can't exit regularly, your program might just as well terminate.
> The best one can do usually is to just try - as you noticed even
> outputting an error might not work.

Deviding the out of error situations this way is a good point. Just
as you said, if global memory is exhausted, it usually makes no
difference whether your program fails miserably or dies "nice".

> [dealing with local memory exhaustion]

That gave me an idea! I may try to extend my exception base class so
that it installs a new handler upon instanciation, that has access to
a memory pool put aside at program start. (There's problems with global
and static instances, however. I still have to think about this for
a while.)

Schobi

Ken Hagan

unread,
Dec 16, 1999, 3:00:00 AM12/16/99
to
I think you are being too ambitious if you want to solve all these
problems at once. I would start by refining "error". (Actually,
I would personally start by abolishing the term "error" since
it is one of those blanket terms that people are happy to use
but reluctant to define clearly.)

If you are interested in a stack trace or meaningful core dump,
then this falls into the categorty of "debug event", and I think
some degree of platform specificity is permissible here. (The
shipping produce won't need them, so why deny yourself their
benefits?) Once you've allowed yourself that freedom, you can
probably find mechanisms that give your system pretty much
the same level of (self) knowledge as you enjoy yourself when
using a debugger. You may also find that these mechanisms
are *less* invasive than traditional #ifdef methods. On Win32
for example, you can run the system under its own debugger
and produce a full stack trace with no inserted code at all. You
can also use resumable exceptions like VMS. I think this
category is the only one that needs items c,d,e &f.

For errors which you want to handle in the shipping product, you
can still usefully distinguish between "resource exhaustion" and
"logic firewalls". The latter *would* be debug events, but in a
system of any size, prudence requires that separate modules do
not trust each other too much.

For resource exhaustion, you'll be most worried about "getting
out safely". The system may be feeling weak. I think the Guru
of the Week challenges on exception safety demonstrate idioms
that are well analysed, if not yet universally used. If you code
does actually manage to get out alive, then I'd argue that this is
normal operation, and none of items a-f apply.

For logic failures, you can give yourself the luxury of multi-levels
and a full logging facility. The system should not be in an over-
sensitive state. I think this covers items a & b.

"Andy Glew" <gl...@cs.wisc.edu> wrote in message
news:834kcl$a...@spool.cs.wisc.edu...


> What I want is an error reporting mechanism that
>
> (a) allows error exceptions to be caught, but which can nonetheless
> guarantee that an error message will be printed, instead of
> silently abort()'ing and dumping core on an uncaught exception.
>

> (b) layered error messages: some place to accumulate error messages
> from the throw-site, as well as higher up the call tree
>
> (c) debuggability: an error reporting mechanism that allows useful
> core dumps to be optionally taken in case of error. Unfortunately,
> I know of no cheap way to get both layered error messages and
> useful core dumps.
>
> (d) user ability to select throwing or aborting or exiting on an error
>
> (e) error message formatting
>
> (f) error message boilerplate such as __FILE__ or __LINE__

Andy Glew

unread,
Dec 21, 1999, 3:00:00 AM12/21/99
to
Ken Hagan <K.H...@thermoteknix.co.uk>

> I think you are being too ambitious if you want to solve all these
> problems at once.

I was probably not clear:

I have a satisfactory solution to all of the issues that
I raised, except for layered error reporting, in vanilla C:

errorf(errcode,printf_fmt,...)

which formats the error messages,
optionally exits or aborts (giving me a useful
coredump), etc.

However, the C++ literature deprecates using such
an error handling strategy, and recommends using
exceptions. Which is fine by me, since I have been looking
for a portable way to implement layered error messages
in C for more than 15 years.

I am, thereore, trying to find out if the C++ community
has evolved error reporting conventions that are at least
as functional as what I am used to.

The answer seems to be "No".

Marc Lepage

unread,
Dec 21, 1999, 3:00:00 AM12/21/99
to
Andy Glew wrote:
>
> except for layered error reporting

If C++ standard implementations *must* be able to unwind stacks for the
exception mechanism, can the standard not go one step further and
provide a portable way to get a stack dump at runtime?

For example, suppose an exception is thrown at point A and caught at
point D. You might, immediately after the catch, call stack_dump which
would return a string:

A(int, char*) throws exception
B(Point)
C(Point)
D(int, Point) caught exception

Then you could dump this to the screen, or a log file, along with
whatever other diagnostics you wish.

Can this be done without overhead in the presence of caught exceptions
which do *not* require a stack dump?

Or is all of this merely trying to make C++ more like an interpreted
language, like LISP, where such is common?

--
Marc Lepage
Software Developer
Molecular Mining Corporation
http://www.molecularmining.com/

Scott McMahan

unread,
Dec 21, 1999, 3:00:00 AM12/21/99
to
Andy Glew (gl...@cs.wisc.edu) wrote:

> errorf(errcode,printf_fmt,...)
> which formats the error messages,
> optionally exits or aborts (giving me a useful
> coredump), etc.

What problem does this solve that throwing an exception does not? I'm
missing something in this discussion. Your exception constructor could
call this function if it wanted to. I don't see what this is doing that
an exception could not do. You could crash the entire program in the
constructor if you wanted to with an exit call or abort or whatever
funky crash your OS provides.

It seemed like the main thrust of the original "brief" was that you had no
way to throw an exception from a library and have it adequately handled --
C++ can do everything you do with your function in a monolithic program,
but it breaks down in libraries since the C++ exception mechanism is not
defined for program "pieces" like DLLs. If you're in one big monolithic
program, you could define an exception and have a debug state object or
something that it queries to decide what to when it is constructed. Users
could catch the exception, if it let itself be caught and didn't abend
the program, or the could let it trickle up to a higher level exception
handler you already installed as part of your library initialization.

Scott

Andy Glew

unread,
Dec 22, 1999, 3:00:00 AM12/22/99
to
Scott McMahan <sc...@aravis.softbase.com> wrote in message news:dTO74.7315$Ye.4...@monger.newsread.com...

> Andy Glew (gl...@cs.wisc.edu) wrote:
>
> > errorf(errcode,printf_fmt,...)
> > which formats the error messages,
> > optionally exits or aborts (giving me a useful
> > coredump), etc.
>
> What problem does this solve that throwing an exception does not?

It formats the error messages, optionally exits or aborts giving
a useful coredump, etc. It acts as a single locus for error messages,
so that they can be sent to stderr or logged or whatever...

Yes, you can do all of this with exceptions. In fact, I think that my original
post sketched at least three different ways of doing this. All I was asking
is "Is there a standard or best known practice?", checking before rolling
my own.

One might also ask "What can exceptions do that the above C code
cannot do?" If the answer were "nothing", then exceptions would be
pretty useless.

But, exceptions can be useful: They can allow continued operation, recovering
from errors. That's probably pretty useful to people who design phone switches,
but is not my concern. For my much more limited area of programming,
exceptions seem to me to have great potential, allowing potentially more
meaningful error messages to be displayed - what I call "layered error messages".
If you want, I can hunt up references on conversational error message reporting,
although, again, that's a bit much for me.

Again, all I am asking is "C++ exceptions seem to allow layered error messages
to be implemented. Has anybody already done so, in a freely available model
available for imitation?"

Andy Glew

unread,
Dec 22, 1999, 3:00:00 AM12/22/99
to
Marc Lepage <mle...@molecularmining.com>

> Andy Glew wrote:
> > except for layered error reporting
>
> If C++ standard implementations *must* be able to unwind stacks for the
> exception mechanism, can the standard not go one step further and
> provide a portable way to get a stack dump at runtime?


Something similar has occurred to me:

One problem with the exception based mechanism for layered
error messages is that the stack context is lost by the stack unwinding.

Frequently, what I want is to print a meaningful layered error message
without unwinding the stack.

Yes: in some sense, that bugbear of "continuing excecution" after
an exception would solve this problem. However, I accept Stroustrup's
statement that continued execution is too difficult to be useful in general.

Rather, what I want to do is to call functions (actually, probably functors)
registered at different levels in the callstack, if an error occurs.

Doing this a priori penalizes error-free code.

What I want is to not pay for it in normal operation, just as exception
handling usually doesn't cost anything, if you have a table driven implementation.

gre...@cibc.ca

unread,
Dec 22, 1999, 3:00:00 AM12/22/99
to
In article <385FA4E8...@molecularmining.com>,

Marc Lepage <mle...@molecularmining.com> wrote:
> Andy Glew wrote:
> >
> > except for layered error reporting
>
> If C++ standard implementations *must* be able to unwind stacks for
the
> exception mechanism, can the standard not go one step further and
> provide a portable way to get a stack dump at runtime?

These are almost conflicting requirements, see below...


>
> For example, suppose an exception is thrown at point A and caught at
> point D. You might, immediately after the catch, call stack_dump which
> would return a string:
>
> A(int, char*) throws exception
> B(Point)
> C(Point)
> D(int, Point) caught exception
>
> Then you could dump this to the screen, or a log file, along with
> whatever other diagnostics you wish.
>
> Can this be done without overhead in the presence of caught exceptions
> which do *not* require a stack dump?

The basic mechanism would not be that hard, but it conflicts with the
requirement of not causing more overhead.

Specifically, if down in A, we throw an exception, then A's stack frame
will be destroyed, as we return from it, and all local variables,
destructed/destroyed.

The same happens as we go up the chain.

While we could keep the 'traced' stack in another place, this would
require memory, on order of the depth where the exception was thrown, to
where it was caught.

So the 'system' could provide such a trace, but at expense to ALL
throws, not just those that eventually wanted a stack trace.

Interestingly, my <sarcasm>FAVORITE</sarcasm> GUI class library, Open
Interface Toolkit from Blaze (formerly Neuron Data) has functionality
very similar to this in C and C++. You write code like this

void Class::Method()
{
ERR_TRACEIN;
// ... real code ...
ERR_TRACEOUT;
}

Combined with a couple of global declarations in the module, it provides
for full stack trace, as long as you have coded correctly. :-)

Someone could probably write a preprocessor to get the same effect.

gre...@cibc.ca


Sent via Deja.com http://www.deja.com/
Before you buy.

Andy Glew

unread,
Dec 23, 1999, 3:00:00 AM12/23/99
to
In case anyone cares, or has similar desires,
I have rolled my own error reporting package that
meets the requirements of my initial post in this string.

IT is best described by example.

To report an error, I do

void foo() {
if( error ) {
NEW_ERROR_STACK << "your message here\n" << die(666);
}
}

Everything is based around a stream type, error_stream_t.

die(int error_code) is a manipulator that either throws an exception,
exits with the specified error code, or aborts, with exception throwing being
the default. error_stream_t::set_error_action( enum error_action_t ) selects.

Layered error messages can be implemented

void bar() {
try {
foo();
}
catch(...) {
PUSH_ERROR_STACK << "your message here\n" << rethrow_error;
}
}

And minimal code in main() ensures that the error messages get printed:

int main() {
try {
bar();
}
catch(...) {
PUSH_ERROR_STACK << "error thrown to main\n" << exit_now;
}
}

NEW_ERROR_STACK and PUSH_ERROR_STACK are
macros that add boilerplate to the default error_stream_t
error_stream(), which is implemented as a pseudo-singleton
for control of initialization order. error_stream is connected to cerr.

The manipulator die(int error_code) has already been mentioned.
Manipulators rethrow_error and exit_now are in the example above;
other, explicit, manipulators include explicit abort_error, exit_error(int),
throw_error(int); flush (forcing messages out); clear (clearing out
pending messages, e.g. if the error is handled).

I currently throw naked integers as the exception objects
- not great, but simple.

This is implemented as a header file, requiring no object code
to be linked in; I have found that header-file-only facilities
are a lot easier to use than facilities that require object modules
or libraries to be linked in.

I believe that this provides a pleasant error formatting facility,
supporting standardized error messages, with the possibility
of layered error messages. Minimal code is required to use it.

---

I'll happily make this code available. It will probably appear
on my website, www.cs.wisc.edu/~glew/glew.html, in the next few
days (when I transfer it from my laptop).

---

Caveat: I have implemented error_stream_t grossly as a wrapper
around ostream, not in the "proper way" by creating a new streambuf
derived class. I did this because Plauger's standard streambuf interception
code doesn't work on GCC 2.95.2; although it is easy to work around,
I prefer to delay the real thing until GCC really supports standard iostreams.

Andy Glew

unread,
Dec 23, 1999, 3:00:00 AM12/23/99
to
> > If C++ standard implementations *must* be able to unwind stacks for the
> > exception mechanism, can the standard not go one step further and
> > provide a portable way to get a stack dump at runtime?
>
> The basic mechanism would not be that hard, but it conflicts with the
> requirement of not causing more overhead.
>
> Specifically, if down in A, we throw an exception, then A's stack frame
> will be destroyed, as we return from it, and all local variables,
> destructed/destroyed.
>
> The same happens as we go up the chain.
>
> While we could keep the 'traced' stack in another place, this would
> require memory, on order of the depth where the exception was thrown, to
> where it was caught.
>
> So the 'system' could provide such a trace, but at expense to ALL
> throws, not just those that eventually wanted a stack trace.

This statement is bogus.

The information necessary to provide such a stack trace
is the same information that allows stack unwinding for exceptions.
It could be accessed, the stack trace could be created,
*without* unwinding the stack.


> Interestingly, my <sarcasm>FAVORITE</sarcasm> GUI class library, Open
> Interface Toolkit from Blaze (formerly Neuron Data) has functionality
> very similar to this in C and C++. You write code like this
>
> void Class::Method()
> {
> ERR_TRACEIN;
> // ... real code ...
> ERR_TRACEOUT;
> }
>
> Combined with a couple of global declarations in the module, it provides
> for full stack trace, as long as you have coded correctly. :-)

Agreed, this is a bogus approach.

By the way, it is also what I call an a priori layered error message
approach, because it does work even when the trace is not required.


> Someone could probably write a preprocessor to get the same effect.

We're all waiting for a preprocessor that creates a reflective API
for C++.

Andy Glew

unread,
Dec 23, 1999, 3:00:00 AM12/23/99
to
In my last post I described a "brass-plated" error reporting package
- an error_stream_t, used as

> void foo() {
> if( error ) {
> NEW_ERROR_STACK << "your message here\n" << die(666);
> }
> }
with the usual stream "formatting", manipulators for various error exits,
and layered error messages via exception throw/catch.

I say "brass plated" because, while certainly not as fancy as gold-plated
error reporting systems such as Hendrik Schober has described, it is
nonetheless featureful and full of code that may have to be carried around.

It's chief advantages are
a) formatting
b) layering
c) support for the possibility of an error report being cancelled, if the
exception is handled, but with error messages being forced out if not
handled (vs. the standard problem with uncaught exceptions, silently
omitting the error messages.


A "tin-plated" approach to error reporting, suitable for situations where
you may want
a) formatting
b) layering
but are willing to give up on c), the possibility of quietly handling exceptions,
is to print to cerr and throw an exception:

void foo() {
if( error ) {

cerr << "your message here\n";
throw 666;
}
}

void bar() {
try {
foo();
}
catch(...) {

cerr << "your message here\n";
throw;
}
}

int main() {
try {
bar();
}
catch(...) {

cerr << "error thrown to main\n";
exit(1);
}
}

This loses the ability to handle exceptions without failing, but
has the advantage of containing absolutely no idiomatic code.

---

Q: what exception object to throw?

There seems to be no useful standard. It has been pointed out that
reusing *any* existing exception class creates the possibility of
exceptions being caught by accident, by accidentally type matching
a different exception use. Therefore, *NOT* reusing an existing exception
class seems to be wisest.

Throwing an int *might* be used in an idiom

catch(int errcode) {
exit(errcode);
}

Similarly, throwing a string might lead to simple parsing

catch(string s) {
if( regexp_match(s,".*foobar error.*) ) {
...
}
}

but neither of these seem to be established as conventions.

Therefore, it is probably just safest to throw a completely ad-hoc
class of your own definition, that is guaranteed not to conflict with
anyone else's exception convention, unless you are plugging into an
existing exception system.

---

Finally, I realized that unit testing is another of my motivations for wanting
an error reporting system that is more powerful that just plain exit(1):

** unit testing is good

** consistency checks that verify assumptions,
and report an error when an assumtion is validated,
are good

-- but, it is significantly harder to write a portable test jig
for something that exits or aborts.

Throwing an exception allows a test jig to test such boundary conditions,
and continue running the test.

E.g.

void foo(int i) {
assert( foo > 0 );
...
}

void test_jig() {
...
for(;;) {
try { foo(-1); }
catch(...) {
cerr << "TEST PASSED?: foo(-1) threw, as expected\n";
break;
}
cerr << "TEST FAILED: foo(-1) did not throw\n";
exit(99);
}
.... more test cases
}

Note that it may be desirable to have the test jig itself throw an error
rather than exiting, so that continued testing at higher levels can be done.

Note that I have assumed that assert() can be configured to throw
an error rather than calling abort() and core dumping. This is straightforward,
albeit non-standard.

Continuing with the "What sort of exception to throw", it may be desirable for
the test jig to
a) verify that a particular, meaningful, error message has been provided
b) verify that a particular check failed, and not a different, unrelated, check
Sometimes you just want to test that an error is reported, but you
don't particularly care what. Or, more likely, you care, but you realize that
testing error message contents leads to particularly fragile tests as error
messages may improve over time. Sometimes you are actually testing
what the error message was, e.g. in response to a user bug report such as
"Error: insufficient resources" is an awfully vague bug report, when something
like "Error: disk full accessing /tmp/bat767" would help the user figure out how
to work around the error. And, sometimes you are trying to test boundary
condition coverage - otherwise it can be easier for a particular assertion not
to be tickled.

I don't have any particularly bright ideas for testing actual error message contents,
except possibly to provide access to the buffered messages in an error stream.
Alternatives such as throwing a string are possible, but fragile.

Testing assertion coverage motivates creation of an ad-hoc structure with
appropriate info, such as a bitmask of boundary conditions.

Ad-hoc structures motivate always catching ... in main:

int main() {
try { ... }
catch(...) {
cerr << "main exception";
exit(1);

Martin Aupperle

unread,
Dec 28, 1999, 3:00:00 AM12/28/99
to
On 23 Dec 1999 06:42:42 -0500, "Andy Glew" <gl...@cs.wisc.edu> wrote:

>
>The information necessary to provide such a stack trace
>is the same information that allows stack unwinding for exceptions.
>It could be accessed, the stack trace could be created,
>*without* unwinding the stack.
>

What would be the outcome? Some list of hex numbers of function entry
addresses? You need some additional information (aka map file) to make
the information usable.
IMHO, if the client wants a readable stack trace, (s)he must agree to
store the necessary strings somewhere in the program. E.G. a local
object in every function with a appropriate ctor and dtor.


------------------------------------------------
Martin Aupperle
MikeAlpha@NoSpam_csi.com
(remove NoSpam_)
------------------------------------------------

David Lindauer

unread,
Dec 31, 1999, 3:00:00 AM12/31/99
to

Martin Aupperle wrote:

> What would be the outcome? Some list of hex numbers of function entry
> addresses? You need some additional information (aka map file) to make
> the information usable.
> IMHO, if the client wants a readable stack trace, (s)he must agree to
> store the necessary strings somewhere in the program. E.G. a local
> object in every function with a appropriate ctor and dtor.

you don't need a full-fledged object for that, since the data is static it
doesn't need to be manipulated at run-time. Given sources for a C++
compiler that does adequate exception handling, just adding the name of
the function into the exception table for the function would itself be a
big improvement towards a useable stack trace... and anyone who is already
living with exception handling surely isn't going to mind another 20-30%
bloat on the exception handling database? Make a nice compiler option :)

David


--
---------------------------------------------------------------
David Lindauer mailto:cam...@bluegrass.net ICQ: 8699592

http://www.members.tripod.com/~ladsoft/index.htm (computer page)

http://www.members.tripod.com/~ladsoft/para/index.htm (awareness
page)
http://www.members.tripod.com/~ladsoft/ttc/index.htm (tao te ching)

Dave Abrahams

unread,
Dec 31, 1999, 3:00:00 AM12/31/99
to
In article <83thm0$1...@spool.cs.wisc.edu> , "Andy Glew" <gl...@cs.wisc.edu>
wrote:

> It's chief advantages are
> a) formatting
> b) layering
> c) support for the possibility of an error report being cancelled, if the
> exception is handled, but with error messages being forced out if not
> handled (vs. the standard problem with uncaught exceptions, silently
> omitting the error messages.

It's chief disadvantage is that streams may throw exceptions, thus obscuring
the error that actually occurred first. Unless your error_stream_t is
somehow immune to this, I would be concerned about your approach.
-Dave

Andy Glew

unread,
Dec 31, 1999, 3:00:00 AM12/31/99
to
Martin

> >The information necessary to provide such a stack trace
> >is the same information that allows stack unwinding for exceptions.
> >It could be accessed, the stack trace could be created,
> >*without* unwinding the stack.
> >
> What would be the outcome? Some list of hex numbers of function entry
> addresses? You need some additional information (aka map file) to make
> the information usable.
> IMHO, if the client wants a readable stack trace, (s)he must agree to
> store the necessary strings somewhere in the program. E.G. a local
> object in every function with a appropriate ctor and dtor.

Oy, vey, I did not think that I needed to spell it out!

Yes: if we were going to use this as a non-stack unwinding implementation
of layered error messages, you would

a) in compiling, whenever an error message "layer" is encountered,
have the compiler save the messages or, more likely, the code necessary
to produce the error message.
Map program counter to this error message code.
Generate this error message code in a distant segment

b) during normal execution, just execute - no overhead for this error code

c) on an error, walk back up the stack WITHOUT unwinding.
Take any stack PC, and look up the corresponding error handling code,
and execute that code. (The biggest hassle is access to auto stack local
variables - doable for "structured programming", but a challenge to a
C/C++ mindset that doesn't have nested procedures.)

Yes, this requires compiler support. Linguistically, you might express
it as

try {
code
}
error {
error-code
}

where, again, error-code is executed in case of an error,
but WITHOUT the stack being wound back.

---

The data structure that manages exceptions is basically something
like the map from program counter to code. Exceptions = this map
+ stack unwinding.

The key aspects of this data structure is that it maps PC to handler
code, and it is created at compile time.

My point is that this sort of data structure would be quite useful
for other things - not just exceptions. Unfortunately, C / C++ do not allow
the user to manipulate program counters, and have limited abilities
to create user defined compile time data structures.

I.e. the PC to exception handler map is hardwired in C++.

Andy Glew

unread,
Jan 1, 2000, 3:00:00 AM1/1/00
to
> > It's chief advantages are
> > a) formatting
> > b) layering
> > c) support for the possibility of an error report being cancelled, if the
> > exception is handled, but with error messages being forced out if not
> > handled (vs. the standard problem with uncaught exceptions, silently
> > omitting the error messages.
>
> It's chief disadvantage is that streams may throw exceptions, thus obscuring
> the error that actually occurred first. Unless your error_stream_t is
> somehow immune to this, I would be concerned about your approach.

Agreed, this is a big disadvantage.

Can you recommend any other candidate approach, that provides
the above three criteria a, b, c, and which nonetheless ensures
that error messages will be forced out?

I'll happily adopt some other equally capable approach. I only suggest
this error_stream_t because I can find no other.

As for "streams throwing exceptions", I think that the following is the approach
I would take if this were problematic. There are three situations which can cause
the stream to throw an exception:
a) Memory allocation failures storing the buffered messages
b) Insertion errors - i.e. formatting errors
c) actual I/O errors

a) Memory allocation is almost an inherent characteristic of my approach,
to buffer the layered error messages until it is determined whether they should
be output or not. If this memory allocation is dynamic, the possibility of running
out of memory is always there. Static allocation can avoid that possibility.
Probably the best way would be to statically allocate a minimum sized buffer
for the leafmost eror message, and dynamically allocate any excess messages.
This can guarantee that at least something will be output.

By the way, currently I use an istrstream for the buffering, GCC's dynamic
version, since GCC doesn't support istringstreams yet. I'll probably go to
istringstream for convenience and to be standard, but, for safety, I may have
to stay with a rolled-my-own non-standard istrstream equivalent, static and
semi-dynamic as discussed above.

b) Insertion errors - exceptions caused while formatting the text to be output
- I think must largely be under user (programmer) control.

c) actual I/O errors when it comes time to flush the layered error messages
are similarly out of scope.

I.e. overall memory allocation errors are the only real problem.

It is unfortunate that throwing an exception within the exception handler that
implements a layered error message will cause an abort. I'd much rather
not use exception handling to do this.... but, lacking the sort of introspection
described in previous posts, I do not see how to do this without a significant
run-time cost. At least, an abort isn't much worse than a non-layered error
message and immediate exit.

Andy Glew

unread,
Jan 1, 2000, 3:00:00 AM1/1/00
to
> It's chief disadvantage is that streams may throw exceptions

Oh, I should also add that a proper implementation
of my error_stream_t would, on UNIX, perform an unbuffered
write() syscall on whatever contiguous memory buffers
hold the layered error messages, to further minimize
the chance of streams related exceptions.

I admit that I have not done this; in fact, although my
error_stream_t uses << and proxies all manipulators
via a member template to an internal istrstream,
it still uses a ostream for final output. I do this
because I am (a) lazy, (b) have not yet encountered
nested exceptions as a problem, and (c) want to
maximize portability of a primitive implementation,
although I realize that eventually a more robust
implementation is desirable.

Dave Abrahams

unread,
Jan 2, 2000, 3:00:00 AM1/2/00
to
In article <84jkoh$q...@spool.cs.wisc.edu> , "Andy Glew" <gl...@cs.wisc.edu>
wrote:

> Agreed, this is a big disadvantage.


>
> Can you recommend any other candidate approach, that provides
> the above three criteria a, b, c, and which nonetheless ensures

> that error messages will be forced out?

I think you might try something like this small adjustment:

struct error_stream_t
{
error_stream_t() : used(0) {}

char buffer[1000];
std::size_t used;
};

template <class T>
error_stream_t& operator<<(error_stream_t& s, const T& y)
{
// standard not consulted. Not responsible for errors!
try
{
std::ostringstream s; s << y;
std::strncpy(buffer + used, s.str().c_str(), 1000 - used);
used += std::min(s.size(), 1000);
} catch(...){}
return s;

Andy Glew

unread,
Jan 3, 2000, 3:00:00 AM1/3/00
to
> I think you might try something like this small adjustment:
[fixed size buffer, << to an ostringstream and then copy to buffer]

Fair enough, but...

Q: why should using an ostringstream and then copying to a buffer
be any better than outputting directly to a fixed size buffer (via the
old ostrstream, or the equivalent)?

ostringstreams hide dynamic memory allocation, and hence an
opportunity for nested exceptions, within themselves, don't they?

Wouldn't it be better to directly output to the fixed size buffer?

---

Also, something both your and my present code share:

They aren't true streams. E.g. my present code proxies << via a
template to an internal ostrstream (I'm still using GCC's old library),
rather than placing itself into the streams and streambuf hierarchy.

The template proxying of << meets 99% of all my needs, but because
it is not derived from a true stream, other interfaces such as
stream.flush() don't work, unless manually intercepted.
("stream << flush" works; "stream.flush()" doesn't, unless I do more work).

Now, like I say, this fake template proxying of << to an internal stream
meets 99% of my needs, but I have been somewhat embarassed because
it is not 100% a stream equivalent. I was planning to convert to a true
stream when GCC becomes more capable of standard C++ isms.

Are you suggesting that it may be better *NOT* to do so?

>
> struct error_stream_t
> {
> error_stream_t() : used(0) {}
>
> char buffer[1000];
> std::size_t used;
> };
>
> template <class T>
> error_stream_t& operator<<(error_stream_t& s, const T& y)
> {
> // standard not consulted. Not responsible for errors!
> try
> {
> std::ostringstream s; s << y;
> std::strncpy(buffer + used, s.str().c_str(), 1000 - used);
> used += std::min(s.size(), 1000);
> } catch(...){}
> return s;
> }

0 new messages