Go to the previous, next section.

Mixing C and Prolog

SICStus Prolog provides a bi-directional, procedural interface for program parts written in C and Prolog. The C side of the interface defines a number of functions and hooks for various operations. On the Prolog side, you have to supply declarations specifying the names and argument/value types of C functions being called as Prolog predicates. These declarations are used by the predicate load_foreign_files/2, which performs the actual binding of C functions to Prolog predicates.

In most cases, the argument/value type declaration suffice for making the necessary conversions of data automatically as they are passed between C and Prolog. However, it is possible to declare the type of an argument to be a Prolog term, in which case the receiving function will see it as a "handle" object, called an SP_term_ref.

The C support routines are available in a Development System as well as in Runtime Systems. The support routines include:

Throughout this chapter, void * in the function definitions may be changed to char * on non ANSI conforming C compilers.

The environment variable SP_PATH is assumed to be set to the name of the SICStus Prolog installation directory. This environment variable plays three distinct roles in this chapter:

Type definitions and function declarations for the interface are found in the header file `<sicstus/sicstus.h>'.

The value of many support functions is a return code which is one of SP_SUCCESS for success, SP_FAILURE for failure, SP_ERROR if an error condition occurred. In particular, uncaught exceptions resulting from calls from C to Prolog raise an error condition. In error situations, the variable SP_errno is set to a value describing the error condition:

int SP_errno

The function SP_error_message returns a pointer to the diagnostic message corresponding to a specified error number:

char *SP_error_message(int errno)

Calling C from Prolog

Functions written in the C language (or any other language that uses the same calling conventions) may be called from Prolog using an interface in which automatic type conversions between Prolog terms and common C types are declared as Prolog facts. Calling without type conversion can also be specified, in which case the arguments and values are passed as SP_term_refs. This interface is modeled after Quintus Prolog.

The interface can be used in two ways. With dynamic linking, foreign language modules may be linked in as needed. However: once a module has been linked in to the Prolog load image it is not possible to unlink the module. With static linking, foreign language modules are statically linked to the SICStus Prolog emulator, and only the binding of predicate names to functions needs to be done at runtime. In Runtime Systems, static linking is the only option available.

The functions interfaced using this foreign language interface may invoke Prolog code and use the support functions described in the other sections of this chapter.

Interface Predicates

To perform either dynamic or static linking, the following predicates are used:

@
A hook predicate. Specifies that a set of C language functions, to be called from Prolog, are to be found in ObjectFile. Functions is a list of functions exported by ObjectFile. Only functions that are to be called from Prolog should be listed. For example:

foreign_file('terminal.o', [scroll,pos_cursor,ask]).

specifies that functions scroll(), pos_cursor() and ask() are to be found in object file `terminal.o'.

@
@
Hook predicates, specify the Prolog interface to a C function. Language is at present constrained to the atom c. CFunctionName is the name of a C function. Predicate specifies the name of the Prolog predicate that will be used to call CFunction(). Predicate also specifies how the predicate arguments are to be translated into the corresponding C arguments.

foreign(pos_cursor, c, move_cursor(+integer, +integer)).

The above example says that the C function pos_cursor() has two integer value arguments and that we will use the predicate move_cursor/2 to call this function. A goal move_cursor(5, 23) would translate into the C call pos_cursor(5,23);.

@
Hookable, links ObjectFiles into the Prolog load image. ObjectFiles is a list of C object files. Libraries is a list of libraries. The C library '-lc' will always be used and need not be specified.

ObjectFiles may be prefixed by a module name (see section Module Prefixing), in which case the calls to foreign_files/2 and foreign/(2-3) are made in that module and the predicates defined are placed in it.

By default, dynamic linking is performed. If, however, the SICStus Prolog emulator has been statically linked with foreign language modules, static linking is performed and Libraries is simply ignored. In Muse, the system is required to be adjusted to one worker first, and the predicates defined become cavalier (see section Programming Considerations).

Example:

| ?- load_foreign_files(['terminal.o'], []).

@
Generates the glue code necessary for static linking for the functions declared for the C object files specified as ObjectFiles. This must be a list of all the object files that are going to be used by load_foreign_files/2.

ObjectFiles may be prefixed by a module name (see section Module Prefixing), in which case the calls to foreign_files/2 and foreign/(2-3) are made in that module.

The glue code is written to `flinkage.c' (in the current working directory). This predicate is not available in Runtime Systems and is not needed at all for dynamic linking.

It is recommended that foreign_file/2 and foreign/(2-3) be declared as dynamic, and written entirely as unit clauses. The utility package library(flinkage), which scans a set of Prolog files for occurrences of foreign declarations, requires this.

The third argument of the predicate foreign/3 specifies how to translate between Prolog arguments and C arguments. A call to a foreign predicate will raise an exception if an input arguments is uninstantiated (instantiation_error/2) or has the wrong type (type_error/4) or domain (domain_error/4). The call will fail upon return from the function if the output arguments do not unify with the actual arguments.

@
@
The argument should be a number. It is converted to a C long and passed to the C function.
@
@
The argument should be a number. It is converted to a C double and passed to the C function.
@
@
The argument should be an atom. Its canonical representation is passed to the C function.
@
@
The argument should be a list of character codes. The C function will be passed the address of an array of characters containing these characters. The array is subject to reuse by other support functions, so if the value is going to be used on a more than temporary basis, it must be moved elsewhere.
@
@
The argument should be an atom. The C function will be passed the address of a text string containing the printed representation of the atom. The C function should not overwrite the string.
@
@
The argument should be an atom. The printable representation of the string will be copied into a newly allocated buffer. The string will be truncated if it is longer than N characters. The string will be blank padded on the right if it is shorter than N characters. The C function will be passed the address of the buffer. The C function may overwrite the buffer, but should not assume that it remains valid after returning.

@
@
The argument should be an integer which value is constrained according to (see section Creating Prolog Terms, SP_put_address()). The value passed will be a void * pointer.
@
@
The argument should be an integer which value is constrained according to (see section Creating Prolog Terms, SP_put_address()). The value passed will be a TypeName * pointer.
@
@
The argument could be any term. The value passed will be the internal representation of the term.
@
@
The C function is passed a reference to an uninitialized long. The value returned will be converted to a Prolog integer.
@
@
The C function is passed a reference to an uninitialized double. The value returned will be converted to a Prolog float.
@
@
The C function is passed a reference to an uninitialized unsigned long. The value returned should be the canonical representation of a Prolog atom.
@
@
The C function is passed the address of an uninitialized char *. The returned string will be converted to a Prolog list of character codes.
@
@
The C function is passed the address of an uninitialized char *. The returned string will be converted to a Prolog atom. Prolog will copy the string to a safe place, so the memory occupied by the returned string may be reused during subsequent calls to foreign code.
@
@
The C function is passed a reference to a character buffer large enough to store an N character string. The returned string will be stripped of trailing blanks and converted to a Prolog atom.
@
@
The C function is passed the address of an uninitialized void *. The returned value, which is constrained according to (see section Creating Prolog Terms, SP_put_address()), will be converted to a Prolog integer.
@
@
The C function is passed the address of an uninitialized TypeName *. The returned value, which is constrained according to (see section Creating Prolog Terms, SP_put_address()), will be converted to a Prolog integer.
@
@
The C function is passed a new SP_term_ref, and is expected to set its value to a suitable Prolog term. Prolog will try to unify the value with the actual argument.
@
@
The C function should return a long. The value returned will be converted to a Prolog integer.
@
@
The C function should return a double. The value returned will be converted to a Prolog float.
@
@
The C function should return an unsigned long. The value returned must be the canonical representation of a Prolog atom.
@
@
The C function should return a char *. The returned string will be converted to a Prolog list of character codes.
@
@
The C function should return a char *. The returned string will be converted to a Prolog atom. Prolog will copy the string to a safe place, so the memory occupied by the returned string may be reused during subsequent calls to foreign code.
@
@
The C function should return a char *. The first N characters of the string will be copied and the copied string will be stripped of trailing blanks. The stripped string will be converted to a Prolog atom. C may reuse or destroy the string buffer during later calls.
@
@
The C function should return a void *. The returned value, which is constrained according to (see section Creating Prolog Terms, SP_put_address()), will be converted to a Prolog integer.
@
@
The C function should return a TypeName *. The returned value, which is constrained according to (see section Creating Prolog Terms, SP_put_address()), will be converted to a Prolog integer.
@
@
The C function should return an SP_term_ref. Prolog will try to unify its value with the actual argument.

Foreign Language Interface with Static Linking

Using static linking, the foreign language modules are statically linked with the emulator code and with glue code. The glue code is created by first loading into SICStus Prolog all foreign_file/2 and foreign/(2-3) declarations that are going to be used by load_foreign_files/2, and then calling prepare_foreign_files/1 as described above.

Once the glue code has been generated, the foreign language modules can be statically linked with the emulator. The following steps are typically performed to produce a Development System with statically linked foreign functions:

% sicstus
SICStus 3 #0: Wed Mar 15 12:29:29 MET 1995
| ?- consult(PL_MODULES),
     prepare_foreign_files(ObjectFiles).
| ?- ^D

% cc -o NAME flinkage.c C_MODULES \
  $SP_PATH/bin/sp.o -lm LDFLAGS

where

@
is the name of the resulting executable

@
is the Prolog files containing foreign/(2-3) and foreign_file/2 declarations.

@
is the C code to be called from Prolog

@
are any C libraries required by C_MODULES, and any machine-dependent flags that may be required for correct linkage (see `PortNote' in the SICStus Prolog source directory).

The executable must be "bootstrapped" as:

% ./NAME -f -b $SP_PATH/bin/sp.ql

As part of the bootstrapping process, the executable will assert a clause

library_directory(LibDir).

where LibDir is derived from $SP_PATH, and the usual Prolog top-level will appear. At this time, a saved state may be created by calling save_program/1. This saved state can be used instead of "bootstrapping" for fast start-up. The saved state is itself an executable file (a Shell script), and has NAME encoded in it.

Support Functions

The support functions include functions to manipulate SP_term_refs, functions to convert data between the basic C types and Prolog terms, functions to test whether a term can be converted to a specific C type, and functions to unify or compare two terms.

Creating and Manipulating SP_term_refs

Normally, C functions only have indirect access to Prolog terms via SP_term_refs. C functions may receive arguments as unconverted Prolog terms, in which case the actual arguments received will have the type SP_term_ref. Also, a C function may return an unconverted Prolog term, in which case it must create an SP_term_ref. Finally, any temporary Prolog terms created by C code must be handled as SP_term_refs.

SP_term_refs are motivated by the fact that SICStus Prolog's memory manager must have a means of reaching all live Prolog terms for garbage collecting and stack shifting purposes, including such terms that are being manipulated by the user's C code. Previous releases of SICStus Prolog provided direct access to Prolog terms and the ability to tell the memory manager that a given memory address points to a Prolog term, but this approach was too low level and highly error-prone. The current design is modeled after and largely compatible with Quintus Prolog release 3.

SP_term_refs are created dynamically. At any given time, an SP_term_ref has a value (a Prolog term). This value can be examined, accessed, and updated by the support functions described in this section.

It is important to understand the rules governing the scope of SP_term_refs in conjunction with calls from Prolog to C and vice versa:

A new SP_term_ref whose value is [] is created by calling

SP_term_ref SP_new_term_ref(void)

The value of the SP_term_ref to is set to the value of the SP_term_ref from by calling SP_put_term(to,from). The previous value of to is lost:

void SP_put_term(SP_term_ref to, SP_term_ref from)

Each Prolog atom is represented internally by a unique integer, represented in C as an unsigned long. This mapping between atoms and integers depends on the execution history. Certain functions require this representation as opposed to an SP_pred_ref. It can be obtain by a special argument type declaration when calling C from Prolog, by calling SP_get_atom(), or by looking up a string s in the Prolog symbol table by calling SP_atom_from_string(s):

unsigned long SP_atom_from_string(char *s)

The print name of a Prolog atom a can be obtained by calling:

char *SP_string_from_atom(unsigned long a)

Creating Prolog Terms

These functions create a term and store it as the value of an SP_term_ref, which must exist prior to the call. They return zero if the conversion fails (as far as failure can be detected), and a nonzero value otherwise, assigning to t the converted value.

@
Assigns to t a new Prolog variable.

@
Assigns to t a Prolog integer from a C long integer.

@
Assigns to t a Prolog float from a C double.

@
Assigns to t a Prolog atom from a, which must be the canonical representation of a Prolog atom. (see section Calling C from Prolog).

@
Assigns to t a Prolog atom from a C string.

@
Assigns to t a Prolog integer from a C pointer.

The pointer must be NULL or an address having the four most significant bits in common with addresses returned by malloc(). (This excludes pointers to automatic C variables, i.e. addresses to the C stack). Further on the address must be aligned on a four bytes boundary.

NULL is converted to the integer 0.

@
Assigns to t a Prolog list of the character codes in s. The list is terminated by the value of tail.

@
Assigns to t a Prolog number by parsing the string in s.

@
Assigns to t a Prolog compound term with all the arguments unbound variables. If arity is 0, assigns the Prolog atom whose canonical representation is name to t. This is similar to calling functor/3 with the first argument unbound and the second and third arguments bound.

@
Assigns to t a Prolog list whose head and tail are both unbound variables.

@
Assigns to t a Prolog compound term whose arguments are the values of arg... If arity is 0, assigns the Prolog atom whose canonical representation is name to t. This is similar to calling =../2 with the first argument unbound and the second argument bound.

@
Assigns to t a Prolog list whose head and tail are the values of head and tail.

Accessing Prolog Terms

These functions will take an SP_term_ref and convert it to C data. They return zero if the conversion fails, and a nonzero value otherwise, and (except the last one) store the C data in output arguments.

@
Assigns to *l the C long corresponding to a Prolog number. The value must fit in *l for the operation to succeed.

@
Assigns to *d the C double corresponding to a Prolog number.

@
Assigns to *a the canonical representation of a Prolog atom.

@
Assigns to **name a pointer to the string that is the name of a Prolog atom. This string must not be modified.

@
Assigns to **pointer a C pointer from a Prolog term. The term should be an integer which value is constrained according to (see section Creating Prolog Terms, SP_put_address()).

@
Assigns to **s a zero-terminated array of characters corresponding to a Prolog list of character codes. The array is subject to reuse by other support functions, so if the value is going to be used on a more than temporary basis, it must be moved elsewhere.

@
Assigns to **s a zero-terminated array of characters corresponding to the printed representation of a Prolog number. The array is subject to reuse by other support functions, so if the value is going to be used on a more than temporary basis, it must be moved elsewhere.

@
Assigns to *name and *arity the C name and arity from the principal functor of a Prolog compound term. If the value of t is an atom, then that atom is assigned to *name and 0 is assigned to *arity. This is similar to calling functor/3 with the first argument bound and the second and third arguments unbound.

@
Assigns to *head and *tail the head and tail of a Prolog list.

@
Assigns to *arg the i:th argument of a Prolog compound term. This is similar to calling arg/3 with the third argument unbound.

Testing Prolog Terms

There is one general function for type testing of Prolog terms and a set of specialized, more efficient, functions, one for each term type.

@
Depending on the type of the term t, one of SP_TYPE_VARIABLE, SP_TYPE_INTEGER, SP_TYPE_FLOAT, SP_TYPE_ATOM, or SP_TYPE_COMPOUND is returned.

@
Returns nonzero if the term is a Prolog variable, zero otherwise.

@
Returns nonzero if the term is a Prolog integer, zero otherwise.

@
Returns nonzero if the term is a Prolog float, zero otherwise.

@
Returns nonzero if the term is a Prolog atom, zero otherwise.

@
Returns nonzero if the term is a Prolog compound term, zero otherwise.

@
Returns nonzero if the term is a Prolog list, zero otherwise.

@
Returns nonzero if the term is an atomic Prolog term, zero otherwise.

@
Returns nonzero if the term is a Prolog number, zero otherwise.

Unifying and Comparing Terms

@
Unifies two terms, returning zero on failure and nonzero on success.

@
Returns -1 if x @< y, 0 if x == y and 1 if x @> y

Memory Allocation

The usual C library memory allocation functions (malloc, realloc, and free) may not work properly in foreign code. The following functions provide these services from SICStus Prolog's memory manager:

@
Returns a properly aligned pointer to a block of at least size bytes. In Muse, the blocks are allocated in shared memory.

@
Changes the size of the block referenced by ptr to size bytes and returns a pointer to the (possibly moved) block. The contents will be unchanged up to the lesser of the new and old sizes. The block referenced by ptr must have been obtained by a call to SP_malloc or SP_realloc, and must not have been released by a call to SP_free or SP_realloc. In Muse, the blocks are allocated in shared memory.

@
Releases the block referenced by ptr, which must have been obtained by a call to SP_malloc or SP_realloc, and must not have been released by a call to SP_free or SP_realloc. In Muse, the blocks are allocated in shared memory.

Calling Prolog from C

In the sequential Development System and in Runtime Systems, Prolog and C code may call each other to arbitrary depths. The ability to call Prolog from C is not available in the Muse Development System in this release.

Before calling a predicate from C you must look up the predicate definition by module, name, and arity. The function SP_predicate() will return a pointer to this definition or return NULL if the predicate is not visible in the module. This definition could be used in more than one call to the same predicate. The module specification is optional. If NULL or "" (the empty string) is given then the default type-in module (see section Module Prefixing) is assumed:

SP_pred_ref SP_predicate(char *name_string,
                         long arity,
                         char *module_string)

The function SP_pred() may be used as an alternative to the above. The only difference is that the name and module arguments are passed as Prolog atoms rather than strings, and the module argument is mandatory. This saves the cost of looking up the two arguments in the Prolog symbol table. This cost dominates the cost of SP_predicate().

SP_pred_ref SP_pred(unsigned long name_atom,
                    long arity,
                    unsigned long module_atom)

Finding One Solution of a Call

The easiest way to call a predicate if you are only interested in the first solution is to call the function SP_query(). It will create a goal from the predicate definition and the arguments, and try to prove it. The call will return SP_SUCCESS if the goal succeeded, SP_FAILURE if it failed, and SP_ERROR if an error condition occurred.

int SP_query(SP_pred_ref predicate, SP_term_ref arg1, ...)

If you are only interested in the side effects of a predicate you can call SP_query_cut_fail(). It will try to prove the predicate, cut away the rest of the solutions, and finally fail. This will reclaim the storage used after the call:

int SP_query_cut_fail(SP_pred_ref predicate, SP_term_ref arg1, ...)

Finding Multiple Solutions of a Call

If you are interested in more than one solution a more complicated scheme is used. You find the predicate definition as above but you don't call the predicate directly.

  1. Set up a call with SP_open_query()
  2. Call SP_next_solution() to find a solution. Call this predicate again to find more solutions if there are any.
  3. Terminate the call with SP_close_query() or SP_cut_query()

The function SP_open_query() will return an identifier of type SP_qid that you use in successive calls, or NULL, if given an invalid predicate reference. Note that if a new query is opened while another is already open, the new query must be terminated before performing any action on the old one. That is, queries must be strictly nested:

SP_qid SP_open_query(SP_pred_ref predicate, SP_term_ref arg1, ...)

The function SP_next_solution() will cause the Prolog engine to find solutions of the open query. The SP_term_refs that you sent with the call to SP_open_query() will be assigned new values. SP_next_solution() will return SP_SUCCESS for success, SP_FAILURE for failure, SP_ERROR if an error condition occurred.

int SP_next_solution(SP_qid query)

You can terminate a query in two ways. The function SP_cut_query() will only take away the choices created since the corresponding SP_open_query(). The data created in the call are still valid and could be passed to another call. The function SP_close_query() will restore the state to what it was before the call to SP_open_query(). If the call created data that you want to keep, it must be converted to C data before calling SP_close_query(). Both functions return SP_SUCCESS for success and SP_ERROR for invalid usage:

int SP_cut_query(SP_qid query)

int SP_close_query(SP_qid query)

Calling Prolog Asynchronously

A Prolog execution may be interrupted by UNIX signals. If you wish to call Prolog back from a signal handler you cannot use SP_query() etc. directly. The call to Prolog has to be delayed until a time when the Prolog execution can accept an interrupt. The function SP_event() serves this purpose, and installs the function func to be called from Prolog when the execution can accept a callback. Returns non-zero iff installation succeeded. func is called with arg as first argument.

A queue of functions, with corresponding arguments, is maintained; that is, if several calls to SP_event() occur before Prolog can a accept an interrupt, the functions are queued and executed in turn at the next possible opportunity. Note that the queuing facility is only safe for signal handlers installed using SP_signal() (see below).

Depending on the value returned from func, the interrupted Prolog execution will just continue (SP_SUCCESS), backtrack (SP_FAILURE), or process an exception (SP_ERROR). These values are the ones returned from the functions for calling Prolog from C. In case of fail or exception the event queue is flushed:

int SP_event(int (*func)(), void *arg)

A UNIX signal handler having called SP_event() should call SP_continue() as its last action, to ensure that the interrupt is processed as early as possible:

void SP_continue()

To install a function, func, as a handler for the signal sig, call:

void (*SP_signal (int sig, void (*func)()))()

SP_signal() will also, if permitted by the operating system, add sig to a set of signals which are all blocked during the handling of the event queue. Some operating systems require that:

void (*SP_reinstall_signal (int sig, void (*func)()))()

be called from a signal handler to unblock or reinstall the handler. This function should be called before SP_continue().

The following piece of C code illustrates these facilities. The function signal_init() installs the function signal_handler() as the primary UNIX signal handler for the signals USR1 and USR2. That function invokes the predicate prolog_handler/1 as the actual signal handler, passing the signal number as an argument to the predicate.

SP_pred_ref event_pred;

static int signal_event(signal_no)
     void *signal_no;
{
  SP_term_ref x=SP_new_term_ref();

  SP_put_integer(x, (int)signal_no);
  return SP_query(event_pred, x);
}

static void signal_handler(sig)
     int sig;
{
  SP_event(signal_event, (void *)sig);
  SP_reinstall_signal(sig, signal_handler);
  SP_continue();
}

void signal_init()
{
  event_pred = SP_predicate("prolog_handler",1,"");
  
  SP_signal(SIGUSR1, signal_handler);
  SP_signal(SIGUSR2, signal_handler);
}

Exception Handling in C

When an exception has been raised, the functions SP_query(), SP_query_cut_fail() and SP_next_solution() return SP_ERROR. To access the exception term (the argument of the call to raise_exception/1), which is asserted when the exception is raised, the function SP_exception_term() is used. As a side effect, the exception term is retracted, so if your code wants to pass the exception term back to Prolog, it must use the SP_raise_exception() function below. If an exception term exists, SP_exception_term() retracts it and stores it as the value of an SP_term_ref which must exist prior to the call and returns nonzero. Otherwise, it returns zero.

int SP_exception_term(SP_term_ref t)

To raise an exception from a C function called from Prolog, just call SP_raise_exception(t) where t is the SP_term_ref whose value is the exception term. The glue code will detect that an exception has been raised, any value returned from the function will be ignored, and the exception will be passed back to Prolog.

void SP_raise_exception(SP_term_ref t)

SICStus Streams

With the SICStus Prolog C interface, the user can define his/her own streams as well as from C read or write on the predefined streams. The stream interface is modeled after Quintus Prolog release 2. It provides:

Prolog Streams

From the Prolog level there is a unique number that identifies a stream. This identifier can be converted from/to a Prolog stream:

@
@
StreamCode is the C stream identifier (an integer) corresponding to the Prolog stream Stream. This predicate is only useful when streams are passed between Prolog and C. Note that StreamCode no longer has a relation to the UNIX file descriptor.

The StreamCode is a Prolog integer representing a SP_stream * pointer as described in (see section Creating Prolog Terms, SP_put_address()).

To write on a Prolog stream from C, special versions of the most common standard C IO functions are used:

@
@
@
@
@
@
@
@
@
@

There are three predefined streams accessible from C:

@
Standard input. Refers to the same stream as user_input in Prolog. Which stream is referenced by user_input is controlled by the flag user_input (see prolog_flag/3) .

@
Standard output. Refers to the same stream as user_output in Prolog. Which stream is referenced by user_output is controlled by the flag user_output (see prolog_flag/3).

@
Standard error. Refers to the same stream as user_error in Prolog. Which stream is referenced by user_error is controlled by the flag user_error (see prolog_flag/3).

@
Current input. It is initially set equal to SP_stdin. It can be changed with the predicates see/1 and set_input/1.

@
Current output. It is initially set equal to SP_stdout. It can be changed with the predicates tell/1 and set_output/1.

Note that these variables are read only. They are set but never read by the stream handling.

Defining a New Stream

The following steps are required to define a new stream in C:

Low Level IO Functions

For each new stream the appropriate low level IO functions have to be defined. Error handling, prompt handling and character counting is handled in a layer above these functions. They all operate on a user defined private data structure pointed out by user_handle in SP_stream.

User defined low level IO functions may invoke Prolog code and use the support functions described in the other sections of this chapter.

@
Should return the character read or -1 on end of file.

@
Should write the character c and return the character written.

@
Flush the stream. Should return 0 on success, EOF on error.

@
Should return 1 on end of file, else 0.

@
Clear the error level.

@
Close the stream. Should return zero.

Installation

A new stream is made accessible to Prolog with the function SP_make_stream().

int SP_make_stream(
        void *handle,
        int (*sgetc)(),
        int (*sputc)(),
        int (*sflush)(),
        int (*seof)(), 
        void (*sclrerr)(),
        int (*sclose)(),
        SP_stream **stream)

The function will:

The handle pointer will be supplied as the handle argument in the calls to the low level functions.

A stream without a close function will be treated as not closable i.e. close/1 will not have any effect on it.

Internal Representation

For most streams you don't have to know anything about the internal representation but there may be occasions when you have to set some fields manually or do some processing on all streams of a particular type. SICStus Prolog maintain a circular list of stream objects of type SP_stream.

@
@
Used for linking streams together. The insertion is done by SP_make_stream() and the deletion is done from the Prolog predicate close/1.

@
This field is set to the empty string, "", by SP_make_stream(). May be set to a suitable string, provided the string will not be overwritten until the stream is closed.

@
A bit vector that contains information about the access modes supported, if the stream is a TTY stream etc. It is not available to the user but the TTY property can be set by the function:

@

  • int fd; The IO descriptor if the stream is associated with a file, socket etc. Otherwise a negative number.

  • void *user_handle; This is the pointer to the user supplied private data for the stream. In the case of SICStus Prolog predefined file streams the user_handle could be a pointer to the standard IO FILE.
  • There is no standard way to tell if a stream is user defined. You have to save pointers to the streams created or check if one of the stream functions installed is user defined, i.e:

    int is_my_stream(SP_stream *s)
    {
      return (s->sclose == my_close);
    }
    

    Muse Support Functions

    Dynamic loading of foreign language functions with load_foreign_files/2 must be done when the system is adjusted to one worker. If you use dynamic linking, it is not recommended to use C-global or static local variables. It is platform dependent if the variables are shared among workers or private. If static linking is used, the variables become private.

    Some useful Muse C-functions:

    @
    Returns the maximum number of workers. Also available as a read-only Muse flag.

    @
    Returns the actual number of allocated workers. Also available as a writable Muse flag.

    @
    Returns the worker identity as a non-negative integer. Also available as a read-only Muse flag.

    @
    @
    @
    The muse_lock and muse_un_lock functions is used to implement mutual exclusion regions. Note: the actual locks in Muse may not be int but they are of equal or smaller size. A Muse lock must be initialized with muse_init_lock() before it can be used.

    In Muse, dynamic memory allocation is performed in shared memory. To allocate worker private memory, it is recommended (as worker_counters in the example see section Muse FLI Example) to allocate an array of size muse_max_workers() that is indexed by the worker id (muse_worker_id()).

    User Hooks

    The user may define functions to be called at certain occasions by the Prolog system. This is accomplished by assigning functions pointers to special hook variables. The functions can be removed by assigning NULL to the hooks.

    @
    Called before reading a character from fd provided it is associated with a terminal device. This function shall return nonzero when there is input available at fd. It is called repeatedly until it returns nonzero. Not available in Muse.

    @
    Called upon abort and reinitialization. The call is made after SICStus Prolog's signal handler installation but before the built-in predicates version/0 and initialization/0 are called. Calling Prolog from functions invoked through this hook is not supported. (This hook is not available in Runtime Systems.)

    @
    Called after restoring saved states with the program arguments as arguments. The call is made after SICStus Prolog's signal handler installation. Calling Prolog from functions invoked through this hook is not supported. (This hook is not available in Runtime Systems.)

    Runtime Systems

    It is possible to mix C and Prolog code to create stand-alone applications, Runtime Systems. In a Runtime System there are some differences in the behavior of the Prolog engine and many built-in predicates are omitted or have limited functionality:

    Initializing the Prolog Engine

    You must call SP_initialize() before any other calls. The function will allocate data areas used by Prolog, initialize command line arguments so that they can be accessed by the argv Prolog flag and load the Runtime Library:

    int SP_initialize(int argc, char **argv, char *boot_path)
    

    boot_path should be the name of a directory, usually `$SP_PATH/bin', containing the compiled Runtime Library `runtime.ql'.

    If boot_path is NULL, SP_initialize() will look up the value of the environment variable SP_PATH and look for the file `$SP_PATH/bin/runtime.ql' that contains the Runtime Library. If SP_PATH is undefined it will look in the current working directory as a last resort.

    It returns SP_SUCCESS if initialization was successful, and SP_ERROR otherwise.

    Optionally, you may also call SP_force_interactive() before any other calls. This will force the I/O built-in predicates to treat the standard input stream as a terminal, even if it does not appear to be a terminal. Same as the `-i' option in Development Systems. (see section Getting Started).

    void SP_force_interactive(void)
    

    Loading Prolog code

    You can load your Prolog code compiled to Prolog object files into the system with the call SP_load(). This is the C equivalent of the Prolog predicate load/1:

    int SP_load(char *ql_file)
    

    SP_load() will return SP_SUCCESS for success or SP_ERROR if an error condition occurred.

    Creating the Executable

    To create a Runtime System you have to link the Runtime Kernel (`$SP_PATH/bin/runtime.o') with your own code. Unfortunately, the Runtime Kernel contains symbols which may clash with symbols used in your code. At present, the only way to solve this problem is to rename the symbols in the user code.

    To build a Runtime System you need:

    @
    The Runtime Kernel contains the SICStus Prolog engine together with control and interface functions.

    @
    Header file for the interface functions.

    @
    Your C code, object files and Prolog code.

    @
    A file containing glue code for any C functions to be loaded by load_foreign_files/2, or `$SP_PATH/bin/flinkage.c' if no functions will be called from Prolog (see section Calling C from Prolog).

    @
    You must compile your Prolog program with the predicate fcompile/1 in your Development System. This will produce the Prolog object files that you load into the Runtime System at runtime.

    To compile and link a Runtime System, perform:

    % sicstus
    SICStus 3 #0: Wed Mar 15 12:29:29 MET 1995
    | ?- fcompile(PL_MODULES).
    | ?- ^D
    
    % cc -o NAME C_MODULES GLUE_CODE \
      $SP_PATH/bin/runtime.o -lm LDFLAGS
    

    where

    @
    is the name of the resulting executable

    @
    is the application Prolog code

    @
    is the application C or object code

    @
    is the glue code

    @
    are any C libraries required by C_MODULES, and any machine-dependent flags that may be required for correct linkage (see `PortNote' in the SICStus Prolog source directory).

    To execute a Runtime System on a target machine you need:

    @
    The Runtime Library is the built-in predicates written in Prolog. This code is loaded into the program at runtime by the function SP_initialize().

    @
    This is your application program linked with the Runtime Kernel as above.

    @
    This is your Prolog program compiled with fcompile/1. This code is loaded at runtime from C with the function SP_load(). If it consists of several `.ql' files, they may be concatenated into a single file.

    load_foreign_files/2 can be used in Runtime Systems with static linking (see section Calling C from Prolog). Just generate the necessary glue code using prepare_foreign_files/1 in a Development System, use it as GLUE_CODE above, and include the C functions to be called by Prolog among the C_MODULES.

    The library module flinkage may be useful in generating glue code from the foreign functions declarations in a set of Prolog files. See section Glue Code Generator.

    Examples

    Train Example (connections)

    This is an example of how to create a Runtime System. The Prolog program `train.pl' will display a route from one train station to another. The C program `train.c' calls the Prolog code and writes out all the routes found between two stations:

    /* train.pl */
    
    connected(From, To, Path) :-
            connected(From, To, Path, [From]).
    
    connected(To, To, [To], _).
    connected(From, To, [From|Path], Visited) :-
            (   connection(From, Via)
            ;   connection(Via, From)
            ),
            not_visited(Visited, Via),
            connected(Via, To, Path, [Via|Visited]).
    
    connection('Stockholm', 'Katrineholm').
    connection('Stockholm', 'Vasteras').
    connection('Katrineholm', 'Hallsberg').
    connection('Katrineholm', 'Linkoping').
    connection('Hallsberg', 'Kumla').
    connection('Hallsberg', 'Goteborg').
    connection('Orebro', 'Vasteras').
    connection('Orebro', 'Kumla').
    
    not_visited([], _).
    not_visited([X|Visited], Y) :- X \== Y, not_visited(Visited, Y).
    

    /* train.c */
    
    #include <sicstus/sicstus.h>
    
    main()
    {
      SP_pred_ref pred;
      SP_qid goal;
      SP_term_ref from, to, path, tail;
    
      SP_initialize(0, NULL, NULL); /* looks up $SP_PATH */
      SP_load("train.ql");
      pred = SP_predicate("connected",3,"");
      from = SP_new_term_ref();
      SP_put_string(from, "Stockholm");
      to = SP_new_term_ref();
      SP_put_string(to, "Orebro");
      path = SP_new_term_ref();
      SP_put_variable(path);
    
      goal = SP_open_query(pred,from,to,path);
      while (SP_next_solution(goal))
        write_path(path);
      SP_close_query(goal);
      exit(0);
    }
    
    
    write_path(path)
         SP_term_ref path;
    {
      SP_term_ref via = SP_new_term_ref();
      SP_term_ref tail = SP_new_term_ref();
      
      SP_put_term(tail,path);
      while (SP_get_list(tail,via,tail))
      {
        char *text;
    
        SP_get_string(via, &text);
        printf("Path: %s\n",text);
      }
      printf("\n");  
    }
    

    Finally, link the program with the Prolog engine and C interface routines and run it:

    % sicstus
    SICStus 3 #0: Wed Mar 15 12:29:29 MET 1995
    | ?- fcompile(train).
    | ?- ^D
    
    % cc -o train train.c $SP_PATH/bin/flinkage.c \
      $SP_PATH/bin/runtime.o -lm LDFLAGS
    % ./train
    
    Path: Stockholm
    Path: Katrineholm
    Path: Hallsberg
    Path: Kumla
    Path: Orebro
    
    Path: Stockholm
    Path: Vasteras
    Path: Orebro
    

    IO on Lists of Character Codes

    This example is taken from the SICStus Prolog library (simplified, but operational). A stream for writing is opened where the written characters are placed in a buffer. When the stream is closed a list of character codes is made from the contents of the buffer. The example illustrates the use of user definable streams.

    The open_buf_stream() function opens a stream where the characters are put in a buffer. The stream is closed by stream_to_chars() which returns the list constructed on the heap.

    The Prolog code (simplified):

    foreign(open_buf_stream, '$open_buf_stream'(-address('SP_stream'))).
    foreign(stream_to_chars, '$stream_to_chars'(+address('SP_stream'),
                                                -term)).
    
    foreign_file('example.o', [open_buf_stream,stream_to_chars]).
    
    :- load_foreign_files('example.o', []).
    
    %% with_output_to_chars(+Goal, -Chars)
    %% runs Goal with current_output set to a list of characters
    
    with_output_to_chars(Goal, Chars) :-
            '$open_buf_stream'(StreamCode),
            stream_code(Stream, StreamCode),
            current_output(CurrOut),
            set_output(Stream),
            call_and_reset(Goal, Stream, CurrOut, StreamCode, Chars).
    
    call_and_reset(Goal, Stream, CurrOut, StreamCode, Chars) :-
            call(Goal), !,
            put(0),
            '$stream_to_chars'(StreamCode, Chars),
            reset_stream(Stream, CurrOut).
    call_and_reset(_, Stream, CurrOut, _, _) :-
            reset_stream(Stream, CurrOut).
    
    reset_stream(Stream, CurrOut) :-
            set_output(CurrOut),
            close(Stream).
    

    The C code:

    #include <sicstus/sicstus.h>
    
    struct open_chars {
      char *chars;       /* character buffer */
      int index;         /* current insertion point */
      int size;
    };
    
    #define INIT_BUFSIZE 512
    
    static int lputc(c, buf)
         int c;
         struct open_chars *buf;
    {
      if (buf->index == buf->size)  /* grow buffer if necessary */
        {
          buf->size *= 2;
          buf->chars = (char *)realloc(buf->chars, buf->size);
        }
      return (buf->chars[buf->index++] = c);   
    }
    
    static int lwclose(buf)
         struct open_chars *buf;
    {
      free(buf->chars);
      free(buf);
      return 0;
    }
    
    void open_buf_stream(streamp)
         SP_stream **streamp;
    {
      struct open_chars *buf;
    
      /* Allocate buffer, create stream & return stream code */
    
      buf = (struct open_chars *)malloc(sizeof(struct open_chars));
      SP_make_stream(buf, NULL, lputc, NULL, NULL, NULL, lwclose, 
                     streamp);
      
      buf->chars = (char *)malloc(INIT_BUFSIZE);
      buf->size = INIT_BUFSIZE;
      buf->index = 0;
    }
    
    void stream_to_chars(streamp, head)
         SP_stream *streamp;
         SP_term_ref head;
    {
      SP_term_ref tail = SP_new_term_ref();
      struct open_chars *buf = (struct open_chars *)streamp->user_handle;
    
      /* Make a list of character codes out of the buffer */
    
      SP_put_string(tail, "[]");  
      SP_put_list_chars(head, tail, buf->chars);
    }
    

    Exceptions from C

    Consider, for example, a function which returns the square root of its argument after checking that the argument is valid. If the argument is invalid, the function should raise an exception instead.

    /* math.c */
    
    #include <math.h>
    #include <stdio.h>
    #include <sicstus/sicstus.h>
    
    double sqrt_check(d)
         double d;
    {
      if (d < 0.0)
        {    /* build a domain_error/4 exception term */
          SP_term_ref culprit=SP_new_term_ref();
          SP_term_ref argno=SP_new_term_ref();
          SP_term_ref expdomain=SP_new_term_ref();
          SP_term_ref t1=SP_new_term_ref();
    
          SP_put_float(culprit, d);
          SP_put_integer(argno, 1);
          SP_put_string(expdomain, ">=0.0");
          SP_cons_functor(t1, SP_atom_from_string("sqrt"), 1, culprit);
          SP_cons_functor(t1, SP_atom_from_string("domain_error"), 4, 
                          t1, argno, expdomain, culprit);
          SP_raise_exception(t1);    /* raise the exception */
          return 0.0;
        }  
      return sqrt(d);
    }
    

    The Prolog interface to this function is defined in a file `math.pl'. The function uses the sqrt() library function, and so the math library `-lm' has to be included:

    /* math.pl */
    
    foreign_file('math.o', [sqrt_check]).
    
    foreign(sqrt_check, c, sqrt(+float, [-float])).
    

    A simple session using this function could be:

    % sicstus
    SICStus 3 #0: Wed Mar 15 12:29:29 MET 1995
    | ?- [math], load_foreign_files(['math.o'], ['-lm']).
    {consulting /home/sics/al/math.pl...}
    {math consulted, 160 msec 597 bytes}
    
    yes
    | ?- sqrt(5.0, X).
    
    X = 2.23606797749979 ? 
    
    yes
    | ?- sqrt(a,X).
    {TYPE ERROR: sqrt(a,_28) - arg 1: expected number, found a}
    | ?- sqrt(-5,X).
    {DOMAIN ERROR: sqrt(-5.0) - arg 1: expected '>=0.0', found -5.0}
    

    The above example used the foreign language interface with dynamic linking. To statically link `math.o' with the Prolog emulator, the following steps would have been taken:

    % sicstus
    SICStus 3 #0: Wed Mar 15 12:29:29 MET 1995
    | ?- [math], prepare_foreign_files(['math.o']).
    
    {consulting /home/sics/al/math.pl...}
    {math consulted, 20 msec 597 bytes}
    {flinkage.c generated, 20 msec}
    
    yes
    | ?- ^D
    % cc -o mathsp $SP_PATH/bin/sp.o flinkage.c math.c -lm LDFLAGS
    % ./mathsp -f -b $SP_PATH/bin/sp.ql
    booting SICStus...please wait
    SICStus 3 #0: Wed Mar 15 12:29:29 MET 1995
    
    | ?- [math], load_foreign_files(['math.o'], ['-lm']).
    
    {consulting /home/sics/al/math.pl...}
    {math consulted, 20 msec 597 bytes}
    
    yes
    | ?- sqrt(5.0, X).
    
    X = 2.23606797749979 ? 
    
    yes
    

    Stream Example

    This is a small example how to initialize a bidirectional socket stream (error handling omitted):

    
    typedef struct {
      int fd;                       /* socket number */
      FILE *r_stream;               /* For reading */
      FILE *w_stream;               /* For writing */
    } SocketData;
    
    int socket_sgetc(SocketData *socket)
    {
      return fgetc(socket->r_stream);
    }
    
    int socket_sputc(char c, SocketData *socket)
    {
      return fputc(c, socket->w_stream);
    }
    
    int socket_sflush(SocketData *socket)
    {
      return fflush(socket->w_stream);
    }
    
    int socket_seof(SocketData *socket)
    {
      return feof(socket->r_stream);
    }
    
    void socket_sclrerr(SocketData *socket)
    {
      clearerr(socket->r_stream);
      clearerr(socket->w_stream);
    }
    
    int socket_sclose(SocketData *socket) 
    {
      fclose(socket->r_stream);
      fclose(socket->w_stream);
      close(socket->fd);
      free(socket);
      return 0;
    }
    
    SP_stream *new_socket_stream(int fd)
    {
      SP_stream *stream;
      SocketData *socket;
      
    /* Allocate and initialize data local to socket */
    
      socket = (SocketData *)malloc(sizeof(SocketData));
      socket->fd = fd;
      socket->r_stream = fdopen(fd,"r");
      socket->w_stream = fdopen(fd,"w");
    
    /* Allocate and initialize Prolog stream */
    
      SP_make_stream(
                     socket,
                     socket_sgetc,
                     socket_sputc,
                     socket_sflush,
                     socket_seof,
                     socket_sclrerr,
                     socket_sclose,
                     &stream);
    
    /* Allocate and copy string */
    
      stream->filename = "socket";
      stream->fd = fd;
      
      return stream;
    }
    

    Go to the previous, next section.