[comp.lang.c++] use of parameterized classes

vaughan@puma.cad.mcc.com (Paul Vaughan) (06/28/90)

Newsgroups: gnu.g++
Subject: libg++ usage
--text follows this line--

In libg++ (the c++ library of classes and parameterized classes
distributed with gnu c++), there is a parameterized class known as
SLList that implements a simple linked list data structure.  To create
an instance of a parameterized class such as SLList, one uses
genclass-- a simple tool that replaces type place holders in a
prototype class definition with a real type name.

I use a lot of SLList's from libg++ that all hold simple pointers.
However, instead of genclassing a new SLList type for each pointer
type, I genclass one voidpSLList and then implement lists for other
classes by making a simple interface file that casts the arguments of
input and output functions to the appropriate pointer type.  For
instance:

(Note the following excerpt could probably be considered a Derived
Work of libg++ and thereby fall under the GPL.  This is just an
excerpt, however--consider it a quote with words changed, added, and
removed. (kinda silly, huh? :-/))

	#include <voidp.SLList.h>

	class Foo;
	typedef Foo* Foop;

	class FoopSLList
	{
	protected:
	  voidpSLList l;

	public:
	  FoopSLList() {};
	  FoopSLList(FoopSLList& a) : l(a.l) {};
	. . .

	  void                  join(FoopSLList& sl) { l.join(sl.l); };

	  Foop&             operator () (Pix p) { return (Foop&) l(p); };

	 . . .


This works out fairly well.  All the functions for FoopSLList are
defined inline and the whole thing adds about 150 bytes to a .o file
when it is included in the source, above the cost of including a
normal Foop.SLList.h.  So I pay a small price for the explicit
conversions, have only one full implementation of voidpSLList,
eliminate a lot of .cc and .o files.  Plus, making a new one just
involves creating one new file and doesn't require modifying the
Makefile.  It also collects all the casting into one place where it's
straightforward to verify that it is correct.  It adds up to a
significant savings by eliminating many Class.SLList.o files from the
executable.

So, I thought I'd apply this technique to Maps and Plex's and Queues,
etc.  This did not work out because these classes in libg++ all have
virtual functions.  It turned out that replacing two Plex (generic
class interface definition with no implementation) and XPLex (an
implementation) types both holding simple class specific pointers
with one voidpPlex and voidpXPlex and four simple conversion .h files
increased my executable size dramatically (over 1 MegaByte!), because
of the proliferation of virtual function tables.

So, I'm just offering this bit of experience to the pool, with the
hope of gaining a better understanding from any followup discussion.


Comments:

I've never quite understood why these classes should have virtual
functions (or be split into two classes, eg. Plex and XPlex).  I've
never had an occassion to use a FoopPlex* instead of a FoopXPlex*.
It's generally pointless to write code that's completely independent
of the implementation, because it usually has to arrange for the
creation of the thing, and thus know exactly what type it is
anyway--so the .h file is necesarily already included.	

The .h file and virtual function table explosion problem has almost
led me to stop using direct instances of objects such as these in
classes.  For instance instead of

#include <Foop.VHMap.h>
class Bar {
protected:
  FoopVHMap map;
...

I'm tempted to use

class FoopVHMap;

class Bar {
protected:
  FoopVHMap& map
...


and arrange for the reference initialization and destruction in the
constructors and destructors.  This is not as convenient, and does
more dynamic memory allocation (of smaller pieces) and so seems kind
of silly, but I expect it to help the modularity problem.  Any
thoughts on this?


 Paul Vaughan, MCC CAD Program | ARPA: vaughan@mcc.com | Phone: [512] 338-3639
 Box 200195, Austin, TX 78720  | UUCP: ...!cs.utexas.edu!milano!cadillac!vaughan