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 |