                             
                             
                             
                             
                             
                             
                             
                             
                             
                             
                             
                             
                             
                             
                             
                             
                             
                             GCOOPE Version 1.0

    Generic C-language Object Oriented Programming Environment Version 1.0

                              by Brian L. Price

                     Released as Public Domain July, 1994.



                                   Table of Contents


            I. Introduction to Objects....................................1
               A. Object-ively Defined....................................1
               B. Genericity isn't just for canned goods..................2
               C. C. Inheritance without genetics.........................3
               D. Introducing GCOOPE......................................3

           II. The GCOOPE Environment.....................................5
               A. Object handles..........................................5
               B. Class variables.........................................6
               C. Instance Variables......................................7
               D. Methods.................................................7
               E. Function Dispatching....................................9
                  1. 1. g is the Name.....................................9
                  2. The Smalltalk Connection............................11
                  3. Local method caching................................11
                  4. Steering the instance...............................13
               F. Classes and Pseudo Classes.............................14
               G. Flag Classes...........................................15

           III. Creating a class definition..............................16
                A. Layout of a class definition module...................16
                B. Method definition section.............................16
                C. Installation routine..................................17

           IV. Expansion and Growth......................................18


           Appendices

            A. Appendix A. The GCOOPE USER'S GROUP.......................19
            B. Appendix B. About the Author in case you're really bored..20

























                                           i



                            GCOOPE Version 1.0 User's Guide

                             By Brian L. Price July, 1994

           I. Introduction to Objects

                A. Object-ively Defined

                          Before diving into GCOOPE, let us take a few
                     moments to examine the concept object.  My pocket
                     dictionary provides a starting point with two
                     interesting definitions:

                          1.  Object - Something serving as a focus of
                               attention or ACTION.

                          2.  Object - (Grammer) A noun that receives or is
                               affected by the action of a verb....

                          An object is first of all a thing, something that
                     exists in a perceptible form, in other words data.
                     That data is the focus of actions, it is changed,
                     translated, or simply accessed by a verb (method).

                          If we consider the verb to be a method for
                     performing an action, and that method is described as a
                     computer algorithm, we have the 'active' portion of the
                     definition.

                          Another way of saying object is active data.  Now
                     how do we apply this concept to computer programming?
                     We must first lay out some useful principles.  Lets
                     borrow some tried and true programming principles as a
                     start:

                          1.  Modularity - modular code is self contained
                               (at least as much as possible), and it has a
                               well defined interface to the outside world.
                               Since our object concept is already a
                               seperate entity from its surroundings,
                               enforcing the principle of modularity helps
                               to keep it that way.

                          2.  Orthagonality - The set of methods that we
                               define to act upon our object's data should
                               be orthagonal.  In other words if you define
                               an add operation, you must define a subtract
                               operation.  This goes hand in hand with our
                               first principle.  If our set of methods is
                               complete and orthagonal, then there is no
                               need for outside intervention.

                          So our concept of object now consists of a piece
                     of data that can be acted upon by an orthagonal set of
                     methods with a well defined interface, and all this is
                     contained as a seperate entity from its surroundings.
                     Lets call this encapsulation.

                                           1



                          Now, by itself, our encapsulated object concept
                     isn't more powerful than procedurally written code.
                     (Although it would be easier to debug.)  We need a few
                     more concepts to build upon what we already have.


                B. Genericity isn't just for canned goods.

                          If you have ever been short on cash, or frugal,
                     you have probably purchased a generic product.
                     Something that is generic is the same as any other
                     thing of the same type or class, at least in all the
                     functional ways.  A generic item can be substituted for
                     any other item of the same type.

                          What if we develop a set of encapsulated objects,
                     a subset of whose methods are remarkably similar in
                     function, lets say that those methods are math
                     functions.  Now if one of those objects has integer
                     data and another has floating point data,  an add
                     operation performs the same type of action on both
                     objects.  Thus we could say that in this case add is a
                     generic action.

                          If we provide a suitable method for detecting
                     which object should be called depending upon the data
                     type, we can develop a generic function add.  It won't
                     matter if its operand is of type float or integer
                     because the correct object method will perform the
                     actual operation.  Thus we have the concept of a
                     generic function label.

                          This concept makes our object concept a bit more
                     powerful.  Now, wherever we have similar operations
                     performed on different types of data, we can define one
                     label which we can then use for that operation no
                     matter what the actual data type is.  We have
                     simplified our interface, since there are fewer
                     function labels required.

                          More importantly, we no longer have to know the
                     exact type or structure of the piece of data we are
                     manipulating, all we have to know is that the operation
                     we want to perform is a valid one for that object.  To
                     simplify if the desired action is filling a basket with
                     fruit, do we really care if it gets filled with peaches
                     in place of tangerines?  Genericity gives us a degree
                     of data independence.










                                           2


                C. C. Inheritance without genetics

                          We now have encapsulated objects with generic
                     methods, what could make this concept more powerful ?
                     In traditional programming we push complexity down the
                     procedure chain.  The real work gets done in seperate
                     functions that we define to do a job.  This allows us
                     to concentrate on the big picture without being
                     overwhelmed by details.  The layout of a program looks
                     like an upside down tree structurally.

                          What if we have written an object to do a simple
                     task and now we want to do a more difficult task that
                     has the simple task as a necessary sub-task.
                     Traditionally, we would call a subroutine.  We could do
                     that with our objects, but is there a better way ?

                          Consider the upside down structure tree of the
                     traditional program to be a family tree and the answer
                     is obvious.  What if we could define a new object that
                     would automatically gain all the capabilities and data
                     structures of the old object ?  This is the concept
                     called inheritance.  If we define a child object with
                     the old object as its parent, we can gain all its
                     abilities (methods) through inheritance.

                          Can we do better ?  Sure, we can allow multiple
                     ancestory ie. more than one parent, we can allow
                     partial ancestory ie. gaining only a subset of the
                     parent's capabilities, and we can redefine the parent
                     capabilities adding our own structure and actions on
                     top of what we inherit.

                          Inheritance lets us both change and build upon the
                     characteristics of the parent.  This concept is made
                     even more powerful by our earlier insistance on
                     encapsulation.  The parent can be changed to adapt it
                     to new requirements and as long as the original
                     interface and capabilities are not changed or removed,
                     it makes no difference to the child object.


                D. Introducing GCOOPE

                          We have reached the point where we have developed
                     a quite powerful and useful concept.  Now how do we
                     apply it ?

                          Brute force is always a good first shot,  so we
                     could write a program module that would define a data
                     type or structure and methods (ie functions) to perform
                     actions upon it.  Well, we'll probably need more than
                     one so we'll have to create and track each....
                     INSTANCE!  Hmm, we have a set of these data regions or
                     instances, and we need only one set of methods to act
                     upon them since they are all members of the same,...
                     CLASS!


                                           3


                          Okay so we have this set of methods and data
                     structure definitions for a class of object, lets call
                     this the class definition.  We need some sort of
                     instance tracking database, lets use a variable to
                     represent each instance.  Call it type object, so I
                     guess the variables would be OBJECT HANDLES.

                          There's the first step, now lets incorporate the
                     concept of genericity.  We need some way of tracking
                     all these methods for each of the .... GENERIC (shape
                     changing) functions.   Heck, we need some way of
                     tracking all these generic function labels.  Routing
                     all these generic calls is going to be as bad as
                     running a taxi service in Manhattan, we need a ....
                     DISPATCHER to tell them where to go.

                          Now for inheritance, we'll just add our ancestor's
                     methods and instance memory to our own, of course we
                     need a way to add our own methods that will overwrite
                     any inherited methods for the same generic, and we have
                     to keep track of which instance of the parent class
                     belongs to our own instance,... you know we need a
                     couple of object classes just to do all these things.

                          At this point GCOOPE politely knocks at your door
                     offering its services.  GCOOPE has lists to track
                     object classes and instances, it has a powerful
                     function dispatcher and instance tracking mechanism, in
                     addition it addresses problems we haven't even
                     considered yet in this discussion.  Topics like default
                     destructors, temporary object deletion, error handling,
                     class definition objects, and more.  Best of all GCOOPE
                     is written in and for the C programming language, bare
                     metal power cloaked in an object friendly environment.

























                                           4


           II. The GCOOPE Environment

                     In this section, we will explore the strengths and
                weaknesses of the GCOOPE system and its implementation.
                GCOOPE is a powerful system but there are pitfalls and costs
                in using it.  GCOOPE, like most other real world systems, is
                a product of choices between often conflicting goals and the
                resulting system is defined by those design choices.


                A. Object handles

                          The data type object, defined in both pcstruct.h
                     and GCOOPE31.h, is primarily used as a 'handle' for an
                     object class or instance.  The type object is defined
                     in such a way that it is always at least 32 bits in
                     size.  It is normally defined as a long integer.

                          For practical purposes, you can consider the
                     object handle to be the object although in actuality it
                     serves as a special type of index/pointer with internal
                     structure members.  For the actual details of the
                     object handle structure, consult the T.R.M. (Technical
                     Reference Manual).

                          A secondary use of the data type object is to
                     return values from a method function.  When used in
                     this manner, it should be cast as the appropriate data
                     type if it is being used to return something other than
                     an object handle.  See the basic class libraries and
                     the gcoope10.h file for assistance macros.




























                                           5


                B. Class variables

                          Class variables are variables or data structures
                     that are accessible to all instances of that class.
                     They are static variables that are the same for all
                     instances of the class.

                          The structure for a class variable area is defined
                     in the class definition, each class can have at most
                     one class variable structure/area.  Another way of
                     stating this is to say that the class variable
                     structure area is a shared memory area private to class
                     members but shared among those same members.  All class
                     variables must be accessed as members of the class
                     variable structure.

                     WARNING: In GCOOPE it is possible to access the class
                          variable area of another class.  DON'T.  This type
                          of practice defeats the purpose of encapsulation.
                          If a change occurs to the structure in the owning
                          class, you are in the same situation that you
                          would be in with C++ friends, or normal C code,
                          namely code re-write and re-compile time!

                          In order to access the class variable area use the
                     public kernel function, getCVptr.  Full details of this
                     function is given in the T.R.M.  The class variable
                     area is automatically created when the class definition
                     is installed.

                          When a class definition is installing itself as an
                     instance of class 'Class', the size of the class
                     variable structure must be passed as the cvSize
                     parameter.

























                                           6


                C. Instance Variables

                          Instance variables are variables or data
                     structures that belong to an instance of a class.  This
                     is a private static memory area for the instance.  Each
                     instance of a class will have a separate instance
                     variable memory area.

                          The structure for an instance variable area is
                     defined in the class definition.  Each class may have
                     at most one instance variable structure.  All instance
                     variables must be referenced as members of that
                     structure.

                     WARNING:  In GCOOPE, it is possible to access the
                          instance memory for another class, or ancestor
                          instance.  DON'T.  This practice makes your code
                          dependant on the internal structure of those other
                          modules, it defeats the entire purpose of
                          encapsulation.  Using this type of practice will
                          result in code that is full of side effects and
                          cross dependancies.

                          To create the instance variable memory area, you
                     must use the public kernel function makeInst.  Full
                     details of this function is given in the T.R.M.  This
                     function should only be called within the constructor
                     (New) method of a class.  It must be called prior to
                     any ancestor constructor calls.

                          To access the instance variable memory area, you
                     use the public kernel function getIVptr.  Full details
                     on the use of this function is given in the T.R.M.

                          When a class definition is installing itself as an
                     instance of the class 'Class', the size of the instance
                     variable structure must be passed as the parameter
                     ivSize.


                D. Methods

                          The generic GCOOPE method prototype is defined as:

                               typedef object (*method)(object,...);

                          A function of type method takes at least one
                     parameter of type object and returns a type object.
                     The first passed parameter must always be an object
                     handle of type object.  Additional parameters may be
                     passed.








                                           7


                          In our method type definition, you may observe the
                     use of the variable argument parameter ...  This is
                     handy because we can use that one type method no matter
                     how many parameters are actually required by an actual
                     method.  This does present some dangers.

                          First, passing a pointer as a variable argument
                     parameter is like running barefoot through a minefield.
                     Second, passing different data types in a variable
                     argument list is like adding machine gun and mortar
                     fire to that minefield.  Unless your name is Superman,
                     you will take serious hits.

                          At first glance, it seems we've put ourselves in a
                     dilemma, we have a powerful abstraction that is too
                     dangerous to use, right?  Wrong.  Why are you passing
                     pointers to a method in the first place ???  You should
                     be passing object handles!  Object handles are much
                     safer because they undergo some error checking and
                     their internal structural integrity is verifiable.  In
                     fact, you can add even more error-checking on top of
                     the built-in checks in your class definitions.

                          As for multiple data types, we already have a type
                     object which can be used to pass almost any other data
                     type.  Why not use it?  If you only pass one parameter
                     in the variable argument field and if you are careful
                     in your design of object class definitions, it is
                     convient and fairly safe to pass a pointer or other
                     data type.  However, with multiple parameters, take
                     heed.

                     1. RULE #1
                               When writing a method for use by an
                          applications program, if more than one parameter
                          is to be passed in the variable argument portion,
                          always use type object for all variable argument
                          list parameters, and always use an object handle
                          to an object instance rather than any pointers to
                          memory areas.
                               Another solution is to use the experimental
                          strong typing option.  See typing.h.  The best
                          approach is probably to use a combination of the
                          above advice, judicious use of strong typing and
                          when in doubt be sure and use the return value
                          typedefs.













                                           8


                E. Function Dispatching

                          The function dispatching and instance tracking
                     system in GCOOPE version 1.0 is far more sophisticated
                     than in the earlier.  Used correctly, they can make
                     your programming chores easier, used incorrectly you
                     will need a good debugger.

                     1. 1. g is the Name

                               The function g is GCOOPE's function
                          dispatcher, its usage is deceptively simply.  Even
                          though complete details are given in the Technical
                          Reference Manual for the GCOOPE kernel, I'll
                          repeat the usage info here.

                          returnObjectValue = g(generic)(objectInstance,
                                                        [methodParms,...]);

                               Where: returnObjectValue is a return value of
                          type object that is returned from the dispatched
                          method; generic is the generic function value of
                          type generic; objectInstance is the object type
                          object handle of the instance or class being acted
                          upon; and the optional methodParms,.... are the
                          parameters passed to the method.

                               If you want the details of its operation, see
                          the Technical Reference Manual, here, just take
                          for granted that it works as advertised.

                          WARNING: SEE RULE #1

                               1.    Incorrect casting of the return value
                                    either in the calling routine or in the
                                    method executed.  32 bit values are
                                    always passed, however depending on the
                                    method, all 32 bits may not be valid.
                                    Be sure to clearly specify in the class
                                    definitions what values are being
                                    returned by their methods.  Also,
                                    whenever possible all methods with the
                                    same generic name should return the same
                                    data type.  Otherwise you will be using
                                    the alternate definition of generic.














                                           9


                               2.    Passing the wrong type in the method
                                    parameter list.  GCOOPE is a weakly
                                    typed system, method parameters are NOT
                                    type checked, so stay alert.  Wherever
                                    possible in your method definitions be
                                    sure and validate the recieved
                                    parameters.
                                         If possible, use the experimental
                                    strong typing option.  It DOES provide
                                    type checking for method parameters,
                                    although it is a bit awkward to use.

                               3.    Passing a char or int type as the
                                    objectInstance parameter instead of an
                                    object type.  Can you say "abnormal
                                    program termination" ?

                               It cannot be stressed enough, GCOOPE is
                          weakly typed, this is both a blessing and a curse,
                          a strength and a weakness.  If you are the type of
                          programmer that commonly expects the compiler to
                          catch or correct your type casting, I strongly
                          suggest that you use the strong typing option and
                          use the return value typedefs.

                               Another point to watch for: with variable
                          argument prototyping such as that used by GCOOPE,
                          it is tempting to define methods for the same
                          generic function that take a different number of
                          parameters or totally different data types as
                          arguments.  DON'T.  That is asking for trouble.

                               The only exceptions to this are the  generic
                          functions New and Err, the definition of these
                          functions require that you use different numbers
                          and types of parameters.  Just be careful and
                          don't carry the practice any further than
                          absolutely necessary.


                          TIP:       While it is handy to use the same
                               generic    function for many similar methods,
                               make sure that the methods truly are similar
                               enough to justify it.  Not only are you
                               tempted to use different parameter types or
                               numbers as noted above, but you will also be
                               slowing down your program when you improperly
                               apply methods to generics.  generic functions
                               with large numbers of methods are slower in
                               dispatching.









                                          10


                     2. The Smalltalk Connection

                               If you have previous experience with
                          Smalltalk, GCOOPE will smell like home.  There are
                          a few snakes in the garden however.  Smalltalk
                          makes regular use of trivial methods, methods such
                          as +.  While you may define the equivalent Plus
                          and the like generics in GCOOPE, don't use them
                          where execution speed is important.

                               You will have noticed that GCOOPE methods
                          execute an order of magnitude faster than
                          Smalltalk methods.  This is because GCOOPE methods
                          are compiled C code while Smalltalk methods are
                          interpreted code.  What may not be so readily
                          apparent is that GCOOPE's function dispatching is
                          very similar to Smalltalk's and so it is not much
                          faster.   What happens with 'trivial' methods is
                          that the execution time for function dispatching
                          dwarfs the execution time for the method.  In
                          other words, GCOOPE will slow down to near
                          Smalltalk speeds when executing these types of
                          methods.

                               When speed is critical, and you only need to
                          call such a method once or twice, do these
                          'trivial' methods locally where possible.  For
                          another idea see the next section.

                               All in all, the use of Smalltalk techniques
                          is generally a very good idea, GCOOPE is very
                          close in concept to the idea of compiled
                          Smalltalk.  They share many of the same strengths
                          and mannerisms.  One trap to avoid is forgetting
                          that GCOOPE has more capabilities with regards to
                          inheritance than Smalltalk does, so don't miss out
                          on reaping those benefits.


                     3. Local method caching

                               If you are going to call the same method more
                          than once or twice in the same statement block or
                          routine,  you can drastically decrease the
                          execution time of the dispatching process.  Just
                          use the following call:

                               methodToCall=p(generic, instanceObject);

                          Where: methodToCall is a method type variable used
                               to hold the method address; generic is the
                               generic function index; and instanceObject is
                               the object type object handle for the
                               instance.





                                          11


                               For this technique to apply, instanceObject
                          must remain constant, or must be a non-inherited
                          flag class instance of the same constant class.
                          (flag classes will be explained in detail later,
                          also see the T.R.M.)

                               Also if instanceObject is not a non-inherited
                          flag class, you will have to make the following
                          call prior to the one above:

                          instanceObject = steer(ClassOfMethod,
                          instanceObject);

                               This will set the instanceObject's instance
                          tracking variable to correctly point to the
                          desired instance data.

                               With all the above limitations, this
                          technique will not be used often, it can however
                          be used in some cases to get around the 'trivial'
                          method penalty described in the previous section.

                               PROGRAMMER'S NOTE:  The best solution to the
                          need for speed in function dispatching is to
                          rewrite the dispatcher so that it does not make
                          any function calls.  Then if writing a platform
                          dependant version, you can drop to assembly and
                          hand-optimize the result of the first step.  This
                          is the path I used in tests of PCOOPE version 2.1
                          and while the dispatcher has changed quite a bit,
                          the new dispatcher should be capable of reaching
                          comparable speeds.  (In 2.1 the speed increase was
                          quite remarkable (try >10x) and well worth the
                          effort.)  If you have a copy of version 2.1
                          examine the dispatcher routine for hints.
























                                          12


                     4. Steering the instance

                               In the section above you caught your first
                          glimpse of the function steer.  It is used to
                          'steer' the instance tracking mechanism so that
                          makeInst and getIVptr will work correctly.
                          Normally this is done automatically by the
                          dispatcher.  However there are certain instances
                          where you must call steer from within your class
                          definitions.

                          a. RULE #2
                                    The rule for the need for a manual call
                               to steer is: any time you are calling a
                               generic function that will dispatch to a
                               method that is defined in an ancestor of the
                               current class, you must use manual steer
                               -ing.  Prime examples of this situation occur
                               in methods for the generics New and Kill (and
                               to a lesser extent in Err).  The procedure is
                               fairly simple:

                               retVal=g(generic)(steer(ancestor,
                                                        instance),[parms]);

                               Where: ancestor is the class object handle
                                    for the ancestor class; and instance is
                                    the object handle for the current
                                    instance.

                               This situation occurs because the current
                          instance is used by the dispatcher to determine
                          what the current class is.   If the current class
                          and an ancestor class have methods for the same
                          generic, you could never reach the ancestor method
                          because the dispatcher would call the current
                          class method.  The above call 'fools' the
                          dispatcher into believing that the ancestor class
                          is already the current class and so the call goes
                          through.  Also see the macro ST() in gcoope10.h.

                               Some commercial products similar in concept
                          to GCOOPE initialize the ancestor classes for you
                          prior to calling the child class's method for New.
                          The problem with this is, that either you end up
                          with dozens of parameters in the call (to pass all
                          the ancestor initialization parms and don't ask me
                          how you track that with multiple ancestory) or you
                          don't have any control over ancestor
                          initialization. Frankly, either case is
                          unacceptable.








                                          13


                               The above solution isn't what I sought, but
                          it is usable and achieves the desired purpose.
                          The only other solutions I could find added way
                          too much complexity to the kernel code and still
                          didn't solve the occasional problem of calling an
                          ancestor method outside of New or Kill.   You
                          would have a bigger, slower function dispatcher,
                          and still need the steer routine!

                               You will also need to use the above technique
                          in your Kill methods wherever they occur, for
                          exactly the same reasons.  Another place where you
                          may find yourself in need of the above solution is
                          when you redefine an ancestor method in a way that
                          adds to the inherited method.  In other words,
                          where you still need to call the ancestor method.

                               Another way to look at the above is, if you
                          don't know the ancestor class you don't need to
                          call steer.  Applications programs should never
                          need it.

                               Performance wise, the extra call doesn't hurt
                          you very much.  First because the dispatcher won't
                          have to go through a steer procedure, and second
                          because it will only occur normally in the
                          constructor, destructor, or error handler portion
                          of the class definition.  New and Kill methods are
                          one time cost routines per object instance and if
                          your code is calling Err very often something is
                          wrong <GRIN>.


                F. Classes and Pseudo Classes

                          We have not yet reached the point where we are
                     ready to begin writing class definitions, but in order
                     to understand some of the next section, you need to
                     introduce yourself to some of the concepts.

                          Classes are comprised of an optional class
                     variable structure definition, an optional instance
                     variable structure definition, method definitions, and
                     an installation function.

                          Pseudo classes are classes that cannot be properly
                     inherited, they have no class or instance variable
                     structure definitions, although they may make use of
                     structure definitions defined internally or externally.
                     Pseudo classes may define methods, although they don't
                     have to.  They may also define non-generic functions.
                     They must include an installation procedure.







                                          14


                          An installation procedure installs the class or
                     pseudo class definition into the GCOOPE system.  It is
                     responsible for inheritance and genericity.  For a
                     class definition, it is also the place where class
                     variables may be initialized.

                          Non-generic functions are simply standard C
                     library type functions instead of generic function
                     methods.

                          Class definitions are where objects are defined,
                     pseudo class definitions interface to the platform
                     and/or hardware.  Pseudo class definitions are also
                     used to expand GCOOPE's kernel adding necessary or
                     optional functionality.  The pseudo class concept was
                     developed because of a desire to treat kernel expansion
                     in a modular way that could make use of kernel
                     resources used by normal class definition modules.


                G. Flag Classes

                          The last topic to cover in the GCOOPE environment
                     is the flag class.  A flag class uses a portion of the
                     object handle as an index to its class object.  This
                     means that the lower 16  bits of the object handle are
                     no longer needed by the GCOOPE system.

                          A flag class uses those 16 bits as its instance
                     variable storage area.  Thus, for the overhead of a
                     simple flag check, flag classes can implement classes
                     such as Boolean, Char, Int, etc. with no instance
                     memory being dynamically allocated!  For full details
                     of the object handle structure see the T.R.M.

                          A flag class is subject to certain limitations.
                     First, it cannot inherit any class which has instance
                     variables.  Second, it must check to see if it is the
                     owner of the instance object handle in each method
                     prior to accessing the instance variable.  Third the
                     New method for such a class must be dual purpose.  If
                     it is being inherited, it must behave exactly as a
                     normal class.

                          Basically, the flag class must be careful to check
                     whether or not it has been inherited with respect to
                     the current instance.  If it has been, it must behave
                     exactly like a normal class.  (Note: a flag class's
                     ivSize must be given as sufficient to store its
                     variable type.)  Before attempting to create a flag
                     class definition, it is strongly suggested that you
                     study the relevant portions of the T.R.M. closely and
                     also be sure to check the example code provided.






                                          15


           III. Creating a class definition

                A. Layout of a class definition module

                          The first statement in a class definition is
                     #define CLASS <className>.  This names the class and
                     becomes the symbol string name for it.  The second
                     statement is #include "GCOOPE31.h" and the third
                     statement is object CLASS;.

                          Following those statements should be your
                     structure definitions for the class variable and
                     instance variable structures.  These structures are
                     optional.  Hint: instead of creating them as a named
                     structure, typedef them as a structure.

                          Next should be the class constructor method
                     definition, followed by any user defined method
                     definitions and by a destructor and error handling
                     method if desired.

                          The final portion of the class definition is the
                     class installation function definition.


                B. Method definition section

                          The only method you must define is a constructor.
                     It should call makeInst and then call the constructors
                     of each superClass by:
                        p(New)(steer(superClass,instance),[initParms]);

                          Other than that, you're on your own as far as
                     methods go.  Just make sure they all fit the following
                     prototype:  retValType methodName(object,...);.

                          A few points to remember however, if you
                     dynamically allocate memory within your object:

                          1.    use the kernel memory management shells
                               s_calloc, s_free, s_malloc, and s_realloc.

                          2.   write a method for Kill that frees the memory
                               and calls p(Kill)(steer(superClass,
                               instance)); for each superClass.  (immeadiate
                               parent) and ending with p(Kill)(Object,
                               instance);.












                                          16


                C. Installation routine

                          The name of the class installation routine MUST be
                     given as the macro CLASS_INSTALL.  This insures
                     compatibility with the class installation macros.  The
                     first statement after any local variable and pointer
                     declarations is:

                     CLASS =
                       p(New)(Class, sizeof(classVarStruct),
                              sizeof(instanceVarStruct), [SuperClass object
                              handle list], END);

                          This creates the class definition, you may now
                     optionally get a pointer to the class variables area by
                     getCVptr and initialize the class variables.

                          The next step is to add each method, this is done
                     by:
                       addPMthd(CLASS, genericName, methodAdr);

                          You can also block ancestor methods for a generic
                     via rmvPMthd (see T.R.M.).  This is useful for ancestor
                     methods which should not be directly called except from
                     a method defined by this class definition.

                          A note here about inheritance, the call to
                     p(New)(Class,...) will setup a description of an
                     instance memory block for instances of this class.
                     That description includes the instance memory blocks
                     for all ancestors.  After performing that setup, the
                     class creation method will add ALL the ancestor methods
                     to the current class automatically.  The order that you
                     specify superClasses (immediate ancestors) is
                     important.  Those given first in the list will take
                     precedence over later entries.  Their methods will
                     overwrite any conflicting or overlapping methods for a
                     generic.  When you use addPM or rmvPM you will
                     overwrite or remove any listed ancestor method for the
                     generic in question.



















                                          17


           IV. Expansion and Growth

                     In the short documentation with the PCOOPE version 2.1
                release, I foolishly stated that the interface was written
                in stone and that all expansion and growth would occur
                through the class definitions.  Alas, as so aptly put by the
                first user's group member, G/PCOOPE isn't ready for that.
                He was 100% correct.

                     In version 1.0 I have attempted to write the kernel in
                an expandable manner.  That is one reason why the provided
                code is optimized for little else than debugging.  For each
                target platform, an optimized version of the appropriate
                kernel module(s) should be created.  Low level functions
                that prove necessary can be added via the pseudo class
                expansion module route.  In the design of this system, I
                have attempted to provide for the possiblity of future
                expansion at each level of the system.

                     GCOOPE has potential as an application framework.  For
                that potential to be realized standard, generic, well
                defined, and expandable interface specifications need to be
                developed.  Available public domain libraries that help to
                meet these needs should be incorporated as expansion
                modules.  No doubt, some custom code will have to be
                written.  The GUI portion alone will prove quite an
                undertaking, if GCOOPE is to realize its potential a GUI
                specification must provide a path to compatibility with
                Windows, X-windows, and other popular GUI environments.

                     GCOOPE also has potential as an object oriented
                operating system extension.  In this concept, the GCOOPE
                kernel would be a system resource with instantiated memory.
                Such an extension would have to include the ability to share
                dynamically loaded modules.  This type of extension to the
                GCOOPE framework is not as difficult as it might sound at
                first, GCOOPE could be made available as a process on a Mach
                3.0 or QNX microkernel.  There would, no doubt, be many
                additional demands placed upon the GCOOPE system however.

                     As of this writing, GCOOPE is in its infancy, at the
                very least it serves as an exploratory framework into the
                next generation of object oriented programming languages,
                application frameworks, and operating systems, at the most
                .... you be the judge.  Or better yet, join the GCOOPE
                user's group and join in the expansion and implementation of
                the GCOOPE system.












                                          18


           Appendix A. Appendix A. The GCOOPE USER'S GROUP


                          JOIN THE GCOOPE USER'S GROUP!


                For a one year membership send check/money order in the
                amount of $20.00, (made payable to Brian Lee Price) to the
                address below:

                          Brian Lee Price

                          GCOOPE USERS GROUP

                          RD2 BOX239AA

                          CLEARVILLE, PA.  15535

                    Please include my middle name to avoid local post office
                    difficulties :).


                     User's group members receive the latest updates to the
                GCOOPE system, new class libraries, expansion modules, etc.
                Limited free technical support over the phone is available,
                and access is granted to a User's group only private limited
                access bbs, you will be added to the quarterly newsletter
                mailing list, and other services are available.

                     It is my hope that the user's group members will
                participate in the growth of the GCOOPE system, expanding
                its libraries and its capabilities.  This way, all user's of
                the framework will benefit from a degree of co-operation
                that is often impossible to achieve with a commercial
                product.
























                                          19


           Appendix B. Appendix B. About the Author (in case you're really
           bored)

                     The author is a starving artist type, currently
                unemployed and living in the mountains of south central
                Pennsylvania (Hi-technology's equivalent of Bumf---, Egypt).
                First introduced to computers after a tour in the USAF as a
                television studio technician in 1982.  First programming
                experience in assembly language on Intertec (who?)
                SuperBrain II CP/M and Atari 800XL machines.

                     Catching the PC wave, the author worked throughout most
                of the 80s as a computer technician/consultant ending up
                working in the computer evaluation laboratory of Entre'
                computer corporate where he was allowed to play with early
                networks, CAD/CAM, and the first 386 machines.  For the late
                80's and early 90's the author was a self employed
                electronic systems and circuit designer/consultant having
                some success with early video frame digitizer designs for
                the Apple Macintosh II before they butchered its expansion
                slot capacity.

                     The last few years spent working with embedded systems
                such as traffic control computers and monitors and playing
                around with computer languages, the author began his
                programming career in C in 1993.  GCOOPE is his first
                publicly released software package.  When not working on the
                GCOOPE project or his (t)rusty '70 Ford F-150, the author is
                working with local business and political interests to
                establish a free public access bbs and Internet link in his
                home county.

                     Anyone need the above talents for a paid job???


























                                          20

