10.2 Using the external Fragment

A program using the external fragment will have the following structure:

INCLUDE '~beta/basiclib/external
--- program: descriptor ---
(# foo: external(# ... #)
do ...
   ... foo ...
#)

10.2.1 Interfacing to External C Functions

When interfacing to C, the pattern callC must be called in the do-part of the External specialization. The BETA compiler will then generate a call to an external routine with the same name as the BETA pattern, using C's style of passing parameters.

A pattern of the form

foo: external
  (# enter ... do callC exit ... #)

describes the interface to an external C function with entry-point foo(_foo). (The do-part can be left out.) As a convenience, the call to C above need not be specified in which case the compiler will insert it automatically.

If you prefer to give the external a different name from the entry-point name, you can state the entry-point name explicitly. If the entry-point name contains special characters, you are forced to do this:

foo: external
  (# ... enter... do 'X$bar' -> callC exit... #);

This pattern describes the interface to an external C function, whose entry-point name is X$bar (and not foo).

It is important that there exist a C function with the same name and exactly the number of enter parameters corresponds to the number of parameters of the C function. If the C function returns any results, it is important that an exit parameters is specified in the BETA external pattern, and that this exit value is evaluated in all usage's of this external (due to an error in the current compiler) If the C function does not return any result, no exit parameters may be specified.

10.2.2 Using call back from C

The following example shows how to install call backs from C to BETA. Readers not familiar with call backs should skip this section.

We use declarations like:

callBackProc: external
  (* This pattern describes the interface to the procedure
   * that is called on the call back. It may have the
   * following type definition in C:
   *
   *   typedef void (*callBackProcPtr)(int i)
   *)
  (# i: @integer;
  enter i
  (* only the types shortInt, integer, char and boolean
   * can be used in the enter and exit parts
   *)
  do cExternalEntry;
     inner;
     (* Had the return type not been void,
      * the exit part should have appeared here.
      *)
  #);

installCallBack: external
  (* This pattern describes the interface for the C
   * function that installs the call back.
   * It has the following C description:
   *
   *   void installCallBack(callBackProcPtr theProcPtr,int j)
   *)
  (# theProcPtr: ##callBackProc;
     j: @integer;
  enter (theProcPtr##,j)
  do callC;
  #);

When writing the actual procedure to be called on the call back, it is easiest to specialise the above callBackProc pattern, as in:

(# ...
  myCallBack: callBackProc
    (# ...
    (* do not specialize the enter part *)
    do 'There is a call back.' -> putline;
       'Value received in parameter i is ' -> puttext;
        i -> putint; '.' -> put; newline;
   (* do not specialize the exit part *)
   #);
   j: @integer;
do 46 -> j;
   (* install the call back: *)
   (myCallBack##, j) -> installCallBack;
   ...
#)

10.2.3 Interfacing to External Pascal Procedures

When interfacing to Pascal or another programming language with a similar activation record organization the pattern Pascal must be called in the do-part of the External specialization. The BETA compiler will then generate a call to an external routine with the same name as the BETA pattern, using the Pascal style of passing parameters.

A pattern of the form

foo: external
  (# enter ... do Pascal exit ... #)

describes the interface to an external Pascal function with entry-point foo (_foo). (Note the exit parameters that must be present in Pascal function interfaces.

A Pascal procedure can be interfaced to through a pattern of the form

foo: external
  (# enter ... do Pascal #)

If the entry-point of the Pascal function or procedure needs to be explicitly specified, a pattern of the form

foo: external
  (# enter ... do 'X$bar' -> Pascal ... #)

can be used to describe the interface to an external Pascal procedure, where the entry-point for the Pascal procedure is X$bar instead of foo.

A pattern of the form

foo: external
  (# enter ... do '$...' -> PascalTrap exit ... #)

or

foo: external
  (# enter ... do '{$...,$...,...}' -> PascalTrap exit ... #)

describes the interface to an external Pascal procedure that is called using Motorola traps. The string in the first example is in the form of a single hexadecimal number, preceded with a $ (e.g. '$A9FF'). The string in the second example is in the form of a comma separated list of hexadecimal numbers, each preceded with a $ and enclosed with braces (e.g. '{$A9FF,$02F4}'). Decimal numbers may be used for specifying the traps. This is done by leaving out the $.

10.2.4 Example Interfacing to Pascal

As an example, an interface routine to a Pascal function (NewHandle) may be implemented in the following way:

NEWHANDLE: external 
  (# theHandle: @integer 
  do pascal 
  exit theHandle 
  #);

10.2.5 Using call backs from Pascal

Call backs from Pascal is handled similar to call backs from C, except that you should use pascalExternalEntry instead of cExternalEntry.

10.2.6 Interfacing to External Data Structures

Transferring data to and from the external languages is dealt with through two special purpose patterns: cStruct and externalRecord.

cStruct is the means for specifying a BETA object with a specific storage layout, and with the purpose of transferring this object to the external language for processing. That is, a cStruct object is allocated by BETA and made available for processing by the external language.

ExternalRecord is the means for specifying a BETA interface into some data structures, allocated by the external language.

10.2.7 cStruct

cStruct defines byteSize [1] that is used for specifying the number of bytes that should be allocated for the BETA object. For specifying the fields, the local patterns byte, short, signedShort and long are available. These patterns contain a local virtual attribute, pos, that is used to specify the byte position of this field in the cStruct object. Note that there is no check for overlapping fields. cStruct also defines put/getByte, put/getShort, put/getSignedShort and put/getLong operations for accessing the bytes, longs, etc. of the cStruct object directly.

10.2.8 ExternalRecord

ExternalRecord defines the ptr attribute, that is used to contain the memory address of the externally allocated data structure. For specifying the interface into the fields of this data structure, externalRecord defines byte, short, signedShort and long with local virtual attribute, pos, to describe the byte position of each field (just as cStruct). ExternalRecord also defines the put/getByte, etc. to make direct access to the bytes, shorts, etc. of the external data structure.

The connection between the externalRecord object and the external data structure is established by letting some external routine return the address of the external data structure, and then transfer this integer into the ptr attribute of the externalRecord object. If it is necessary to transfer this address back to the external language, it can be done by transferring the ptr attribute back through some external language routine.

10.2.9 Example Using cStruct

The following example shows how to interface to the C language using cStruct and external. The BETA pattern myStruct describes a BETA object to be transferred to the foo C function. The BETA pattern foo describes the interface to a C function called foo. It is important that there exist a C function with the name foo and exactly the same parameters and result.

(# myStruct: cStruct
   (* myStruct describes a cStruct consisting of 8 bytes
      'a' denotes byte[0]
      'b' denotes byte[1]
      'c' denotes byte[2-5]
      'd' denotes byte[6-7]
   *)
   (# byteSize ::< (# do 8 -> value #);
      a: byte (# pos ::< (# do 0 -> value #) #);
      b: byte (# pos ::< (# do 1 -> value #) #);
      c: long (# pos ::< (# do 2 -> value #) #);
      d: short (# pos ::< (# do 6 -> value #) #) 
   #);
   foo: external 
     (* This pattern describes the interface to the following
      * C function, called 'foo':
      *
      *  int foo(int i, short si, char a, char *t, myStruct *r) 
      *)
     (# i: @integer; si: @shortint;
        a: @char; t: [1] @char;
        r: ^myStruct;
        status: @ integer;
     enter (i, si, a, t, r[])
     exit status
     #);
   theStruct: @myStruct;
   m, n, status: @integer;
   c: @char;
do ...
   m -> theStruct.a; (* overflow is not detected *)
   17 -> theStruct.b; ...
   (n, 117, c, 'smith', theStruct[]) -> foo -> status;
   (if status
    // 117 then
       theStruct.d -> m;
    ...
   if);
   (11, m, 'x', 'smith', NONE) -> foo -> status;
#)

10.2.10 Example Using externalRecord

Here TCSbuffer is the interface into some data structure in some image processing software. TCAlloc is the interface into the C routine in that software, allocating this data structure, and allocate is an example of using this routine for getting access to the externally allocated data structure. Finally, update illustrates how to transfer the memory address back into the external language.

TCSbuffer: externalRecord
(# display:         @long(# pos ::< (# do 0 -> value #)#);
   window:           @long(# pos ::< (# do 4 -> value #)#);
   visual:           @long(# pos ::< (# do 8 -> value #)#);
   colormap:       @long(# pos ::< (# do 12 -> value #)#);
   depth:                  @long(# pos ::< (# do 16 -> value #)#);
   gc:           @long(# pos ::< (# do 20 -> value #)#);
   colorLookup: @long(# pos ::< (# do 24 -> value #)#);
   width:                  @long(# pos ::< (# do 56 -> value #)#);
   height:           @long(# pos ::< (# do 60 -> value #)#);
   data:                     @long(# pos ::< (# do 64 -> value #)#);
   xOffset:         @long(# pos ::< (# do 68 -> value #)#);
   yOffset:         @long(# pos ::< (# do 72 -> value #)#);
   zoom:                     @long(# pos ::< (# do 76 -> value #)#);
   updateTile:   @long(# pos ::< (# do 80 -> value #)#);
#);

TCAlloc: External 
  (# width, height: @integer;
     buffer: @integer
  enter (width, height)
  exit buffer
#);

allocate:
  (* allocates a true color buffer of resolution
   * width x height
   *)
  (# width, height: @integer; buffer: ^TCSbuffer
     noMemoryError :< TCSnoMemoryError;
  enter(width, height, buffer[])
  do (width, height) -> TCALLOC -> buffer.ptr;
     (if ptr //-1 then noMemoryError if);
     INNER
  #);

update: 
  (* Draws a region of a true color buffer on the window it's
   * associated with.  The x, y, width and height arguments 
   * give the location and size of the region in BUFFER 
   * coordinates, NOT window coordinates.
   *)
  (# x, y, width, height: @integer; buffer: ^TCSbuffer
     noBufferError :< TCSnoBufferError;
     internError :< TCSinternError;
  enter (buffer, x, y, width, height)
  do (if (buffer.ptr, x, y, width, height) -> TCUPDATE
      // 0 then 'update' -> NoBufferError
      //-4 then 'update' -> internError
     if);
   INNER
#);


[1] Note, that cStruct is defined in betaenv, and that the external library defines additional attributes to this cStruct pattern. ByteSize is defined in betaenv, whereas the rest of the attributes mentioned here, are described in external


Basic Libraries - Reference Manual
© 1990-2002 Mjølner Informatics
[Modified: Monday October 23rd 2000 at 22:18]