Manual: Prolog Calling Foreign Code


Quintus Prolog Manual


(PREV) (NEXT)

I-3: Prolog Calling Foreign Code

I-3-1: Introduction

This chapter describes how to load and call programs written in C, Pascal, FORTRAN, or Assembly language. This may be desirable in order to:

Examples showing the correct use of the foreign interface are found in the demo and library subdirectories of the Quintus Prolog installation directory. Examples of incrementally loading C, Pascal and FORTRAN code can be found in section I-3-15.

Foreign functions are loaded directly into the Prolog system by using one of the built-in predicates load_foreign_executable/1 or load_foreign_files/2. These predicates load executable images or object files into the address space of the running Prolog.

Before calling these predicates, you must prepare facts in the data base that describe which functions may be called by Prolog, the native language of each function, and the argument types of each function. This information is used to link Prolog predicates and foreign functions when loading the foreign code.

The foreign language interface supports the direct exchange of Prolog's atomic data types (atoms, integers and floating-point numbers). The data is automatically converted between Prolog's internal representation and the internal representation of the foreign language.

The foreign language interface also supports passing any Prolog term to C and receiving any Prolog term from C. A set of C functions is provided to type test and access terms passed to C and to create new Prolog terms in C. For information on these functions see Part M.

Prolog procedures that are attached to foreign functions are determinate, in that they succeed at most once for a given call and are not re-entered on backtracking. This imposes no serious limitation, since it is always possible to divide a foreign function into the part to be done on the first call and the part to be redone on backtracking. Backtracking can then take place at the Prolog level where it is naturally expressed.

I-3-1-1: Summary of steps

Following is a summary of the steps which enable you to call foreign code from a Prolog predicate:

IN THE PROLOG CODE:

  1. Declare the relevant object file(s), and the names of the functions defined in them, by defining clauses for foreign_file/2 (see section I-3-3).
  2. Specify the argument passing interface for each function by defining clauses for foreign/3 (see section I-3-4).
  3. Load the foreign files into Prolog by calling load_foreign_executable/1 or load_foreign_files/2 (see section I-3-2).

I-3-2: Using Shared Object Files

A shared object file which typically has a name that ends in '.so', is constructed from a list of object files (and libraries) using the UNIX linker "ld". The object files are generated using the foreign language compiler and normally have names that end in '.o'.

Some linkers require special options to construct a shared object file and some linkers may require that the object files used to generate the shared object files be compiled with "position independent code" which in turn may require special options to the compiler. These details are dependent on the operating system platform and should be explained in the man page for "ld".

The following two sections describe the use of shared object files on SVR4 systems including Solaris 2.x.

I-3-2-1: Loading Foreign Executables

The built-in Prolog predicate load_foreign_executable/1 is used to load foreign functions directly into Prolog from a "shared object file" and to attach selected functions and routines in the loaded file to Prolog predicates.

The example below demonstrates the use of these predicates to load code compiled using the C compiler. Note that the compiler is called with the -K pic option, which generates "position independent code". When a shared object file is mapped into a process's address space, those pages in memory that are not modified by this process can be shared with other processes. Position independent code maximises the number of pages that can remain shared after loading. On Solaris 2.x shared objects can be built without this option, thus the example would still work using ordinary position dependent code, but not all operating systems allow this.


% cc -c -K pic math.c
% cc -c -K pic other.c
% ld -G -o math.so math.o other.o -lm
% prolog

| ?- compile(foreign).
| ?- load_foreign_executable(math).

In the above example, 'foreign' (or 'foreign.pl') is a file containing facts that describe how Prolog is to call the foreign functions. If it is given a filename without an extension then it automatically appends '.so' to it (or the appropriate extension for the platform); thus in the example above, 'math' is specified to load 'math.so'.

The loading process may fail if:

If the load does not complete successfully then an exception is raised and the call to load_foreign_executable/1 fails; no change is made to the Prolog state. The load can be retried once the problem has been corrected.

Once a foreign program is loaded, it cannot be unloaded or replaced, although you can abolish or redefine any procedure attached to it.

NOTES:
  1. Any foreign file loaded via a load_foreign_executable/1 command which is embedded in a file being loaded into Prolog will be sought relative to the directory from which the file is being loaded. For example, if the file /usr/fred/test.pl contains the command
    
       :- load_foreign_executable(test).
    
        
    then the file to be loaded would be /usr/fred/test.so.
  2. When "ld" is given a library such as '-lX11', it will look for a "shared library" version and if one exists record this library as a dependency in the shared object file. load_foreign_executable/1 will then automatically load this library (if not already loaded) when it loads the shared object file. If "ld" is given a library for which no shared library exists, then object files from the static library are incorporated into the shared object file as needed. For example if there is no shared '-lm' library, routines will be extracted from /usr/lib/libm.a. This means that any routine in a static library that is to be accessed from Prolog must have some reference to it in one of the object files being linked into the shared object file.
  3. It is better to load one large shared object file than many small ones. You may have several Prolog files that require routines from one shared object file - in other words, a shared library. The shared library is only loaded once, but different functions could be attached to Prolog predicates in different calls to load_foreign_executable/1. For example, most of the files in the Prolog Library which load foreign code use the shared library file "libpl.so".

A description of the internal operation of the load_foreign_executable/1 predicate is given in section I-3-12 to help solve more difficult foreign code loading problems.

I-3-2-2: Loading Foreign Files

load_foreign_files/2 is an alternative interface to load_foreign_executable/1 which constructs a shared object file from the list of object files and libraries given as its arguments and then maps the resulting shared object file into the Prolog address space. In general it is recommended that you take the responsibility for the construction of a shared object file and then use load_foreign_executable/1 directly. Using load_foreign_files/2 is slower because it has to invoke "ld" to construct the shared object file everytime the program is loaded.

Example:


% cc -c math.c
% cc -c other.c
% prolog

| ?- compile(foreign).
| ?- load_foreign_files([math,other],['-lm']).

Again, the file 'foreign' (or 'foreign.pl') contains the facts that describe how Prolog is to call the foreign functions. If the extensions on filenames given in the first argument to load_foreign_files/2 are omitted then '.o' is automatically appended to them. Thus, in the above example, load_foreign_file/2 will try to load 'math.o' and 'other.o'.

I-3-3: Linking Foreign Functions to Prolog Procedures

When load_foreign_executable/1 or load_foreign_files/2 is called, it calls the hook predicates foreign_file/2 and foreign/3 in the current source module. These should have been previously defined by clauses of the form:


foreign_file(FileName, [Function1,Function2,...,FunctionN]).

foreign(Function1, Language, PredicateSpecification1).
foreign(Function2, Language, PredicateSpecification2).
....
foreign(FunctionN, Language, PredicateSpecificationN).

Example:


foreign_file(math, [sin,cos,tan]).

foreign(sin, c, sin(+float,[-float])).
foreign(cos, c, cos(+float,[-float])).
foreign(tan, c, tan(+float,[-float])).

NOTE: If a Prolog module includes foreign code, all relevant foreign/[2,3] and foreign_file/2 facts should be loaded into that module and the load_foreign_executable/1 or load_foreign_files/2 command should be called from that module.

A foreign_file/2 fact lists the functions that will be provided by the associated (shared) object file. When using load_foreign_files/2, a fact of this form must be provided for each file specified in the ListOfFiles argument. The functions specified should be only those that are to be attached to Prolog procedures. Supporting functions that will not be called directly from Prolog should not be listed.

Each foreign/3 fact describes how a foreign function is to be attached to a Prolog procedure. PredicateSpecification specifies the Prolog procedure and also the argument passing interface (described below). A fact of this form must be provided for each function that is to be attached to a Prolog procedure.

When load_foreign_executable/1 or load_foreign_files/2 is called, the specified files are loaded into the running Prolog and then all the specified Prolog procedures are abolished and redefined to be links to the foreign functions. Calling one of the Prolog procedures now results in a call to a foreign function.

Prolog procedures can be directly linked to UNIX library functions. Note, however, that some functions shown in the library documentation are actually C macros (found in included .h files). In this case, the simplest approach is to write a C function which uses the macro and then link to that function.

You may abolish or redefine (using compile/1) any procedure which has been attached to a foreign function. This severs the link between the Prolog predicate and the foreign function. It is not possible to reestablish this link.

The foreign_file/2 and foreign/3 facts must be consistent whenever load_foreign_executable/1 or load_foreign_files/2 is called. They are, however, not used after this point and may be abolished, if desired.


Footnote: See example in the reference page for foreign/[2,3].

.

The load_foreign_executable/1 and load_foreign_files/2 commands can be used any number of times in a Prolog session to load different foreign programs. For example:


| ?- compile(f1),
     load_foreign_executable(f1),
     abolish([foreign/3, foreign_file/2]).
| ?- compile(f2),
     load_foreign_executable(f2),
     abolish([foreign/3, foreign_file/2]).

 
Each compile/1 installs a new set of facts describing a set of functions to be loaded by load_foreign_executable/1. Unless you abolish all foreign/3 and foreign_file/2 facts before each compilation, Prolog will warn you that foreign/3 and foreign_file/2 have been previously defined in another file.

A better way to do this is to insert the call to load_foreign_executable into the file which defines foreign_file/2 and foreign/3 as an embedded command. For example, you could add the following command to the end of the file f1.pl:


:-  load_foreign_executable(f1),
    abolish([foreign/3, foreign_file/2]).

 
so that compiling f1.pl will automatically load f1.so. This embedded command will also work when building a stand-alone program, as described in Chapter H-1.

I-3-4: Specifying the Argument Passing Interface

The argument passing interface is specified by defining facts for foreign/3 of the form:


foreign(+Routine, +Language, +PredicateSpecification)

 
Routine is an atom which names a foreign code routine and Language is an atom (either 'c', 'pascal', or 'fortran') that names the language in which the routine is written.

NOTE: Assembly code can be loaded if it emulates the exact calling conventions of one of C, FORTRAN, or Pascal. Language is then chosen, accordingly, to be one of 'c', 'fortran', or 'pascal'. For an extended example, see section I-3-15-4.

PredicateSpecification specifies the Prolog name given to the foreign code routine and how its arguments will be passed to and from the foreign code routine.

PredicateSpecification is of the form:


PredicateName(ArgSpec1, ArgSpec2, ...ArgSpecN)

 
where PredicateName is the name of the Prolog predicate (an atom) and each ArgSpec is an argument specification for each argument of the predicate. An ArgSpec informs the Prolog system how to pass or receive a Prolog term in the corresponding argument position.

Prolog checks the types of the input arguments; a foreign function call will raise an exception if any input argument is not of the right type.

If the argument passed is an atomic object then the interface automatically converts between Prolog's representation of the data and the representation expected by the foreign function. Thus the external function does not need to know how Prolog represents atoms, integers, or floats in order to communicate with Prolog. This feature simplifies the integration of foreign code with Prolog; in particular, it makes it easier to interface directly with already-written functions in libraries and other programs. It also allows for compatibility with later versions of Quintus Prolog and with versions of Quintus Prolog running on other hardware.

NOTE: The only atomic object that cannot be passed directly through the foreign interface is a db_reference. db_references can be passed to foreign code using the general term passing mechanism using +term and -term. You can take apart and build db_references in foreign language using the QP_get_db_reference() and QP_put_db_reference() functions.

On the other hand, generic Prolog terms passed to a foreign function (using +term) are not converted to any representation in the foreign language. Instead the foreign function gets a reference to a Prolog term. A set of functions/macros is provided to type test, access and create Prolog terms through these references (see Chapter M). Similarly when a generic term is returned (using -term or [-term]) from a foreign function there is no conversion of any data structures in the foreign language into an equivalent Prolog representation. The foreign function has to return a reference to a Prolog term which it originally got from Prolog or from one of the functions/macros provided to manipulate Prolog terms (the QP_put* and QP_cons* families of functions) Further details in section I-3-8.

Arguments are passed to foreign functions in the same order as they appear in the Prolog call, except for the return value. At most one "return value" argument can be specified; that is, there can be only one [-integer], [-float], [-atom], [-string], [-string(N)], or [-address(typename)] specification. There need not be any "return value" argument, in which case the value returned by the function is ignored. Both input and output specifications cause data to be passed to the foreign function (except of course for the "return value" argument, if present). Each input argument is appropriately converted and passed, by reference or by value, depending on the language's calling convention, and each output argument is passed as a pointer through which the foreign function will send back the result. Note that for C, input arguments are always passed by value.

]

Prolog assumes that a foreign function will return output arguments of the specified types; if it does not, the result is unpredictable. Normally, unbound variables will be supplied in the Prolog goal for all the output argument positions. However, any value may be supplied for an output argument; when the foreign function has been completed, its outputs are unified with the values supplied and a failure to unify results in the failure of the Prolog goal.

Detailed information about passing particular data types through the foreign interface can be found in section I-3-5 through section I-3-9. Examples showing the correct use of the foreign interface are presented in section I-3-15.

I-3-5: Passing Integers

In previous releases of Quintus Prolog, integers were represented with 29 bits. Since most languages represent integers using 32 bits, errors could occur when passing very large positive or negative numbers between Prolog and foreign functions. In Release 3 and later releases of Quintus Prolog the precision of integers has been raised to 32 bits; hence, these errors no longer occur.

I-3-5-1: Passing an Integer to a Foreign Function



Prolog:  +integer
C:       long int x
Pascal:  x: integer
FORTRAN: integer x

The argument must be instantiated to an integer, otherwise the call will raise an exception. The Prolog integer is converted to a 32-bit integer and passed to the foreign function.

I-3-5-2: Returning an Integer from a Foreign Function



Prolog:  -integer
C:       long int *x;
         *x = ...
Pascal:  var x: integer
         x := ...
FORTRAN: integer x
         x = ...

A pointer to a 32-bit integer is passed to the function. It is assumed that the function will overwrite this integer with its result. When the foreign function returns, the integer being pointed to is converted to a Prolog integer and unified with the corresponding argument of the Prolog call. The argument can be of any type; if it cannot be unified with the returned integer, the call fails. If the foreign function does not overwrite the integer, the result is undefined.

I-3-5-3: An Integer Function Return Value



Prolog:  [-integer]
C:       long int f(...)
           {
             long int x;
             return x;
           }
Pascal:  function f(...): integer;
           var x: integer;
           begin
             f := x;
           end
FORTRAN: integer function f(...)
           integer x
           f = x
         end

No argument is passed to the foreign function. The return value from the function is assumed to be an integer. It is converted to a Prolog integer and unified with the corresponding argument of the Prolog call. The argument can be of any type; if it cannot be unified with the returned integer, the call fails.

I-3-6: Passing Floats

In previous releases of Quintus Prolog, floating-point numbers had less precision than single-precision floats in other languages. The result was a loss of precision when floats were passed between Prolog and foreign functions. In Release 3 and later releases of Quintus Prolog the precision of floating-point numbers has been raised to 64 bits (double precision); hence, these errors no longer occur.

Quintus Prolog Release 3 also supports the passing of floating point numbers explicitly as doubles or singles.

I-3-6-1: Passing a Float to a Foreign Function



Prolog:  +float
C:       double x;
Pascal:  x: real
FORTRAN: real x

The argument must be instantiated to an integer or a float; otherwise the call will raise an exception. The Prolog number is converted to a 32-bit single-precision (FORTRAN) or a 64-bit double-precision (C or Pascal), float and passed to the foreign function. Many C compilers will allow the parameter declaration to be 'float' instead of 'double' because they always convert single-precision floating-point arguments to double-precision. However, C compilers conforming to the new ANSI standard will not do this, so it is recommended that 'double' be used.



Prolog:  +single
ANSI C:  float x;
FORTRAN: real x

The argument must be instantiated to an integer or a float; otherwise the call will raise an exception. The Prolog number is converted to a 32-bit single-precision float and passed to the foreign function. +single can also be used to interface Prolog to any foreign function where you know that the value passed is going to be picked up as a 32-bit float.



Prolog:  +double
C:       double x;
Pascal:  real x

The argument must be instantiated to an integer or a float; otherwise the call will raise an exception. The Prolog number is converted to a 64-bit double-precision (C or Pascal) float and passed to the foreign function. +double can also be used to interface Prolog to any foreign function where you know that the value passed is going to be picked up as a 64-bit float.

I-3-6-2: Returning a Float from a Foreign Function



Prolog:  -float
C:       float *x;
         *x = ...
Pascal:  var x: real
         x := ...
FORTRAN: real x
         x = ...

A pointer to a single-precision float is passed to the function. It is assumed that the function will overwrite this float with its result. When the foreign function returns, the float is converted to a Prolog float and unified with the corresponding argument of the Prolog call. The argument can be of any type; if it cannot be unified with the returned float, the call fails. If the foreign function does not overwrite the float, the result is undefined.



Prolog:  -single
C:       float *x;
         *x = ...
Pascal:  var x: real
         x := ...
FORTRAN: real x
         x = ...

A pointer to a single-precision float is passed to the function. It is assumed that the function will overwrite this float with its result. When the foreign function returns, the float is converted to a Prolog float and unified with the corresponding argument of the Prolog call. The argument can be of any type; if it cannot be unified with the returned float, the call fails. If the foreign function does not overwrite the float, the result is undefined.



Prolog:  -double
C:       double *x;
         *x = ...
Pascal:  var x: real
         x := ...

A pointer to a double-precision float is passed to the function. It is assumed that the function will overwrite this float with its result. When the foreign function returns, the float is converted to a Prolog float and unified with the corresponding argument of the Prolog call. The argument can be of any type; if it cannot be unified with the returned float, the call fails. If the foreign function does not overwrite the float, the result is undefined.

I-3-6-3: A Floating-point Function Return Value



Prolog:  [-float]
C:       double f(...)
           {
             double x;
             return x;
           }
Pascal:  function f(...): real;
           var x: real;
           begin
             f := x;
           end
FORTRAN: real function f(...)
           real x
           f = x
         end

No argument is passed to the foreign function. The return value from the function is assumed to be a single-precision (FORTRAN) or double-precision (C and Pascal) floating point number. It is converted to a Prolog float and unified with the corresponding argument of the Prolog call. The argument can be of any type; if it cannot be unified with the returned float, the call fails.

Many C compilers will allow the function return value to be 'float' instead of 'double' because they always convert single-precision floating-point arguments to double-precision. However, C compilers conforming to the new ANSI standard will not do this, so it is recommended that 'double' be used.



Prolog:  [-single]
ANSI C:  float f(...)
           {
             float x;
             return x;
           }
Pascal:  function f(...): real;
           var x: real;
           begin
             f := x;
           end
FORTRAN: real function f(...)
           real x
           f = x
         end

No argument is passed to the foreign function. The return value from the function is assumed to be a single-precision (FORTRAN) or double-precision (C and Pascal) floating point number. It is converted to a Prolog float and unified with the corresponding argument of the Prolog call. The argument can be of any type; if it cannot be unified with the returned float, the call fails.



Prolog:  [-double]
C:       double f(...)
           {
             double x;
             return x;
           }
Pascal:  function f(...): real;
           var x: real;
           begin
             f := x;
           end
FORTRAN: real function f(...)
           real x
           f = x
         end

No argument is passed to the foreign function. The return value from the function is assumed to be a single-precision (FORTRAN) or double-precision (C and Pascal) floating point number. It is converted to a Prolog float and unified with the corresponding argument of the Prolog call. The argument can be of any type; if it cannot be unified with the returned float, the call fails.

I-3-7: Passing Atoms

The foreign function interface allows Prolog atoms to be passed to functions either in a canonical form as unsigned integers, or as pointers to character strings.

For each Prolog atom there is a single canonical representation. Programs can rely on the property that identical atoms have identical canonical representations. Note, however, that the canonical form of an atom is not necessarily identical across different invocations of the program. This means that canonical atom representations should not be used in files or interprogram communication. For these purposes strings should be used. Foreign functions can store canonical atoms in data structures and pass them around and then back to Prolog, but they should not attempt any other operations on them.

Strings passed from Prolog to foreign functions should not be overwritten. Strings passed back from foreign functions to Prolog are automatically copied by Prolog if necessary. Thus the foreign program does not have to retain them and can reuse their storage space as desired.

There are three ways of passing atoms through the foreign interface:

  1. as canonical integers (to or from any language): see section I-3-7-1.
  2. as null-terminated strings (to or from C): see section I-3-7-2.
  3. as fixed-length, blank-padded strings (to or from FORTRAN or Pascal): see section I-3-7-2.

I-3-7-1: Passing Atoms in Canonical Form

This section deals with passing atoms in canonical form, that is, as unsigned integers.



Prolog:  +atom
C:       QP_atom x;
Pascal:  x: integer
FORTRAN: integer x

The argument must be instantiated to an atom, otherwise the call will signal an error. An unsigned integer representing the Prolog atom is passed to the foreign function. Atoms can be converted to strings through the functions QP_string_from_atom() or QP_padded_string_from_atom() (see section I-3-7-4).



Prolog:  -atom
C:       QP_atom *x
         *x = ...
Pascal:  var x: integer
         x := ...
FORTRAN: integer x
         x = ...

A pointer to an unsigned integer is passed to the function. It is assumed that the function will overwrite this unsigned integer with its result. This result should be a canonical representation of an atom already obtained from Prolog, or one generated through the function QP_atom_from_string() or the function QP_atom_from_padded_string() (see section I-3-7-4). Returning an arbitrary integer will have undefined results. When the foreign function returns, the atom represented by the unsigned integer being pointed to is unified with the corresponding argument of the Prolog call. The argument can be of any type; if it cannot be unified with the returned atom, the call fails. If the foreign function does not overwrite the unsigned integer, the result is undefined.



Prolog:  [-atom]
C:       QP_atom f(...)
           {
             QP_atom x;
             return x;
           }
Pascal:  function f(...): integer;
           var x: integer;
           begin
             f = x;
           end
FORTRAN: integer function f(...)
           integer x
           f = x
         end

No argument is passed to the foreign function. The return value from the function is assumed to be an unsigned integer which should be a canonical representation of an atom already obtained from Prolog, or one generated by one of the functions QP_atom_from_string() or QP_atom_from_padded_string() (see section I-3-7-4). Returning an arbitrary integer will have undefined results. The atom represented by the unsigned integer is unified with the corresponding argument of the Prolog call. The argument can be of any type; if it cannot be unified with the returned atom, the call fails.

I-3-7-2: Passing Atoms as Strings between Prolog and C

This section describes passing atoms as pointers to null-terminated character strings. This is the way to pass atoms as strings between Prolog and C. For FORTRAN and Pascal, the specification of string arguments is different than for C - see section I-3-7-3.



Prolog:  +string
C:       char *x
Pascal:  Not supported
FORTRAN: Not supported

The argument must be instantiated to an atom, otherwise the call will signal an error. A pointer to a null-terminated string of characters is passed to the C function. This string must not be overwritten by the C function.



Prolog:  -string
C:       char **x;
         *x = ...
Pascal:  Not supported
FORTRAN: Not supported

A pointer to a character pointer is passed to the C function. It is assumed that C will overwrite this character pointer with the result it wishes to return. This result should be a pointer to a null-terminated string of characters. When the C function returns, the atom which has the printed representation specified by the string is unified with the corresponding argument of the Prolog call. The argument can be of any type; if it cannot be unified with the returned atom, then the call fails. If the C function does not overwrite the character pointer, then the result is undefined.

Prolog copies the string if required, so that it is not necessary for the C program to worry about retaining it. Beware, however, that the string must not be an 'auto', because in this case its storage may be reclaimed after the foreign function exits but before Prolog has managed to copy it.



Prolog:  [-string]
C        char *f(...)
           {
             char *x;
             return x;
           }
Pascal:  Not supported
FORTRAN: Not supported

No argument is passed to C. The return value from the C function is assumed to be a character pointer pointing to a null-terminated string of characters. The atom which has the printed representation specified by the string is unified with the corresponding argument of the Prolog call. The argument can be of any type; if it cannot be unified with the returned atom, the call fails.

Prolog copies the string if required, so that it is not necessary for the C program to worry about retaining it. Beware, however, that the string must not be an 'auto', because in this case its storage may be reclaimed after the foreign function exits but before Prolog has managed to copy it.

I-3-7-3: Passing Atoms as Strings to/from Pascal or FORTRAN

This section describes passing atoms as pointers to fixed-length, blank-padded arrays of characters. This is the way to pass atoms as strings between Prolog and Pascal or FORTRAN. See section I-3-7-2 for how to pass atoms as null-terminated strings between Prolog and C.

Implementation note: The foreign interface makes some assumptions about how string parameters are handled in Pascal and FORTRAN compilers. If a given Pascal or FORTRAN compiler has different conventions for the handling of string parameters, the interface will not work. The conventions are:



Prolog:  +string(N)
C:       Not supported
Pascal:  type stringN = packed array [1..N] of char;
         var x: stringN
FORTRAN: character*N

The argument must be instantiated to an atom, otherwise the call will signal an error. A character array, containing a copy of the characters of the atom, is passed by reference to the function. The text is truncated on the right or padded on the right with blanks to length N.

Note that the Pascal parameter is call-by-reference (var), the same as for the -string(N) case below.



Prolog:  -string(N)
C:       Not supported
Pascal:  type stringN = packed array [1..N] of char;
         var x: stringN
FORTRAN: character*N

A pointer to a character array of length N, initialized to all blanks, is passed to the function. It is assumed that the function will fill in this array. When the function returns, the atom which has the printed representation specified by the character array is unified with the corresponding argument of the Prolog call.

Trailing blanks in the character array are ignored. Thus if the foreign function sets a character array of length 6 to 'atom ', Prolog will convert the result to the atom 'atom'. Leading blanks are significant: if the foreign function returns 'this ', the resulting atom is 'this'.

The argument can be of any type; if it cannot be unified with the returned atom, then the call fails. If the function does not fill in the character array, then the result is the null atom ''.



Prolog:  [-string(N)]
C:       Not supported
Pascal:  Not Supported
FORTRAN: character*N function

This argument specification is valid only for FORTRAN. The FORTRAN function result is initialized to a blank-filled character array of length N. It is assumed that the function will fill this array. The atom which has the printed representation specified by the character array is unified with the corresponding argument of the Prolog call.

Trailing blanks in the character array are ignored, as for the -string(N) case above.

The argument can be of any type; if it cannot be unified with the returned atom, then the call fails. If the function does not fill in the character array, then the result is the null atom ''.

I-3-7-4: Converting between Atoms and Strings

Four functions are provided to enable foreign functions to translate from one representation of an atom to another. The first two functions are most useful for C: they convert between canonical atoms and null-terminated C strings. The other two functions are most useful for Pascal and FORTRAN: they convert between canonical atoms and blank-padded character arrays.

QP_string_from_atom(atom)

atom
QP_atom (that is, an unsigned integer passed by value)
Returns:
Pointer to a null-terminated string of characters (C convention for strings)
Returns a pointer to a string representing 'atom'. This string should not be overwritten by the foreign function.

QP_atom_from_string(string)

string
Pointer to a null-terminated string of characters (C convention for strings)
Returns:
QP_atom
Returns the canonical representation of the atom whose printed representation is 'string'. The string is copied, and the foreign function can reuse the string and its space.

QP_padded_string_from_atom(pointer_to_atom, pointer_to_padded_string, pointer_to_length)

pointer_to_atom
Pointer to a QP_atom (that is, an unsigned integer passed by reference)
pointer_to_padded_string
Pointer to a character array
pointer_to_length
Pointer to an integer (that is, an integer passed by reference)
Returns:
integer
Fills in the character array of length '*pointer_to_length' with the string representation of the atom. The string is truncated or blank-padded to '*pointer_to_length' if the length of the atom is greater than or less than '*pointer_to_length', respectively. The length of the atom (not '*pointer_to_length') is returned as the function value.

QP_atom_from_padded_string(pointer_to_atom, pointer_to_padded_string, pointer_to_length)

pointer_to_atom
Pointer to a QP_atom (that is, an unsigned integer passed by reference)
pointer_to_padded_string
Pointer to a character array
pointer_to_length
Pointer to an integer (that is, an integer passed by reference)
Returns:
integer
Sets '*pointer_to_atom' to the canonical representation of the atom whose printed representation is the string (less any trailing blanks) contained in the character array of length '*pointer_to_length'. Returns the length of the resulting atom (not '*pointer_to_length') as the function value.

Below are C specifications of these functions. Note that the arguments of the last two functions are passed by reference. Hence, the last two functions can be called directly from Pascal or FORTRAN. The first two functions are designed to be called from C, in which all parameters are passed by value.


char * QP_string_from_atom(atom)
     QP_atom atom;

QP_atom QP_atom_from_string(string)
     char *string;

int QP_padded_string_from_atom(atom,string,length)
     QP_atom *atom;
     char *string;
     int *length;

int QP_atom_from_padded_string(atom,string,length)
     QP_atom *atom;
     char *string;
     int *length;

Canonical atoms are particularly useful as constants, to be used in passing back results from foreign functions. The above functions can be used to initialize tables of such constants.

These functions can only be called from languages other than C if those languages have a C-compatible calling convention for passing integers and pointers. For example, this is true for both Pascal and FORTRAN running under UNIX 4.2 BSD. See the appropriate Quintus Prolog Release Notes for any further details pertaining to your system.

I-3-8: Passing Prolog Terms

This section describes passing Prolog terms to a foreign function and receiving Prolog terms from a foreign function. For the current release this interface is supported only for C.

There is a difference between passing atomic objects (atoms, floats, db_reference and integers) and generic Prolog terms through the foreign interface. Generic Prolog terms passed to a C function (using +term) are not converted to any representation in C. Instead the foreign function in C gets a reference to the Prolog term which is of type QP_term_ref (defined in #include <quintus/quintus.h>). Similarly when a generic term is returned (using -term or [-term]) from a foreign function there is no conversion of any data structures in the foreign language into an equivalent Prolog representation. The foreign function has to return a reference to a Prolog term which it originally got from Prolog or from one of the functions/macros provided to manipulate Prolog terms such as the QP_put* and QP_cons* families of functions.

When Prolog terms are referred to from C, what the C function holds is an indirect reference to the Prolog term. There is a reason for this indirection. Prolog terms live in the Prolog global stack, and migrate when Prolog does garbage collection or stack shifting. If the C function held onto a direct reference to a Prolog term it would become invalid after one of these memory management operations. Prolog cannot update and relocate these references that C is holding onto since it is impossible to distinguish between Prolog references and other integers and pointers that C holds onto.

The C code should also be aware of the scope (or lifetime) of the references to Prolog terms passed to it. Once you return to Prolog from a call to a foreign function, all the references to Prolog terms passed to the foreign function are invalid. All references to terms created by the foreign function are also invalid.

WARNING: You should not store references to prolog terms into global variables in the foreign language.

The scope of references to terms are more restricted when C calls Prolog. If Prolog returns a term as a result of a C call to a Prolog predicate, that term is valid only till the call for the next solution from that Prolog predicate (using QP_next_solution()). This also holds true for terms created in C. If you create a term after one call to a Prolog predicate then the reference to that term is only valid till the call for next solution from that Prolog predicate.

I-3-8-1: Passing a Prolog term to a Foreign Function



Prolog:  +term
C:       QP_term_ref

The argument can be any Prolog term. The C function gets an object of type QP_term_ref (defined in #include <quintus/quintus.h>). QP_term_type() and associated functions can be used to test the type of the term. And the QP_get() functions can be used to access the value associated with the term. QP_unify() can be used to used to unify terms or subterms of terms passed to C.

I-3-8-2: Returning a Prolog term from a Foreign Function



Prolog:  -term
C:       QP_term_ref x;

An initialized QP_term_ref (defined in #include <quintus/quintus.h>) is passed to the C function. It is assumed that the function will assign a term to this QP_term_ref using one of the QP_put() functions. When the foreign function returns, the term that the QP_term_ref refers to is unified with the corresponding argument of the Prolog call. The argument can be of any type; if it cannot be unified with the referred term, the call will fail.

I-3-8-3: A Prolog term returned as a value of a Foreign Function



Prolog:  [-term]
C:       QP_term_ref f(...);
           {
                QP_term_ref ref = QP_new_term_ref();
                return ref;
           }

No argument is passed to the foreign function. The return value from the function is assumed to be a reference to a Prolog term of type QP_term_ref. The term that the QP_term_ref refers to is unified with the corresponding argument of the Prolog call. The argument can be of any type; if it cannot be unified with the referred term, the call will fail.

I-3-9: Passing Pointers

Pointers should be passed through the foreign interface using the specification


address(typename)

. They could also be passed as integers but there are two added advantages for using the address specification. The first is that the 'plint' library can check for consistency between the foreign declarations and the foreign code. The second advantage is for possible optimizations on platforms whose pointers require more than 29 bits.

The typename is there so that 'plint' knows what kind of argument to pass or what kind of result to demand and typename should be the name used in the foreign language to identify the type of object named by the pointer. It is sufficiently important to be able to check the foreign/3 declarations that Prolog will issue a warning if the typename is not an atom, but it makes no other use of the typename. The typename can even be omitted entirely, using 'address' as an argument specification. In this case, 'plint' will assume that 'address(char)' is the argument type desired.

Note that programs should not rely on numeric relations between foreign language pointers being true of the Prolog integers to which they are converted.

See section I-3-15-5 for an example of passing pointers through the foreign interface. For further examples, see library(charsio) and library(vectors).



Prolog:  +address(typename)
C:       typename *x
Pascal:  type ptr = ^typename;
         x: ptr
FORTRAN: typename x(*)

The argument must be instantiated to an integer, otherwise the call fails. If the argument is 0, the foreign function will receive the NULL pointer. Otherwise the argument will be converted to a pointer. The coding is system-dependent. All you can rely on is that NULL and "malloc() pointers" can be passed from the foreign language to Prolog and that Prolog can then pass the same pointers back to the foreign language.

FORTRAN programmers will note that +address(integer) and +address(float) parameters are useful for passing arrays to FORTRAN, but since FORTRAN has no pointer data type (and no equivalent of malloc()), address results are not possible. Therefore arrays cannot be constructed in FORTRAN and then passed to Prolog; they must be constructed in C or Pascal. section I-3-15-5 gives an example where arrays are constructed in C and later passed to a FORTRAN routine.

The typename must be an atom, but is otherwise ignored by Prolog. It is present for the 'plint' utility which checks that your Prolog foreign/3 facts are compatible with your C source files.



Prolog:  -address(typename)
C:       typename **x;
         *x = ...
Pascal:  type ptr = ^typename;
         var x: ptr;
         x = ...
FORTRAN: Not supported

A pointer to a pointer is passed to the foreign function. It is assumed that the function will overwrite this variable with the result it wishes to return. This result should be either the NULL pointer or a malloc() pointer. When the function returns, the result is converted to a Prolog integer which is then unified with the corresponding argument of the Prolog call. The argument can be of any type; if it cannot be unified with the returned integer, the call fails. If the foreign function does not set the result, the result is undefined.

The typename must be an atom, but is otherwise ignored by Prolog. It is present for the 'plint' utility which checks that your Prolog foreign/3 facts are compatible with your C source files.



Prolog:  [-address(typename)]
C:       typename *f(...)
           {
              typename *x;
              return x;
           }
Pascal:  type ptr = ^typename;
         function f(...): ptr;
           var x: ptr;
           begin
             f := x;
           end
FORTRAN: Not supported

No argument is passed to the foreign function. The return value from the foreign function is assumed to be a pointer to an object of the type indicated by typename. This pointer should be either NULL or a malloc() pointer. It is converted to a Prolog integer which is then unified with the corresponding argument of the Prolog call. The argument can be of any type; if it cannot be unified with the returned integer, the call fails.

The typename must be an atom, but is otherwise ignored by Prolog. It is present for the 'plint' utility which checks that your Prolog foreign/3 facts are compatible with your C source files.



Prolog:  +address
C:       char *x
Pascal:  type charp = ^char;
         x: charp
FORTRAN: Not supported

This is equivalent to +address(char) (see +address(typename) above). Note that +address(char) is not useful in FORTRAN because FORTRAN will not accept a pointer to a character array as representing that array. Therefore +address is not allowed in FORTRAN. To pass a character array to FORTRAN use the +string(N) argument type as described in section I-3-7-3.



Prolog:  -address
C:       char **x
         *x = ...
Pascal:  type charp = ^char;
         var x: charp;
         x = ...
FORTRAN: Not supported

This is equivalent to -address(char) (see -address(typename) above).



Prolog:  [-address]
C:       char *f(...)
           {
             char *x;
             return x;
           }
Pascal:  type charp = ^char;
         function f(...): charp;
           var x: charp;
           begin
             f := x;
           end
FORTRAN: Not supported

This is equivalent to [-address(char)] (see [-address(typename)] above).

I-3-10: Important Prolog Assumptions

For information about memory allocation, see the discussion of PROLOGINITSIZE, PROLOGMAXSIZE and PROLOGINCSIZE IN Chapter G-12.

I-3-11: Debugging Foreign Code Routines

In order to debug foreign code in conjunction with Prolog code, it is necessary to statically link your program together with the Development Kernel as discussed in Chapter H-1. The resulting executable can then be debugged using any standard debugger, such as dbx(1).

Note that it is often useful for debugging purposes to build an application that is linked with QUI, since then both the Prolog and the non-Prolog parts of the application can be debugged simultaneously, using the QUI debugger and the standard debugger respectively. See section H-1-10 for how to do this. If you do this, you may find that the standard debugger gets affected by the way that QUI uses the SIGIO signal. Most standard debuggers provide a way of ignoring specified signals, which is what is needed here. For example, under dbx or dbxtool the command "ignore IO" should be issued before starting up the QUI with the "run" command.

WARNING: Under source-level debuggers such as dbx(1), single stepping out of a function which was called from Prolog does not work properly. You should always 'continue' in such a situation.

I-3-12: Implementation of load_foreign_executable/1

This section gives some information on the implementation of load_foreign_executable/1 which may help in solving more difficult foreign code loading problems. This information applies when the foreign code is being loaded dynamically on top of the Development System. Refer to Chapter H-1 for information on how foreign code is linked into a stand-alone program.

load_foreign_executable/1 loads a shared object file by calling the dlopen(1) library function in SVR4. This automatically loads in any shared libraries that are stored as dependencies in the file and allows the object file to also refer to symbols in the main program (but not any previously dynamically loaded objects).

On Solaris 2.x, to locate a "library" dependency, i.e. one where -lNAME was given to "ld" when constructing the shared object file, load_foreign_executable/1 will search for a file of the form libNAME.so.n, where n is the version number of the library, using the following algorithm:

  1. in the path given by the environment variable LD_LIBRARY_PATH, if set
  2. in the path specified by the -L option to "ld" or environment variable LD_RUN_PATH when constructing the shared object with "ld"
  3. in /usr/lib

When attaching Prolog predicates to foreign functions, these functions are searched for first in the shared object just loaded (and its dependencies) and if not found then in the main program (and its dependencies).

I-3-13: Implementation of load_foreign_files/2

load_foreign_files/2 is implemented by constructing a shared object file and then using the same mechanism to load the shared object file as for load_foreign_executable/1. The shared object file is constructed by the UNIX linker with the following command:


ld -G -o /tmp/qpnnnn.so LinkFile ListOfFiles ListOfLibraries -lc

If any libraries are specified in ListOfLibraries then a LinkFile is generated that references all routines to be accessed by Prolog so that, if any of the specified libraries are static libraries, all the relevant object files will be included in the shared object file. In many cases no additional libraries are required and so ListOfLibraries = [] and no LinkFile is generated.

Note that when using languages other than C, various specific libraries may need to be included (such as '-lpc' or '-lF77').

I-3-14: Library support for linking foreign code

The structs package (see Chapter K-10) allows Prolog to hold pointers to C data structures and arrays and access and store into fields in those data structures in a very efficient way.

Support for translating between Prolog terms and C data structures is provided by library(terms) and lists can be mapped to C arrays with library(vectors). These are useful when you want to pass a complete copy of the data structure over to the other language. If you only want to access parts of a structure then the structs package is recommended.

The 'plint' is a library utility package designed to check if the foreign declarations in Prolog are consistent with the definitions of the functions in the foreign language. It takes a set of Prolog and C files, converts the foreign/3 facts in the Prolog files to a C file which calls each of the foreign functions with the arguments that the foreign/3 fact says it should have, and then calls the UNIX 'lint' command to check that this C file is consistent with the other C files (and that the other C files are consistent with each other). Full documentation for 'plint' can be found in the text file library('plint.doc'). Unfortunately, 'plint' can only be used when the foreign language is C.

I-3-15: Foreign Code Examples: UNIX

This section presents examples of incrementally loading C, Pascal and FORTRAN code into Prolog, using the foreign language interface under UNIX.

I-3-15-1: C Interface

If the C file 'c.c' is compiled as shown below, then loading the Prolog file as shown will produce the indicated results.

IN THE C CODE: c.c


/* c1(+integer, [-integer]) */
long int c1(a)
long int a;
{
   return(a+9);
}

/* c2(-integer) */
void c2(a)
long int *a;
{
   *a = 99;
}

/* c11(+atom, [-atom]) */
QP_atom c11(a)
QP_atom a;
{
   return(a);
}

/* c21(+atom, -atom) */
void c21(a,b)
QP_atom a;
QP_atom *b;
{
   *b = a;
}

/* c3(+float, [-float]) */
double c3(a)
double a;
{
   return(a+9.0);
}
/* c4(-float) */
void c4(a)
float *a;
{
   *a = 9.9;
}
/* c5(string, [-string]) */
char * c5(a)
char * a;
{
   return(a);
}
/* c6(-string) */
void c6(a)
char * *a;
{
   *a = "99";
}

At the command level:


% cc -c c.c

Produces the file 'c.o'.

IN THE PROLOG CODE:


foreign_file(c, [c1, c2, c11, c21, c3, c4, c5, c6]).

foreign(c1,  c, c1(+integer, [-integer])).
foreign(c2,  c, c2(-integer)).
foreign(c11, c, c11(+atom, [-atom])).
foreign(c21, c, c21(+atom, -atom)).
foreign(c3,  c, c3(+float, [-float])).
foreign(c4,  c, c4(-float)).
foreign(c5,  c, c5(+string,[-string])).
foreign(c6,  c, c6(-string)).

:- load_foreign_files([c], []),
   abolish(foreign_file,2),
   abolish(foreign,3).

Loading the Prolog file (see the reference pages for foreign/3, foreign_file/2 and load_foreign_files/2) into Prolog and invoking the following query gives the following results:

| ?- c1(1,X1), c2(X2), c11(foo,X11), c21(foo,X21), c3(1.5,X3), c4(X4),
     c5(foo,X5), c6(X6).

X1 = 10,
X2 = 99,
X11 = X21 = X5 = foo,
X3 = 10.5,
X4 = 9.89999,
X6 = '99' ;


no

I-3-15-2: Pascal Interface

If the Pascal file 'p.p' is compiled as shown below, then loading the Prolog file as shown will produce the indicated results.

IN THE PASCAL CODE: p.p


type
alfa = packed array[1..10] of char;

(* p1(+integer, [-integer]) *)
function p1(a: integer32): integer32;

begin
  p1 := a + 9;
end;

(* p2(-integer) *)
procedure p2(var a: integer32);

begin
  a := 99;
end;

(* p11(+atom, [-atom]) *)
function p11(a: integer32) : integer32;

begin
  p11 := a;
end;

(* p21(+atom, -atom) *)
procedure p21(a: integer32; var b: integer32);

begin
  b := a;
end;

(* p3(+float, [-float]) *)
function p3(a: real) : real;

begin
  p3 := a + 9.0;
end;

(* p4(-float) *)
procedure p4(var a: real);

begin
  a := 9.9;
end;

(* p5(+string(10), -string(10)) *)
procedure p5(var s: alfa; var t: alfa);
begin
        t := s;
end;

(* p6(-string(10)) *)
procedure p6(var s: alfa);
begin
        s := 'output';
end;

 
At the command level:

% pc -c p.p

Produces the file 'p.o'

IN THE PROLOG CODE:


foreign_file(p, [p1, p2, p11, p21, p3, p4, p5, p6]).

foreign(p1,  pascal, p1(+integer, [-integer])).
foreign(p2,  pascal, p2(-integer)).
foreign(p11, pascal, p11(+atom, [-atom])).
foreign(p21, pascal, p21(+atom, -atom)).
foreign(p3,  pascal, p3(+float, [-float])).
foreign(p4,  pascal, p4(-float)).
foreign(p6,  pascal, p5(+string(10),-string(10))).
foreign(p5,  pascal, p6(-string(10))).

:- load_foreign_files([p], ['-lpc']),
   abolish(foreign_file,2),
   abolish(foreign,3).

Loading the Prolog file (see foreign/3) into Prolog and invoking the following query gives the following results:

| ?- p1(1,X1), p2(X2), p11(foo,X11), p21(foo,X21), p3(1.5,X3), p4(X4),
     p5('parameter',X5), p6(X6).

X1 = 10,
X2 = 99,
X11 = X21 = foo,
X3 = 10.5,
X4 = 9.89999 ;
X5 = parameter ;
X6 = output ;

no

NOTES:

  1. Passing of unsized strings (i.e. use of the "string" argument specification in a foreign/3 fact) is not supported in this interface since 'pc' does not have a convention for passing variable length arrays. Instead, padded strings (the "string(N)" argument specification) must be used. Notice that the corresponding parameter of "+string(N)" declaration is actually a call by reference parameter in Pascal procedures.
  2. The "ld" option '-lpc' must be included in the call to load_foreign_files/2 so that the foreign code routine will have access to the standard Pascal library.

I-3-15-3: FORTRAN Interface

If the FORTRAN file 'f.f' is compiled as shown below, then loading the Prolog file as shown will produce the indicated results.

IN THE FORTRAN CODE: f.f


C       f1(+integer, [-integer])
        integer function f1(a)
            integer a

            f1 = a + 9
            return
        end

C       f2(-integer)
        subroutine f2(a)
            integer a

            a = 99
            return
        end

C       f11(+atom, [-atom])
        integer function f11(a)
            integer a

            f11 = a
            return
        end

C       f21(+atom, -atom)
        subroutine f21(a,b)
            integer a
            integer b

            b = a
            return
        end

C       f3(+float, [-float])
        real function f3(a)
            real a

            f3 = a + 9.0
            return
        end

C       f4(-float)
        subroutine f4(a)
            real a

            a = 9.9
            return
        end

C       f5(+string(10), [-string(10)])
        character*10 function f5(s)
        character*10 s

                f5 = s
                return
        end

C       f6(-string(10))
        subroutine f6(s)
        character*10 s

                s = 'output'
                return
        end

At the command level:


% f77 -c f.f

Produces the file 'f.o'.

foreign_file(f, [f1_, f2_, f11_, f21_, f3_, f4_, f5_, f6_]).

foreign(f1_,  fortran, f1(+integer, [-integer])).
foreign(f2_,  fortran, f2(-integer)).
foreign(f11_, fortran, f11(+atom, [-atom])).
foreign(f21_, fortran, f21(+atom, -atom)).
foreign(f3_,  fortran, f3(+float, [-float])).
foreign(f4_,  fortran, f4(-float)).
foreign(f5_,  fortran, f5(+string(10),[-string(10)])).
foreign(f6_,  fortran, f6(-string(10))).

:- load_foreign_files([f], ['-lF77']),
   abolish(foreign_file,2),
   abolish(foreign,3).

Loading the Prolog file (see foreign/3) into Prolog and invoking the following query gives the following results:

| ?- f1(1,X1), f2(X2), f11(foo,X11), f21(foo,X21), f3(1.5,X3), f4(X4),
     f5('parameter',X5), f6(X6).

X1 = 10,
X2 = 99,
X11 = X21 = foo,
X3 = 10.5,
X4 = 9.89999 ;
X5 = parameter ;
X6 = output ;

no

When you load FORTRAN code into a C program, you must ensure that any necessary FORTRAN run-time support code is loaded as well. The FORTRAN run-time library is divided into three parts in UNIX systems based on 4.2BSD:

UNIX systems based on System V have libF77.a and libI77.a but not libU77.a.

To ensure that these libraries will be loaded, use the "ld" options '-lF77', '-lI77', or '-lU77' respectively.

You should check your FORTRAN documentation for advice about combining FORTRAN subroutines with a C main program.

NOTES:

  1. Passing of unsized strings (for example, use of the "string" argument specification in a foreign/3 fact) is not supported in this interface. Instead, padded strings (the "string(N)" argument specification) must be used.
  2. The names of subroutines passed to the predicates foreign_file/2 and foreign/3 must end with an underscore (_) to comply with the way in which 'f77' generates external symbols.
  3. The FORTRAN run-time library has been seen documented as '-lf77'. As case is significant in loader options, be sure to load this library using '-lF77'.

I-3-15-4: Assembly Code Interface

The following example is written in Sun-3 MC68020 assembly language and emulates the Sun-3 C compiler's calling conventions. This code will not run if your configuration is in any way different from this.

If the Assembly file 's.s' is assembled as shown below, then loading the Prolog file as shown will produce the indicated results.

IN THE ASSEMBLY CODE: s.s


        .text
        .globl  _s1
_s1:    link    a6,#0
        movl    a6@(8),d0
        addl    #9,d0
        unlk    a6
        rts

        .text
        .globl  _s2
_s2:    link    a6,#0
        movl    a6@(8),a0
        movl    #99,a0@
        unlk    a6
        rts

        .text
        .globl  _s11
_s11:   link    a6,#0
        movl    a6@(8),d0
        unlk    a6
        rts

        .text
        .globl  _s21
_s21:   link    a6,#0
        movl    a6@(12),a0
        movl    a6@(8),a0@
        unlk    a6
        rts

        .text
        .globl  _s3
_s3:    link    a6,#0
        .data
        .even
L26:    .long   0x40220000, 0x0
        .text
        movl    a6@(8),d0
        movl    a6@(12),d1
        lea     L26,a0
        jsr     fvaddi
        unlk    a6
        rts

        .text
        .globl  _s4
_s4:    link    a6,#0
        .data
        .even
L30:    .long   0x411E6666
        .text
        movl    a6@(8),a0
        movl    L30,a0@
        unlk    a6
        rts

        .text
        .globl  _s5
_s5:    link    a6,#0
        movl    a6@(8),d0
        unlk    a6
        rts

        .text
        .globl  _s6
_s6:    link    a6,#0
        .data
L37:    .ascii  "99\0"
        .text
        movl    a6@(8),a0
        movl    #L37,a0@
        unlk    a6
        rts
        .data
        .globl  ieeeused

 
At the command level:

% as s.s -o s.o

Produce the file 's.o'.

IN THE PROLOG CODE:


foreign_file(s, [s1, s2, s11, s21, s3, s4, s5, s6]).

foreign(s1,  c, s1(+integer, [-integer])).
foreign(s2,  c, s2(-integer)).
foreign(s11, c, s11(+atom, [-atom])).
foreign(s21, c, s21(+atom, -atom)).
foreign(s3,  c, s3(+float, [-float])).
foreign(s4,  c, s4(-float)).
foreign(s5,  c, s5(+string,[-string])).
foreign(s6,  c, s6(-string)).

:- load_foreign_files([s], []),
   abolish(foreign_file,2),
   abolish(foreign,3).

Loading the Prolog file (see the reference page for foreign/3) into Prolog and invoking the following query gives the following results:

| ?- s1(1,X1), s2(X2), s11(foo,X11), s21(foo,X21), s3(1.5,X3), s4(X4),
     s5(foo,X5), s6(X6).

X1 = 10,
X2 = 99,
X11 = X21 = X5 = foo,
X3 = 10.5,
X4 = 9.89999,
X6 = '99' ;

no

NOTES:

  1. The second argument of the foreign/3 fact specifies a language (either 'c', 'pascal', or 'fortran'). The assembly code must emulate the exact calling conventions of this language. The assembly code should expect the size and form of the arguments to be the same as those expected by the appropriate language above.
  2. An entry point to an assembler routine must be a label prefixed with an underscore (_). This label must have a corresponding "global" (external) declaration in the assembly source file. The additional underscore does not appear in the entry point name when used in both the foreign_file/2 and foreign/3 facts.

I-3-15-5: Passing pointers between Prolog and Foreign Code

Suppose we have a FORTRAN subroutine which multiplies a 3-element vector by a 3-by-3 matrix, returning a 3-element vector. This situation is then represented by the following code:


C code:

typedef double vec_3[3];
typedef double mat_3_3[3][3];

vec_3 *make_vec(a, b, c)
    double a, b, c;
    {
        register vec_3 *x;

        x = (vec_3*)malloc(sizeof(vec_3));
        (*x)[0] = a, (*x)[1] = b, (*x)[2] = c;
        return x;
    }

mat_3_3 *make_mat(a0, a1, a2, b0, b1, b2, c0, c1, c2)
    double a0, a1, a2, b0, b1, b2, c0, c1, c2;
    {
        register mat_3_3 *x;

        x = (mat_3_3*)malloc(sizeof(mat_3_3));
        (*x)[0][0] = a0, (*x)[0][1] = a1, (*x)[0][2] = a2,
        (*x)[1][0] = b0, (*x)[1][1] = b1, (*x)[1][2] = b2,
        (*x)[2][0] = c0, (*x)[2][1] = c1, (*x)[2][2] = c2;
        return x;
    }





FORTRAN code:

      subroutine matvec(mat, vec, ans)
          real mat(3,3), vec(3), ans(3)

          ans(1) = mat(1,1)*vec(1)+mat(2,1)*vec(2)+mat(3,1)*vec(3)
          ans(2) = mat(1,2)*vec(2)+mat(2,2)*vec(2)+mat(3,2)*vec(3)
          ans(3) = mat(1,3)*vec(3)+mat(2,3)*vec(2)+mat(3,3)*vec(3)
          return
      end





Prolog Code:

foreign(make_vec, c, make_vec(+float,+float,+float,
                              [-address(vec_3)])).
foreign(make_mat, c, make_mat(+float,+float,+float,
                              +float,+float,+float,
                              +float,+float,+float,
                              [-address(mat_3_3)])).
foreign(matvec_, fortran, matvec(+address(float),+address(float),
                                +address(float))).      % note all +!

make_vec([A,B,C], X) :-
        make_vec(A, B, C, X).

make_mat([[A0,A1,A2],[B0,B1,B2],[C0,C1,C2]], X) :-
        make_mat(A0,A1,A2, B0,B1,B2, C0,C1,C2, X).

do_matvec(Vec, Mat, AnsObj) :-
        make_vec(Vec, VecObj),
        make_mat(Mat, MatObj),
        make_vec(0.0, 0.0, 0.0, AnsObj),
        matvec(VecObj, MatObj, AnsObj).

I-3-16: Summary of Predicates and Functions

Reference pages (in Chapter L) for the following provide further detail on the material in this chapter.

QP_atom_from_padded_string() QP_unify() QP_get*() foreign/3 QP_is*() foreign_file/2 QP_next_solution() load_foreign_executable/1 QP_put*() load_foreign_files/2 QP_string_from_padded_atom() QP_term_type()




     

             

I-3-17: Library Support

plint vectors terms


Copyright (C) 1998 SICS
contact: product support sales information