Calling External Functions

Additional functionality can be added to ABC via external function calls. These calls may be necessary for performance, to call a complex subsystem like a database, to control hardware or use existing software. The primary interface is via C although other languages may be used if the operating system permits. In this chapter, the interface is described in terms of C.

Calling within ABC

The CALL statement is used to call an external function within ABC. An example is shown below.

  createChildFor aWidget:Widget
    { creator -> aWidget.
      widgetid -> CALL Motif(56):Integer. 
    }
This example is taken from type Window and is used to create a window. The windowing system itself is treated as an external subsystem. The subsystem known within ABC as "Motif" is called with the receiver of the 'createChildFor:' message put on the ABC stack followed by the objects listed in the parameter list. Once this function is complete, it will reply with an object. Since ABC can't know the type of the object, the type is supplied via typecasting.

Coding in C

ABC works with an internal subsystem name while the actual C code works with the actual name of a function. This correspondence is provided by editing the file 'home/abc/env/extendop.c'. For example, if your procedure was called Matrix, the portion of the file which must be edited would look like (changes are printed in BOLD):

/* Install your external function in initExternal and call the initialization
   procedure for your external system(if needed).  
   Call the exit function in exitExternal (if needed). */

int     initExternal()
  {
    int	Motif(),XlibGlue(),installExternal(),Matrix(); /*
    int   i;

    for (i = 0; i < MAXEXT; i++)
      ExtTable[i].hash = 0;

    /* install your main routine here */
    installExternal("Motif",Motif);
    installExternal("Xlib",XlibGlue);
    installExternal("Matrix",Matrix);

    /* initialize if necessary */
    if (!SystemMode) {
      MotifInit(); 
      XlibInit();
      InitMatrix();
    };
  }

  /* exit if necessary */
  int     exitExternal()
  {

    if (!SystemMode) {
      MotifExit();
      MatrixExit();
    };
  }
Your source code file for Matrix should look something like:

  #include "dconstant.h"
  #include "dobject.h"
  #include "globals.h"

int Matrix() /* note that there are no parameters */ { /* put your code here */ }

Finally copy /home/abc/env/Makefile to your directory and change the extendop lines and add the matrix lines to read:

extendop.o: extendop.c # co *.c $(CC) $(CFLAGS) *.c matrix.o: matrix.c $(CC) $(CFLAGS) *.c

Also add matrix.o to the definition of ABCOBJS.

You should then be able to type 'make' to build your own ABC image.

Referencing parameters

An example of ABC code to call your Matrix procedure would be:

aMethod { CALL Matrix(5). }

When the CALL statement is executed in ABC, all the parameters in the CALL statement are pushed on the ABC stack and then control is passed to your C procedure. While in your procedure, you may gain access to the parameters via the following macros or procedures:

intparm(n)
gets the nth parameter (0 is the receiver) as an integer

realparm(n)
gets the nth parameter as a real

strparm(n)
gets the nth parameter as a null-terminated string

boolparm(n)
gets the nth parameter as a boolean

ptrparm(n)
gets the nth parameter as a pointer to an object. In this case, you are responsible for retrieving the appropriate parts from the object. These may be obtain by using the GetPtr macro as in

DPointer *p,*q;

p = ptrparm(3); /* get the third parameter */ q = GetPtr(p,5); /* get the fifth part */

You should exercise caution in using these pointers. If you do any ABC memory allocation within your C routines, these pointers may become invalid. In such cases, you should reexecute the above code to get the correct values.

Before your function returns, you must push a value back on the stack which is the REPLY object. You can use the following functions or macros to accomplish this:

pushint(i)
pushes Integer i on the stack

pushreal(r)
pushes Real r on the stack

pushstr(s)
pushes String s on the stack

pushbool(b)
pushes Boolean b on the stack

pushptr(p)
pushes Pointer p on the stack

pushme
pushes the receiver on the stack
To reference this return object, use something like

aMethod { i -> CALL Matrix(5):Integer. }