16 Access to External Functions and Data

The Mjølner System allows a tight integration between the BETA language and routines and data structures, originating from the C language. Many of the libraries in the Mjølner System (such as the interface to the X Window System) is based on this tight integration.

The integration allows for two types of integration, namely integration of routines, and integration of data structures. The facilities give the BETA programmer the possibility to invoke routines, written in C, and for accessing data structures, allocated in C. Moreover, the facilities also works the other way around, namely by allowing BETA patterns to be invoked (instantiated) from C routines, and BETA objects to be manipulated by C routines.

16.1 Example

Imagine that we have a database with person records. The database has a C interface and we like to use the database in BETA.

The following C declarations and functions illustrates a simplified database:

typedef struct Person {
  long ID;
  char *firstname,*surname;
  char sex; /* m(ale) or f(emale) */
} Person;

#define MaxPersons 200

Person Persons[MaxPersons];

Person *getPerson(long ID) {
  if (ID>=0 && ID<MaxPersons)
    return &Persons[ID];
  else
    return 0;
}

long putPerson(long ID, char * firstname, char* surname, char sex) {
  if (ID>=0 && ID<MaxPersons) {
    Persons[ID].ID=ID;
    Persons[ID].firstname=firstname;
    Persons[ID].surname=surname;
    Persons[ID].sex=sex;
    return 1;
  } else {
    return 0;
   }
}

We can then interface to the two functions and the Person struct by the following external and externalRecord declarations:

getPerson: external
  (# ID: @integer;
     ptr: @integer;
  enter ID
  exit ptr
  #);
putPerson: external
  (# ID: @integer;
     firstname, surname: [1]@char;
     sex: @char;
     result: @boolean;
  enter (ID,firstname,surname,sex)
  exit result
  #);
Person: externalRecord
  (# ID: @long(# pos::(# do 0-> value #)#);
     firstname: @long(# pos::(# do 4-> value #)#);
     surname: @long(# pos::(# do 8-> value #)#); (* char ptr *)
     sex: @byte(# pos::(# do 12-> value #)#);
  #);

Interfacing to C routines are done by specifying the external pattern as the superpattern for the BETA pattern, which, when invoked, should invoke the C routine. The name of the entry call of the C routine should be the same as the name of the BETA pattern. 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. The pattern:

getPerson: external
  (# ID: @integer;
     ptr: @integer;
  enter ID
  exit ptr
  #);

describes the interface to an external C function with the name getPerson.

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 externally. externalRecord is the means for specifying a BETA interface into some data structures, allocated externally. The pattern:

Person: externalRecord
  (# ID: @long(# pos::(# do 0-> value #)#);
     firstname: @long(# pos::(# do 4-> value #)#);
     surname: @long(# pos::(# do 8-> value #)#); (* char ptr *)
     sex: @byte(# pos::(# do 12-> value #)#);
  #);

describes an interface to an external allocated struct (Person) with four fields.

We can create a person by calling putPerson like this:

(117,'Roger','Smith','m')->putPerson

We can get a person from the database by:

117 -> getPerson -> aPerson.ptr;

Notice, that we must assign to the ptr attribute of the externalRecord Person.

The Person can now be examined like any other BETA object, except for the 'string' declarations firstname and surname. These refers to C strings. The Mjølner System includes a cString library for easy interface to these C strings, so we simply make a small operation to print out the strings:

putCString:
  (# cstr: @cString;
  enter cstr
  do cstr.get -> puttext;
  #);
Finally we must specify where to find the C object file we are interfacing to. This is done using a OBJFILE specification. The specification:
OBJFILE nti     '$/cperson.obj'
        mac     '$/cperson.obj'
        default '$/cperson.o';
means that we should link with the file cperson.o (or cperson.obj, depending on the platform) located in the subdirectory with the name of the platform - the same name as the code subdirectory ('$' expands to name of platform).

The C file can also be generated directly by the BETA compiler by a BUILD specification. The specification:

BUILD nti     '$$/cperson.obj' 'cperson.c' 
              'betacc $0 $1'
      ppcmac  '$$/cperson.obj' 'cperson.c' 
              'MrC -w 2 -o $0 $1'
      default '$$/cperson.o' 'cperson.c'
              '$CC -c -o $0 $1';

means that we should link with the file cperson.o (or cperson.obj) located in the subdirectory with the name of the platform - the same name as the code subdirectory ('$$' expands to name of platform). And that in order to generale the cperson.o (or cperson.obj file), the BETA compiler should invoke the betacc, MrC og CC compiner, depending on the platform)

And now the complete program:

Program 20: Person.bet

ORIGIN '~beta/basiclib/external';
INCLUDE '~beta/sysutils/cstring';
BUILD nti     '$$/cperson.obj' 'cperson.c' 
              'betacc $0 $1'
      ppcmac  '$$/cperson.obj' 'cperson.c' 
              'MrC -w 2 -o $0 $1'
      default '$$/cperson.o' 'cperson.c'
              '$CC -c -o $0 $1';
--program: descriptor-- 
(# 
   getPerson: external
     (# ID: @integer;
        ptr: @integer;
     enter ID
     exit ptr
     #);
   putPerson: external
     (# ID: @integer;
        firstname, surname: [1]@char;
        sex: @char;
        result: @boolean;
     enter (ID,firstname,surname,sex)
     exit result
     #);
   Person: ExternalRecord
     (# ID: @long(# pos::(# do 0-> value #)#);
        firstname: @long(# pos::(# do 4-> value #)#);
        surname: @long(# pos::(# do 8-> value #)#); (* pointers to text *)
        sex: @byte(# pos::(# do 12-> value #)#);
     #);
   putCString:
     (# cstr: @cString;
     enter cstr
     do cstr.get -> puttext;
     #);
   aPerson: @Person;
do 
   (* store a person in C-database *)
   (if not ((117,'Roger','Smith','m')->putPerson) then
       'Failed to store person'->putline; stop;
   if);
   (* get person from C-database *)
   117 -> getPerson -> aPerson.ptr;
   (if aPerson.ptr = 0 then
       'Failed to retrieve person' -> putline; stop;
   if);
   'Person: ' -> puttext;
   aPerson.ID -> putint; 
   ' ' -> put;
   'Name: \'' -> puttext;
   aPerson.firstname -> putCString;
   ' ' -> put;
   aPerson.surname -> putCString; 
   '\' ' -> puttext;
   'Sex: ' -> puttext;
   aPerson.sex-> put;
   newline;
#)

Output of running the program is:

nil% Person
Person: 117 Name: 'Roger Smith' Sex: m


Libraries Tutorial
© 1994-2002 Mjølner Informatics
[Modified: Thursday October 19th 2000 at 14:10]