Re: [Alephmodular-devel] On errors
Status: Pre-Alpha
Brought to you by:
brefin
|
From: Woody Z. I. <woo...@sb...> - 2003-01-31 16:55:11
|
On Friday, January 31, 2003, at 09:23 AM, Br'fin wrote:
> Hsm, was just thinking about exceptions and C++. Exceptions get to be a
> nuisance if the current function itself is just a pass through for an
> exception. (open_preferences would continue to throw a
> fileNotFoundException that originated in open_file and also had to
> propagate through wad_open.) Or do you just need to pussyfoot around
> your declarations to make sure that if you're passing an exception that
> you didn't declare any memory before the point where the exception can
> occur?
>
IIRC C++ is not as picky as Java with respect to declaring 'throws'
etc., which could be viewed as a mixed blessing I suppose.
As for allocating memory, well your use of the phrase 'declaring memory'
concerns me - if a function does int i; or char myStorage[40]; , the
memory needed for those operations is taken from the stack, and will be
released automatically when the stack is unwound during the exception
propagation.
OTOH allocating memory with new etc. does take some care, because the
exception mechanism (understandably) won't try to follow through the
remainder of a function it's unwinding to see if there are any delete's
in there. So typically I think C++ programmers would use a stack-based
wrapper around dynamically-allocated storage... that is, you have a
class that's something like (I'm making this up, I'm sure there are
better ways to do these things out there:)
template
class Autoreleaser<typename tType> {
tType* mPointer;
public:
Autoreleaser(tType* inPointer) { mPointer = inPointer; }
~Autoreleaser() { if(mPointer != NULL) delete mPointer; }
};
bool
foo() {
Blah* theBlah = new Blah;
Autoreleaser<Blah> theBlahAutoreleaser(theBlah); // do I have to
specify the type explicitly as a template argument?
do_some_stuff_that_might_throw();
if(something)
return false;
do_more_stuff();
return true;
}
This way (since theBlahAutoreleaser's destructor will be called) theBlah
will get deleted when foo() exits either by falling off the end,
returning from somewhere, or through exception unwinding. You could
always have a method "DontRelease()" or something in Autoreleaser that
you could call at the end of your function if you wanted to keep the
pointer in a successful operation, etc.
I think the C++ Standard Library type 'auto_ptr' might include the above
functionality among others. I haven't studied or used it yet. But if
it's appropriate, I of course support using the standard mechanism
rather than homegrown.
My Logging mechanism in A1 is intended for such stack-based use: you use
the utility macro logContext1("trying to gather player %s",
thePlayerName);, say, and it pushes that context onto the Logger's
stack. When the block that 'owns' the stack-allocated variable (hidden
from you by the macro) exits, one way or another, that context
description is popped off the stack - no explicit action is needed in
the code. Log messages emitted with one of the standard Logging macros
(logError2("unable to establish connection to %d:%d", theAddress.host,
theAddress.port);) thus can be prepended with some or all of their
enclosing contexts (but the context switches themselves don't have to be
logged if no log messages are emitted within them).
The same logging message could be entered from a variety of runtime
contexts (e.g. having problems opening a file could happen during
startup, or while opening a film, or while trying to load a replacement
texture, etc.). The low-level routine logs its problem and depends on
the higher-level routines having established a context ("loading
replacement texture file '%s' for collection %d shape %d") for that
problem. Obviously there's some overhead to switching contexts, even if
no intervening messages are logged, but IMO it's a small price for a
scheme that's convenient for programmers (and thus will be used) and
useful for log-readers (because it provides the information needed
without too much excess, and because the programmers actually use it -
so it contains (and will contain more) useful information). The
existing dprintf() and fdprintf() were forwarded to Logging, so all
those existing log messages automatically come out in the new system
(and can potentially benefit from context information), giving an
immediate 'forced adoption'. ;)
Anyway still, reducing the context-switching overhead is one area (of
many) for improvement in the scheme.
Oops, sorry, did not mean to get _that_ far off track from the original
discussion of leveraging destructor calls for stack-based objects. But,
I guess I'm me. :)
Woody
|