[comp.lang.c++] Classes with limited #'s of instances

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