llama@eleazar.dartmouth.edu (Joe Francis) (08/14/90)
I have had multiple requests to post this information, which is a summary of two reponces I received plus the entirety of two others on the topic of error handling of classes with an instance limit. I am new enough to C++ that I probably abuse some terminology, and quite possibly misunderstood the two replies I summerized (which I wouldn't have if I hadn't lost them). ------------------------------------------------------------------------------- None of the methods suggested was a completely general solution, in that they assume that the objects will only be created with new, ie no local or global storage, only free store. With that in mind, here are a couple of replies I got. I also lost a couple of replies, which went something like this: 1) You can't make your constructor have a return value, but you can make it have arguments - so pass it a pointer to an error indicator. The constructor checks the instance limit, and if it fails the error indicator is set accordingly. A wrapper for creation of the objects can be used to check for errors. So instead of: Object* object = new Object(); you get: Object* object = Object::MakeObject(); static Object* Object::MakeObject(void) { short error; Object* object = new Object(&error); if (!error) return object; delete object; return NILPOINTER; } This assumes you want the caller to see a nil pointer from EITHER failed allocation or exceding the limit. If you want the caller to be able to distinquish between the two you can do away with MakeObject and have the caller check "error". 2) Make your own version of "new" for the class, which checks the instance limit itself and calls the general "new" only if that limit is not exceeded. This way you don't ever allocate an object that is just going to be immediately deleted due to exceeding the limit. Following are the messages I received which I haven't lost. Cheers, Joe --------------------- From taumet!taumet!steve@uunet.UU.NET Wed Aug 1 20:10:44 1990 Received: from uunet.UU.NET by eleazar.dartmouth.edu (5.61D1/4.2) id AA27457; Wed, 1 Aug 90 20:10:34 -0400 Received: from taumet.UUCP by uunet.uu.net (5.61/1.14) with UUCP id AA12715; Wed, 1 Aug 90 12:48:14 -0400 Received: by taumet.com (smail2.5+deliver); 1 Aug 90 09:41:46 PDT (Wed) Reply-To: steve@taumet.com (Stephen Clamage) Message-Id: <9008011641.AA26682@taumet.com> Date: Wed, 1 Aug 90 16:41:44 GMT From: steve@taumet.com (Stephen Clamage) X-Local-Time: Wed, 1 Aug 90 09:41:44 PDT To: llama@eleazar.dartmouth.edu Subject: Re: whoops, lets chuck this object Newsgroups: comp.lang.c++ References: <23442@dartvax.Dartmouth.EDU> Status: R In comp.lang.c++ you write: >I just want attempts to create local or free objects to bounce like >a bad check if my limit is exceded, and for the caller to be told >this in a respectable way, and not have to do to much paperwork as >a result. At the moment, there is no better way than what you are already doing: Set a status flag for the user to check. You could improve on this in the way similar situations are handled in the streams and iostreams libraries: Define operator void* and operator! to return 0 and 1 respectively in the case of bad status. Define a status() function (or ok(), or some such) to report whether things are ok. All other member functions simply refuse to do any work (and report failure if appropriate) whenever the status is bad. Now the user has many levels at which to discover that things have gone wrong: He can test a pointer to the object, he can request status, or he can note that member functions didn't work or reported failure. class special { public: special() { if( ++count <= max ) { ... } } ok() { return count <= max; } operator void*() { return count <= max ? this : 0; } int operator !() { return count > max; } void f1() { if( ok() ) { ... } } int f2() { if( ! ok() ) return FAIL; else { ... } } ... private: const int max = 10; static int count; ... }; Classes derived from special will have to check special::ok() in their constructors, or they may wind up using uninitialized data. Another approach is to use the ANSI C (and unix C) signal mechanism. The constructor can raise a signal which aborts unless the user catches it. The user can choose to catch it and handle the situation appropriately. Future releases of C++ will include an exception mechanism, which will be the clean way to handle the whole thing. It will be some time before this is available, so don't wait for it. -- Steve Clamage, TauMetric Corp, steve@taumet.com From edward@runxtsa.runx.oz.au Mon Aug 6 20:24:59 1990 Received: from munnari.OZ.AU by eleazar.dartmouth.edu (5.61D1/4.2) id AA08235; Mon, 6 Aug 90 20:24:50 -0400 Received: from runxtsa.runx.oz (via metro) by munnari.oz.au with SunIII (5.61+IDA+MU) id AA21546; Tue, 7 Aug 1990 10:24:41 +1000 (from edward@runxtsa.runx.oz.au for llama@eleazar.dartmouth.edu) Received: by runxtsa.runx.oz (5.51) id AA29309; Tue, 7 Aug 90 01:33:57 AEST (from edward@runxtsa.runx.oz for llama%eleazar.dartmouth.edu@cs.mu.oz) Date: Tue, 7 Aug 90 01:33:57 AEST From: edward@runxtsa.runx.oz.au (Edward Birch) Message-Id: <9008061533.AA29309@runxtsa.runx.oz> To: llama@eleazar.dartmouth.edu Subject: re: counting # of invocations of C++ objects Status: R In article: <23442@dartvax.Dartmouth.EDU> > I have a class which has a limit on the number of instances that can > exist at any one time. Currently my constructor checks the limit, > and if we are over it a flag in my object is set. It is then the > callers responsibility to check the flag, call delete if necessary > (if the object is free, not local), and do whatever needs to be done > to keep the user happy (yeah, right). > I just want attempts to create local or free objects to bounce like > a bad check if my limit is exceded, and for the caller to be told > this in a respectable way, and not have to do to much paperwork as > a result. I have given code below that will allow you to create objects via ::new() but not allow you to create automatics. If the limit has been exceecded then ::new() will return 0. Edward Birch ------------------ CUT HERE --------------------------------------- #include <stdio.h> // This is a "garbage" class for exclusive use of the class "test". // It is used to enable the "test" class to have access to ::new() // class garbage { public: garbage(int x) { (void)x; }; }; static const int MAX_TEST_ITEMS = 10; // limit on item count class test { private: static int test_count; // initialized to 0 test(class garbage * g) { (void)g; }; // interface to ::new public: // The following code should be duplicated in any // public constructors for "test". // test() { class garbage g(0); // Trap for invocations of the following form // // class poot name; // // The only protection against this is to report // error and terminate // if (this) { fprintf(stderr, "automatic test invocation not allowed\n"); exit(1); } // Are we allowed to allocate another element // if (test_count >= MAX_TEST_ITEMS) this = 0; else { test_count++; this = new class test(&g); // other initialization code goes here } }; ~test() { test_count--; // other de-initialization code goes here }; }; #define DIM(x) (sizeof (x) / sizeof *(x)) // Test code resides here // main() { class test * tab[12], * tab1[5]; char * sep; int i; printf("The last two of these should fail\n"); for (i = 0, sep = ""; i < DIM(tab); i++) { tab[i] = new class test; printf("%s%d %s", sep, i, (tab[i] ? "OK" : "ERR")); sep = ", "; } printf("\n"); printf("\n"); printf("Delete three elements\n"); for (i = 0, sep = ""; i < 3; i++) { delete tab[i]; tab[i] = 0; printf("%sDEL %d", sep, i); sep = ", "; } printf("\n"); printf("\n"); printf("The last two of these should fail\n"); for (i = 0, sep = ""; i < DIM(tab1); i++) { tab1[i] = new class test; printf("%s%d %s", sep, i, (tab1[i] ? "OK" : "ERR")); sep = ", "; } printf("\n"); printf("\n"); printf("Should terminate with error NOW\n"); fflush(stdout); class test quit; }
sking@nowhere.uucp (steven king) (08/16/90)
Beware of using something like foo::foo() { if(this) // some sort of error signaling } to prevent automatic creation ("on the stack") ie. main() { foo foobar; } it will also prevent using the class as a base class or using it as a global since in both cases the value passed as _this_ to the constructor will be a valid non null pointer. -- NONE of the standard disclaimers apply =============================================================================== UUCP: ...!cs.utexas.edu!ut-emx!nowhere!sking