 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
MikeB Guest
|
Posted: Sat Sep 13, 2003 7:53 pm Post subject: Using longjmp in c++ |
|
|
I thought that would get your attention!
I'm looking for advice on the correct error handling / recovery strategy to
take.
I'm currently working on a project which uses an exception-unaware C++
compiler (ARM RealView - http://www.arm.com/support/rvct2_faq ). Although
the compiler developers are currently adding support for exceptions, as I
understand it, this won't be released until Q4,2003 / Q1,2004. Fortunately,
this is well before the project is expected to ship
The project is also expected to reuse legacy 'C' APIs - these make extensive
use of return codes to convey error information
Our dilemma is how we should implement error handling and recovery in the
short term. I'd rather not even start adding temporary support for return
codes as I'm sure that this will be a slippery slope.
Now, in the first instance, our main concern is catching any errors which
originate from the legacy APIs - these are effectively all of type 'int'. In
view of this, it is relatively simple to hand-roll support for this type of
exception using setjmp/longjmp. We could then use the preprocessor to
compile this in until the new compiler arrives at which point we can
preprocess it out.
Does this seem a reasonable temporary solution or should we just capitulate
and start adding return codes?
Regards,
MikeB
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
llewelly Guest
|
Posted: Sun Sep 14, 2003 11:21 am Post subject: Re: Using longjmp in c++ |
|
|
"MikeB" <objectivedynamics (AT) hotmail (DOT) com> writes:
[snip explanation of why exceptions are not (yet) availible]
| Quote: | Now, in the first instance, our main concern is catching any errors which
originate from the legacy APIs - these are effectively all of type 'int'. In
view of this, it is relatively simple to hand-roll support for this type of
exception using setjmp/longjmp.
|
Here are 3 disadvantages to keep in mind when using setjmp/longjmp:
(0) local variables which are not volatile have undefined state
when returning to the setjmp point using longjmp.
(1) Few (no?) member functions of standard library objects are
volatile quallified. This means you can't use standard
library objects as local objects in a block you may arrive at
via longjmp.
(2) In most cases, few third-party or inhouse member functions are
volatile qualified.
| Quote: | We could then use the preprocessor to
compile this in until the new compiler arrives at which point we can
preprocess it out.
|
.... and that makes 4 disadvantages.
| Quote: | Does this seem a reasonable temporary solution or should we just capitulate
and start adding return codes?
[snip] |
I think return codes present fewer opportunities for error, and I
feel the resulting errors (in addition to being fewer in number)
tend to be much easier to find.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Dave Harris Guest
|
Posted: Sun Sep 14, 2003 11:31 am Post subject: Re: Using longjmp in c++ |
|
|
[email]objectivedynamics (AT) hotmail (DOT) com[/email] (MikeB) wrote (abridged):
| Quote: | I'm currently working on a project which uses an exception-unaware
C++ compiler
[...]
Now, in the first instance, our main concern is catching any errors
which originate from the legacy APIs - these are effectively all of
type 'int'. In view of this, it is relatively simple to hand-roll
support for this type of exception using setjmp/longjmp. We could
then use the preprocessor to compile this in until the new compiler
arrives at which point we can preprocess it out.
|
Longjmp won't unwind the stack, so this would mean coding without much
use of destructors and RAII. Can you live with that? It doesn't seem
very tenable to me. How will you write classes that manage resources?
Eg anything like std::string or std::vector will tend to leak memory.
I would look hard at using error codes wrapped in some suitable
Result class. You can use the class to ensure that errors are tested
for before the Result object is destroyed. For the time being you
can treat that as a bug, and assert(). This will help prevent one of
the basic problems of result codes: forgetting to test them. When
exceptions are supported you can treat it as permissible and throw
instead. So the Result class can also ease the transition to exceptions
a little.
If you must use longjmp I would look at managing your own list of
objects-to-be-destroyed. Maybe even garbage collection.
| Quote: | Does this seem a reasonable temporary solution or should we just
capitulate and start adding return codes?
|
I would rather live without exceptions than without destructors.
-- Dave Harris, Nottingham, UK
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Antoun Kanawati Guest
|
Posted: Sun Sep 14, 2003 10:03 pm Post subject: Re: Using longjmp in c++ |
|
|
The short answer is NO!
For a more elaborate answer, keep reading.
MikeB wrote:
| Quote: | ...
Now, in the first instance, our main concern is catching any errors which
originate from the legacy APIs - these are effectively all of type 'int'. In
view of this, it is relatively simple to hand-roll support for this type of
exception using setjmp/longjmp. We could then use the preprocessor to
compile this in until the new compiler arrives at which point we can
preprocess it out.
...
|
C++ and setjmp/longjmp do not mix well at all.
Back in the early 90's, a lot of people wrote "exceptions" based
on longjmp (I did a few myself).
The general structure of such a beast would be:
1. A "runtime stack", where YOU keep track of the objects to be cleaned
up when you "throw". This is not the real runtime stack, where the
compiler tracks these things for you.
2. Methods for registering/unregistering objects on your "runtime
stack".
This is where things get problematic.
One of the common patterns is deriving all classes from something
like "CleanupObject", which has a virtual destructor, and programming
the statck unwind against that class. With templates, you can do
develop a less invasive solution that is, in principle, equivalent.
The other problem is to figure out which objects are to be destroyed
during an unwind and which are to be left alone. That is: which
objects are stack based, and which are not.
Two common solutions are known:
1. The programmer explicitely unwind-protects such objects, and relies
on the normal destructor behavior to manage the protection; eg:
Foo foo;
// make sure foo is destroyed in case of a longjmp.
// UnwindProtect interacts with the GLOBAL cleanup
// stack.
UnwindProtect<Foo> foouwmp;
2. You develop the right voodoo to distinguish automatic objects
from heap/static/embedded objects. You will need additional
voodoo to hook up such objects into the unwind mechanism
(like a common root class with a virtual destructor).
Now, after all is said and done, you still have issues:
1. longjmp interactions with optimizations and volatility.
2. exceptions thrown during construction: pre-exceptions C++
dealt with construction as an atomic operation. So, if
your constructor fails during the initialization of an
embedded object, the remaining parts of the object are
still random bits. So, during unwind-induced cleanup
you will have some serious issues to deal with (random
pointers, and such).
3. the possibility of longjmping into the wrong place; a
longjmp that winds the stack up instead of unwinding it.
(this can happen if an expired jmp buffer is reused
inadvertently.)
4. threads: the cleanup stack must be global. With
multiple threads, you will need a per-thread cleanup
stack. Then, you will have to worry about possible
interactions between threads, etc...
(note: even with the exception-enabled C++ this may
be an issue, since threads are not part of the
language.)
Note that I have not said anything about the nature of the
exception data itself. That is a non-issue.
So, in short words, this is a non-trivial undertaking, fraught
with risk. Furthermore, the impact on the structure of your
classes and code is global; every class you design, and
every line of code has to reflect the limitations of the
strap-on exception system.
The best bet is to stick to return codes.
A not-too-bad approximation is to set up an exit-on-error
label at the end of functions, and use the foul goto
to avoid deeply nested if/else constructs. With the
proper application of macro-magic, this can be made to
look like exception handling code (until you get the
realy thing).
Another technique is to use the '&&' and '||' operators
as a syntactic variant on if/else, as in shell programs.
You'll need to support the technique with spacing
conventions. With some templates, and creative idiom
creation, this won't look so bad.
--
Antoun Kanawati
[email]antounk (AT) comcast (DOT) net[/email]
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Thomas Richter Guest
|
Posted: Mon Sep 15, 2003 10:02 am Post subject: Re: Using longjmp in c++ |
|
|
Hi,
| Quote: | Does this seem a reasonable temporary solution or should we just capitulate
and start adding return codes?
|
Depends on your class design. It is possible to use this design, I've done
this myself as well because in the environment we had we couldn't use
exceptions for similar reasons - the project was setup with exceptions
included, but we had to remove them for several reasons. However, you should
keep care of the following problems:
o) Local variables might get trashed if "long-jumped" to them. You would need
to declare them volatile. That has to happen only where you have a longjump,
i.e. an "emulated catch". Thus, "avoid putting objects onto the stack" and
allocate them on the heap.
o) Hand-rolled try/catch will not call destructors of objects if they move
out of visibility. That has a very important and severe impact on the design
of your classes as you have to be very careful about the memory management.
The way we did it here was to keep all dynamically allocated object somewhere
recorded in the administrating class, thus all objects would be in a kind of
large tree. Thus, if the root object would get destroyed, all other objects
would be cleaned up as well recursively. Specifically, this problem implies
that constructors must never fail and never "setjmp" out of the object. If
they would, the allocated object would not yet have been linked into the
object tree and you would have a memory leak. Instead, use object factories
that also link the object into some kind of administration structure.
I still have a class here that also manages a try/catch stack in a very
simple and efficient way should there be any need for it.
Greetings,
Thomas
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|