
Hello to all Clipper Junks,

If you,ve worked with Clipper a lot, no doubt you will have discovered
severeal undocumented functions in the library. Found them yourself or
mentioned by others. So have I.

I have been working with Clipper since version Autumn '86 (almost a full
decennium now, how time flies!). With the introduction of verion 5.0 (a 
real bitch, full of bugs) OOPs made its entrance with the four well 
know classes: TBROWSE, TCOLUMN, GET and ERROR. It soon became clear to 
me that object oriented programming had a lot of potential so I started 
looking for ways to create my own classes in Clipper. I've used OOP
(one of the first PD libraries), SuperClass (too slow), Numerous 
Class(y) versions, the object engine of FiveWin (get the evaluation 
version, your applications can become real Windows 3.1x/'95 programs) 
and of course OODLES (supplied with the business box of 5.3).

As an employee of a software house I've have worked for some 25 
companies throughout The Netherlands. As most of them had the policy 
not to use any 3rd party software libraries with Clipper I started to 
figure out how to create classes of my own.

After a lot of debugging/decompiling, trying and hanging the computer
I finally found the answer, Clipper had a few functions of its own with
which the job could be done. Without even patching or changing object 
files in CLIPPER.LIB or write functions in C or Assembler.

If you have ever made a list of library functions of CLIPPER.LIB with
tools like LIB.EXE or TLIB.EXE you've probably encountered the 
following ones:

 1.   __CLASSNEW()
 2.   __CLASSADD()
 3.   __CLASSINS()
 4.   __CLASSSEL()
 5.   QSELF()

All ingredients needed to create your own classes. Below you will find 
a discussion of how to use them, what they return and what parameters
they must be passed.

( Now would be a good time to have a printed copy of the examples and 
include files ready, which came with this text ).

1. <nClassHandle> := __CLASSNEW( <cClassName>, <nNrOfInstVars> )

   With function __CLASSNEW() you can create a new class in Clipper.
   It returns the next available <nClassHandle>. A reference to the 
   class. You will need this handle later on to be able to add instance
   variables and messages/methods to your class.

   <nClassHandle> - Unique number within your application, assigned by
                    CLIPPER.

   <cClassName> - Name of the class. As it actually is a function, it 
                  must be unique and no longer than 10 characters long.
                  Watch out with names like: ERROR, GET, TBCOLUMN en 
                  TBROWSE for obvious reasons they are not quite 
                  acceptable as classnames. (Can be used but the 
                  original CLIPPER classes will be no longer 
                  accessible).

   <nNrOfInstVars> - Internally CLIPPER maintains a dictionary of class
                  information. Which classes, which instances belong to 
                  what class, etc. With this parameter you reserve 
                  space for your class in the dictionary.

2a. __CLASSADD( <nClassHandle>, <cInstVarName>, <cInstVarFunc> )
 b. __CLASSADD( <nClassHandle>, <cMessageName>, <cMethodName> )

   The usage of this function is devided into two parts. First you use 
   it to define the instance variables of your class. Secondly to 
   define the messages you can send to your class and methods that
   will handle these messages.

   a. <nClassHandle> - Return value of __CLASSNEW() (see 1).

      <cInstVarName> - Unique name of an instance variable of the 
                       class. ( like:  Cargo, RowPos, ColPos, etc.) 

      <cInstVarFunc> - Name of a unique GET/SET function belonging to  
                       the instance variable. Must be unique per 
                       application, like all other functions you use.

      To be able to create one instanace variable, __CLASSADD() has to 
      be called twice. First with syntax 2a. Second with the same syntax,
      but with an underscore in front of the variable name.

      Example:
          1. __CLASSADD( nHandle,  "nRow", "nRowFunction" )
          2. __CLASSADD( nHandle, "_nRow", "nRowFunction" )

      The first is used to GET the contents of the variable, the second 
      to SET it. You would only use the first name, the other is used 
      by Clipper internally.

      To be able to say for example:
        
                     oLine:nRow := 20

      a GET/SET function has to be created:

      function MyRowFunc( xValue )
         local Self := QSELF()

         if PCOUNT() > 0    /* Must be PCOUNT(), variable can be NIL */
            Self[<nInstVarNumber>] := xValue return Self
         endif
      return Self[<nInstVarNumber>]

      IMPORTANT: <nInstVarNumber> is a sequence number. It points to 
                 a position in your class. Just like an element number
                 in an array. Each instance variable gets its 
                 own number when added to the class.

   b. <nClassHandle> - (See 2a.)

      <cMessageName> - Name of a particular action you want your class 
                       to perform on an object. With this name you 
                       refere to a method (see below).
                       It would be good practice to give messages of 
                       different classes the same name when performing
                       functionally equal actions (huh?) like:
                       oGet:Display(), oWin:Display(), oLine:Display(),
                       oWin:Close(), oDBF:Close(), etc.

      <cMethodName>  - The name of the function Clipper will execute 
                       when you send your object the the above message.
                       
                       So oWin:Display() would for example execute
                       function WindowDisplay().  

      B.T.W.: <cMessageName> and <cMethodName> may be the same, just 
              make sure they're unique in your application. 

      When debugging keep the following in mind: as __CLASSADD() is 
      passed string parameters, messages will not be recognized as 
      functions by the debugger when they are not named after the 
      methods. You will get a runtime error. Without the debugger, 
      however, all will work fine.  


3. <oInstance> := __CLASSINS( <nClassHandle> )

   This function also has two purposes: Adding your class to the 
   internal class dictionary and constructing objects (instances) of 
   your class.

   The first case is obvious: Clipper has to know when you're sending 
   messages which class they belong to and what function it should call.

   In the second case, you generate an 'instance' of a class. You 
   probably won't have lost any sleep wondering about it, but you 
   are doing just that each time you create an object of one of the 
   four Clipper classes. Each variable in your application of type 
   'OBJECT', is an instance of some class, __CLASSINS() is the 
   mechanisme which makes it happen.

   <oInstance>    - Return value of __CLASSINS(), a newly constructed 
                    object of your class.

                    Compare it with: oBrowse := TBROWSENEW(),

                    'oBrowse' is an instance of the class 'TBROWSE'. 
                    Now you can figure out what 'instance variable'
                    means!

   <nClassHandle> - (See 2a.)


4. <aMessageList> := __CLASSSEL( <nClassHandle> )

   <aMessageList> - Return value of __CLASSSEL(), an array with strings
                    of all instance variables and methods of the 
                    selected class. (Strangely enough not in the order
                    they have been declared).

   <nClassHandle> - (See 2a.)


5. <oInstance> := QSELF()

   When using classes and objects in your program, this will be the 
   most frequently called function. It enables you to manipulate 
   values of instance variables inside a method (remember, a method is 
   just a function but with a special purpose). Outside a method 
   QSELF() has no functionality. 

   <oInstance> - Return value of QSELF(), returns a reference to the 
   instance which is sending a message.

   Example:


   Function MyFunc()
   local oWindow := WindowNew()

         oWindow:nTop    :=  5
         oWindow:nLeft   := 10
         oWindow:nBottom := 20
         oWindow:nRight  := 65

         oWindow:Display()
   return NIL

   function WindowDisplay()
   local Self := QSELF()

         DISPBOX( Self:nTop, Self:nLeft, Self:nBottom, Self:nRight )

   return Self               /* Enables message chaining like:
                        oBrowse:GoTop():RefreshAll():ForceStable()
                             */

Basically this is all there is to it. I'm still trying to find out 
how to inherit from Clipper/Own classes. Currently this is not possible.

Mail me if you have trouble understanding any of the above. I will try  
to answer all questions you may have.

E-Mail 100326.1204@compuserve.com
Rene van der Lende, Almere, The Netherlands
