Go to the first, previous, next, last section, table of contents.


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 macros 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_resource/1, 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, for which access functions are provided.

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

Notes

ANSI Conformance
Throughout this chapter, void * in the function definitions may be changed to char * on non ANSI conforming C compilers.
The SP_PATH variable
It is normally not necessary to set this environment variable, but its value will be used at runtime if no explicit boot path is given when initializing a Runtime or Development System. In this chapter, the environment variable SP_PATH is used as a shorthand for the SICStus Prolog installation directory, whose default UNIX location is `/usr/local/lib/sicstus37'). See section Environment Variables.
Definitions and declarations
Type definitions and function declarations for the interface are found in the header file `<sicstus/sicstus.h>'.
Error Codes
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 macro SP_errno will return 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 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 partly modeled after Quintus Prolog.

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

Functions, or their equivalent, in any other language having C compatible calling conventions may also be interfaced using this interface. When referring to C functions in the following, we also include such other language functions. Note however that a C compiler is needed since a small amount of glue code (in C) must be generated for interfacing purposes. Note also that on some platforms different calling conventions may exist for C functions. Check the Release Notes to see which one is used by this interface.

Foreign Resource and Conversion Declarations

A foreign resource is a set of C functions, defined in one or more files, installed as an atomic operation. The name of a foreign resource, the resource name, is an atom, which should uniquely identify the resource. Thus, two foreign resources with the same name cannot be installed at the same time.

For each foreign resource, a foreign_resource/2 fact is used to declare the interfaced functions. For each of these functions, a foreign/(2,3) fact is used to specify conversions between predicate arguments and C-types. These conversion declarations are used for creating the necessary interface between Prolog and C.

The functions making up the foreign resource, the automatically generated interface code, and any libraries, are compiled and linked to form a linked foreign resource, which can be statically or dynamically linked to a Development or Runtime System. In all cases, the declared predicates are installed by the built-in predicate load_foreign_resource/1. The resource name of a linked foreign resource is derived from its file specification by deleting any leading path and the suffix.

Static and Dynamic linking

The interface can be used in two ways.

With static linking, the linked foreign resource is statically linked to a Development or Runtime System. load_foreign_resource/1 determines if a resource has been statically linked, in which case only the binding of predicate names to functions needs to be done. Otherwise, dynamic linking is attempted.

With dynamic linking, the linked foreign resource can be linked and unlinked to a Development or Runtime System, as needed.

A linked foreign resource may be implemented, depending on the hardware and operating system platform, as e.g. a shared object or a dynamic link library.

Conversion Declarations

Conversion declaration predicates:

foreign_resource(+ResourceName,+Functions)
A hook predicate. Specifies that a set of foreign functions, to be called from Prolog, are to be found in the resource named by ResourceName. Functions is a list of functions exported by the resource. Only functions that are to be called from Prolog and optionally one init function and one deinit function should be listed. The init and deinit functions are specified as init(Function) and deinit(Function) respectively (see section Init and Deinit Functions). This predicate should be defined entirely in terms of facts (unit clauses). For example:
foreign_resource('terminal', [scroll,pos_cursor,ask]).
specifies that functions scroll(), pos_cursor() and ask() are to be found in the resource `terminal'.
foreign(+CFunctionName, +Predicate)
foreign(+CFunctionName, +Language, +Predicate)
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. This predicate should be defined entirely in terms of facts (unit clauses). For example:
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);. 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 available conversions are listed in the next subsection.

Conversions between Prolog Arguments and C Types

The following table lists the possible values for the arguments in the predicate specification of foreign/(2,3). The value declares which conversion between corresponding Prolog argument and C type will take place.

Prolog: +integer
C: long
The argument should be a number. It is converted to a C long and passed to the C function.
Prolog: +float
C: double
The argument should be a number. It is converted to a C double and passed to the C function.
Prolog: +atom
C: unsigned long
The argument should be an atom. Its canonical representation is passed to the C function.
Prolog: +chars
C: char *
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.
Prolog: +string
C: char *
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.
Prolog: +string(N)
C: char *
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.
Prolog: +address
C: void *
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.
Prolog: +address(TypeName)
C: TypeName *
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.
Prolog: +term
C: SP_term_ref
The argument could be any term. The value passed will be the internal representation of the term.
Prolog: -integer
C: long *
The C function is passed a reference to an uninitialized long. The value returned will be converted to a Prolog integer.
Prolog: -float
C: double *
The C function is passed a reference to an uninitialized double. The value returned will be converted to a Prolog float.
Prolog: -atom
C: unsigned long *
The C function is passed a reference to an uninitialized unsigned long. The value returned should be the canonical representation of a Prolog atom.
Prolog: -chars
C: char **
The C function is passed the address of an uninitialized char *. The returned string will be converted to a Prolog list of character codes.
Prolog: -string
C: char **
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.
Prolog: -string(N)
C: char *
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.
Prolog: -address
C: void **
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.
Prolog: -address(TypeName)
C: TypeName **
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.
Prolog: -term
C: SP_term_ref
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.
Prolog: [-integer]
C: long F()
The C function should return a long. The value returned will be converted to a Prolog integer.
Prolog: [-float]
C: double F()
The C function should return a double. The value returned will be converted to a Prolog float.
Prolog: [-atom]
C: unsigned long F()
The C function should return an unsigned long. The value returned must be the canonical representation of a Prolog atom.
Prolog: [-chars]
C: char *F()
The C function should return a char *. The returned string will be converted to a Prolog list of character codes.
Prolog: [-string]
C: char *F()
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.
Prolog: [-string(N)]
C: char *F()
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.
Prolog: [-address]
C: void *F()
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.
Prolog: [-address(TypeName)]
C: TypeName *F()
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.
Prolog: [-term]
C: SP_term_ref F()
The C function should return an SP_term_ref. Prolog will try to unify its value with the actual argument.

Interface Predicates

In the following descriptions, the Resource argument is an unsuffixed file specification of a linked foreign resource.

link_foreign_resource(+Resource,+SourceFile,+Option,+CFiles,+ObjectFiles,+Libraries)
Builds a linked foreign resource. Option can be either dynamic or static as described in the alternative form shown below. This predicate is not available in Runtime Systems. The work of this predicate is done by a command located in `$SP_PATH/bin'. This command is configurable as described in the Release Notes. It can also be invoked directly from a shell as an alternative to running it from Prolog:
splfr Resource SourceFile [+dynamic | +static] [+c CFiles] [+o ObjectFiles] [+l Libraries] [+copt COptions]
Reads terms from SourceFile, applying term expansion and extracting any foreign_resource/2 fact with first argument matching the name of Resource and all foreign/(2,3) facts. Compiles CFiles and the interface code derived from these facts and links the resulting object files with ObjectFiles and Libraries to form the linked foreign resource Resource. The option +dynamic (+static) specifies that the linked foreign resource is suitable for dynamic (static) linking. The default is dynamic. The option +copt can be used to pass options to the C-compiler.
load_foreign_resource(:Resource)
Unless a foreign resource with the same name as Resource has been statically linked, the linked foreign resource specified by Resource is linked into the Prolog load image. In both cases, the predicates defined by Resource are installed, and any init function is called. Dynamic linking is not possible if the foreign resource was linked using the +static option. If a resource with the same name has been previously loaded, it will be unloaded as if unload_foreign_resource(Resource) was called, before Resource is loaded. In Muse, the system is required to be adjusted to one worker first, and the predicates defined become cavalier (see section Programming Considerations).
unload_foreign_resource(:Resource)
Any deinit function associated with Resource is called, and the predicates defined by Resource are uninstalled. If Resource has been dynamically linked, it is unlinked from the Prolog load image. In Muse, the system is required to be adjusted to one worker first, and the predicates defined become cavalier

The following predicates are provided for backwards compatibility and should be avoided in new code:

foreign_file(+File,+Functions)
A hook predicate. Specifies that a set of foreign functions, to be called from Prolog, are to be found in File. This predicate is only called from load_foreign_files/2.
load_foreign_files(:ObjectFiles,+Libraries)
Hookable, a resource name is derived from the first file name in ObjectFiles by stripping off the suffix. If this resource has been statically linked, the predicates defined by it are installed; otherwise, a linked foreign resource containing the declared functions is created and loaded. Not available in Runtime Systems.

Init and Deinit Functions

An init function and/or a deinit function can be declared by foreign_resource/2. If this is the case, these functions should have the prototype:

void FunctionName (int when)

The init function is called by load_foreign_resource/1 after the resource has been loaded and the interfaced predicates have been installed.

The deinit function is called by unload_foreign_resource/1 before the interfaced predicates have been uninstalled and the resource has been unloaded.

The init and deinit functions may use the C-interface to call Prolog etc.

Foreign resources are unloaded before saving states, and reloaded afterwards or when the saved state is restored; see section Saving and Restoring Program States. Foreign resources are also unloaded when exiting Prolog execution. The parameter when reflects the context of the (un)load_foreign_resource/1 and is set as follows for init functions:

SP_WHEN_EXPLICIT
Explicit call to load_foreign_resource/1.
SP_WHEN_RESTORE
Resource is reloaded after save or restore.

For deinit functions:

SP_WHEN_EXPLICIT
Explicit call to unload_foreign_resource/1.
SP_WHEN_SAVE
Resource is unloaded before save.
SP_WHEN_EXIT
Resource is unloaded before exiting Prolog.

Creating the Linked Foreign Resource

Suppose we have a Prolog source file ex.pl containing:

foreign(f1, p1(+integer,[-integer])).
foreign(f2, p2(+integer,[-integer])).
foreign_resource(ex, [f1,f2]).
:- load_foreign_resource(ex).

and a C source file ex.c with definitions of the functions f1 and f2, both returning long and having a long as only parameter. The conversion declarations in `ex.pl' state that these functions form the foreign resource ex.

To create the linked foreign resource, simply type (to Prolog):

| ?- link_foreign_resource(ex,'ex.pl',dynamic,['ex.c'],[],[]).

or alternatively (to the Shell):

% splfr ex ex.pl +c ex.c

The linked foreign resource `ex.so' (file suffix `.so' is system dependent) has been created. It will be dynamically linked by the directive :- load_foreign_resource(ex). when the file `ex.pl' is loaded. Linked foreign resources can also be created manually (see section Glue Code Generator).

Dynamic linking of foreign resources can also be used by Runtime Systems. On some platforms, however, the executable must not be stripped for dynamic linking to work, i.e. its symbol table must remain.

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 memory management 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)

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

int SP_atom_length(unsigned long a)

Same as strlen(SP_string_from_atom(a)) but runs in O(1) time.

Prolog atoms, and the space occupied by their print names, are subject to garbage collection when the number of atoms has reached a certain threshold, under the control of the agc_margin Prolog flag (see section Information about the State of the Program), or when the atom garbage collector is called explicitly. The atom garbage collector will find all references to atoms from the Prolog specific memory areas, including SP_term_refs and arguments passed from Prolog to foreign language functions. However, atoms created by SP_atom_from_string and merely stored in a local variable are endangered by garbage collection. The following functions make it possible to protect an atom while it is in use. The operations are implemented using reference counters to cater for multiple, independent use of the same atom in different foreign resources:

int SP_register_atom(unsigned long a)

Registers the atom a with the Prolog memory manager by incrementing its reference counter. Returns a nonzero value if the operation succeeds.

int SP_unregister_atom(unsigned long a)

Unregisters the atom a with the Prolog memory manager by decrementing its reference counter. Returns a nonzero value if the operation succeeds.

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.

int SP_put_variable(SP_term_ref t)
Assigns to t a new Prolog variable.
int SP_put_integer(SP_term_ref t, long l)
Assigns to t a Prolog integer from a C long integer.
int SP_put_float(SP_term_ref t, double d)
Assigns to t a Prolog float from a C double.
int SP_put_atom(SP_term_ref t, unsigned long a)
Assigns to t a Prolog atom from a, which must be the canonical representation of a Prolog atom. (see section Calling C from Prolog).
int SP_put_string(SP_term_ref t, char *name)
Assigns to t a Prolog atom from a C string.
int SP_put_address(SP_term_ref t, void *pointer)
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). Furthermore, the address must be aligned on a four bytes boundary. NULL is converted to the integer 0.
int SP_put_list_chars(SP_term_ref t, SP_term_ref tail, char *s)
Assigns to t a Prolog list of the character codes in s. The list is terminated by the value of tail.
int SP_put_list_n_chars(SP_term_ref t, SP_term_ref tail, long n, char *s)
Assigns to t a Prolog list of the first n character codes or zeroes in s. The list is terminated by the value of tail.
int SP_put_number_chars(SP_term_ref t, char *s)
Assigns to t a Prolog number by parsing the string in s.
int SP_put_functor(SP_term_ref t, unsigned long name, int arity)
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 to an atom and an integer, respectively.
int SP_put_list(SP_term_ref t)
Assigns to t a Prolog list whose head and tail are both unbound variables.
int SP_cons_functor(SP_term_ref t, unsigned long name, int arity, SP_term_ref arg, ...)
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.
int SP_cons_list(SP_term_ref t, SP_term_ref head, SP_term_ref tail)
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.

int SP_get_integer(SP_term_ref t, long *l)
Assigns to *l the C long corresponding to a Prolog number. The value must fit in *l for the operation to succeed.
int SP_get_float(SP_term_ref t, double *d)
Assigns to *d the C double corresponding to a Prolog number.
int SP_get_atom(SP_term_ref t, unsigned long *a)
Assigns to *a the canonical representation of a Prolog atom.
int SP_get_string(SP_term_ref t, char **name)
Assigns to *name a pointer to the string that is the name of a Prolog atom. This string must not be modified.
int SP_get_address(SP_term_ref t, void **pointer)
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()).
int SP_get_list_chars(SP_term_ref t, char **s)
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.
int SP_get_list_n_chars(SP_term_ref t, SP_term_ref tail, long n, long *w, char *s)
Copies into s at most n character codes or zeroes from the list t. The number of character codes actually written is assigned to *w. tail is set to the remainder of the list. The array s must have room for at least n characters.
int SP_get_number_chars(SP_term_ref t, char **s)
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.
int SP_get_functor(SP_term_ref t, unsigned long *name, int *arity)
Assigns to *name and *arity the canonical representation and arity of 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 to a compound term or an atom and the second and third arguments unbound.
int SP_get_list(SP_term_ref t, SP_term_ref head, SP_term_ref tail)
Assigns to head and tail the head and tail of a Prolog list.
int SP_get_arg(int i, SP_term_ref t, SP_term_ref arg)
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.

int SP_term_type(SP_term_ref t)
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.
int SP_is_variable(SP_term_ref t)
Returns nonzero if the term is a Prolog variable, zero otherwise.
int SP_is_integer(SP_term_ref t)
Returns nonzero if the term is a Prolog integer, zero otherwise.
int SP_is_float(SP_term_ref t)
Returns nonzero if the term is a Prolog float, zero otherwise.
int SP_is_atom(SP_term_ref t)
Returns nonzero if the term is a Prolog atom, zero otherwise.
int SP_is_compound(SP_term_ref t)
Returns nonzero if the term is a Prolog compound term, zero otherwise.
int SP_is_list(SP_term_ref t)
Returns nonzero if the term is a Prolog list, zero otherwise.
int SP_is_atomic(SP_term_ref t)
Returns nonzero if the term is an atomic Prolog term, zero otherwise.
int SP_is_number(SP_term_ref t)
Returns nonzero if the term is a Prolog number, zero otherwise.

Unifying and Comparing Terms

int SP_unify(SP_term_ref x, SP_term_ref y)
Unifies two terms, returning zero on failure and nonzero on success.
int SP_compare(SP_term_ref x, SP_term_ref y)
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:

void *SP_malloc(unsigned int size)
Returns a properly aligned pointer to a block of at least size bytes. In Muse, the blocks are allocated in shared memory.
void *SP_realloc(void *ptr, unsigned int size)
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.
void SP_free(void *ptr)
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 signals or similar asynchronous events. 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) or backtrack (SP_FAILURE or SP_ERROR). An exception raised by func will be processed in the interrupted Prolog execution. In case of fail or exception the event queue is flushed:

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

A 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 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();
  int rc;

  SP_put_integer(x, (int)signal_no);
  rc = SP_query(event_pred, x);
  if (rc == SP_ERROR && SP_exception_term(x))
    SP_raise_exception(x);        /* Propagate any raised exception */
  return rc;
}

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:

stream_code(+Stream,?StreamCode)
stream_code(?Stream,+StreamCode)
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 any relation to the 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 I/O functions are used:

int SP_getc(void)
int SP_fgetc(SP_stream *s)
void SP_putc(int c)
void SP_fputc(int c, SP_stream *s)
void SP_puts(char *string)
void SP_fputs(char *string, SP_stream *s)
int SP_printf(char *format, ...)
int SP_fprintf(SP_stream *s, char *format, ...)
int SP_fflush(SP_stream *s)
int SP_fclose(SP_stream *s)

There are three predefined streams accessible from C:

SP_stdin
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) .
SP_stdout
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).
SP_stderr
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).
SP_curin
Current input. It is initially set equal to SP_stdin. It can be changed with the predicates see/1 and set_input/1.
SP_curout
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 I/O Functions

For each new stream the appropriate low level I/O 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 I/O functions may invoke Prolog code and use the support functions described in the other sections of this chapter.

int my_fgetc(void *handle)
Should return the character read or -1 on end of file.
int my_fputc(char c, int handle)
Should write the character c and return the character written.
int my_flush(void *handle)
Flush the stream. Should return 0 on success, EOF on error.
int my_eof(void *handle)
Should return 1 on end of file, else 0.
void my_clrerr(void *handle)
Clear the error level.
int my_close(void *handle)
Close the stream. Should return zero.

Installing a New Stream

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.

SP_stream *backward;
SP_stream *forward;
Used for linking streams together. The insertion is done by SP_make_stream() and the deletion is done from the Prolog predicate close/1.
char *filename;
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.
unsigned long mode;
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:
void SP_set_tty(SP_stream *s)
int fd;
The I/O 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 I/O 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);
}

Hookable Standard Streams

As of SICStus release 3.7, the standard I/O streams (input, output, and error) are hookable, i.e. the streams can be redefined by the user.

SP_UserStreamHook *SP_set_user_stream_hook(SP_UserStreamHook *hook)
Sets the user-stream hook to hook.
SP_UserStreamPostHook *SP_set_user_stream_post_hook(SP_UserStreamPostHook *hook)
Sets the user-stream post-hook to hook.

Writing User-stream Hooks

The user-stream hook is, if defined, called during SP_initialize(). SP_set_user_stream_hook() and SP_set_user_stream_post_hook() must therefore be called before SP_initialize() is called. It has the following prototype:

SP_stream *user_stream_hook(int which)

If the hook is not defined, SICStus will attempt to open the standard TTY/console versions of these streams. If they are unavailable (such as for windowed applications under Windows), the result is undefined.

It is called three times, one for each stream. The which argument indicates which stream it is called for. The value of which is one of:

SP_STREAMHOOK_STDIN
Create stream for standard input.
SP_STREAMHOOK_STDOUT
Create stream for standard output.
SP_STREAMHOOK_STDERR
Create stream for standard error.

The hook should return a standard SICStus I/O stream, as described in section Defining a New Stream.

Writing User-stream Post-hooks

The user-stream post-hook is, if defined, called after all the streams have been defined, once for each of the three standard streams. It has a slightly different prototype:

void user_stream_post_hook(int which, SP_stream *str)

where str is a pointer to the corresponding SP_stream structure. There are no requirements as to what this hook must do; the default behavior is to do nothing at all.

The post-hook is intended to be used to do things which may require that all streams have been created.

User-stream Hook Example

This section contains an example of how to create and install a set of user-streams.

The hook is set by calling SP_set_user_stream_hook() in the main program like this:

SP_set_user_stream_hook((SP_UserStreamHook *)user_strhook);

Remember: SP_set_user_stream_hook() and SP_set_user_stream_post_hook() must be called before SP_initialize().

The hook user_strhook() is defined like this:

SP_stream *user_strhook(int std_fd)
{
  SP_stream *s;

  SP_make_stream(NULL, my_getc, my_putc, my_flush, my_eof, my_clrerr, NULL, &s);

  return s;
}

See section Installing a New Stream for a description on the parameters to SP_make_stream().

Muse Support Functions

Dynamic loading of foreign language functions 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:

int muse_max_workers(void)
Returns the maximum number of workers. Also available as a read-only Muse flag.
int muse_num_workers(void)
Returns the actual number of allocated workers. Also available as a writable Muse flag.
int muse_worker_id(void)
Returns the worker identity as a non-negative integer. Also available as a read-only Muse flag.
void muse_init_lock(int *lock)
void muse_lock(int *lock)
void muse_un_lock(int *lock)
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 section Muse FLI Example) to allocate an array of size muse_max_workers() that is indexed by the worker id (muse_worker_id()).

Hooks

The user may define functions to be called at certain occasions by the Prolog system. This is accomplished by passing the functions as arguments to the following set-hook-functions. The functions can be removed by passing a NULL.

typedef int (SP_ReadHookProc) (int fd)
SP_ReadHookProc SP_set_read_hook (SP_ReadHookProc *)
The installed function is 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.
typedef void (SP_VoidFun) (void)
SP_VoidFun * SP_set_reinit_hook (SP_VoidFun *)
The installed function is 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.)
typedef void (SP_VoidFun) (void);
SP_VoidFun * SP_set_interrupt_hook (SP_VoidFun *)
The installed function is called on occasions like expansion of stacks, garbage collection and printouts, in order to yield control to special consoles etc. for interrupt checking. 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 calling any other interface function. 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, equivalent to `$SP_PATH/bin'. 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/sprt.sav' which contains the Runtime Library.

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

Optionally, you may also call SP_force_interactive() before calling SP_initialize(). 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)

Optionally, you may also call SP_set_memalloc_hooks() before calling SP_initialize(). This will define the bottom layer of Prolog's memory manager, in case your application has special requirements.

typedef void *(SP_AllocHook)(unsigned int size, 
                             unsigned int align, 
                             unsigned int *actual_sizep);
typedef void *(SP_ReAllocHook)(void *ptr, 
                               unsigned int oldsize, 
                               unsigned int newsize, 
                               unsigned int align, 
                               unsigned int *actual_sizep);
typedef int (SP_FreeHook)(void *ptr, 
                          unsigned int size);

void SP_set_memalloc_hooks(int usage,
                           SP_AllocHook *init_alloc_hook,
                           SP_AllocHook *alloc_hook,
                           SP_ReAllocHook *realloc_hook,
                           SP_FreeHook *free_hook)

The effect of SP_set_memalloc_hooks is controlled by the value of usage, which should be one of:

MM_USE_MALLOC
The bottom layer will be based on malloc()/free(). The other arguments are ignored. Same as the `-m' option in Development Systems. (see section Getting Started).
MM_USE_SBRK
The bottom layer will be based on sbrk(). The default for UNIX; not available for Windows. The other arguments are ignored.
MM_USE_SPARSE
The bottom layer will be based on VirtualAlloc()/VirtualFree(). The default for Windows; not available for UNIX. The other arguments are ignored.
MM_USE_OTHER
The bottom layer will be based on the other arguments. Their meaning is explained below.

In the latter case, the other arguments should be functions as specified below:

alloc_hook
must allocate and return a pointer to a piece of memory that has at least size bytes aligned at align in it. align is guaranteed to be a power of 2. The actual size of the piece of memory should be returned in *actual_sizep. Should return NULL if it cannot allocate any more memory.
init_alloc_hook
is a special case of alloc_hook. It will be called initially whereas alloc_hook will be called subsequently. It can do whatever initialization that this layer of memory management wants to do.
realloc_hook
is called with a piece of memory to be resized and possibly moved. ptr is the pointer, oldsize its current size. The function must allocate and return a pointer to a piece of memory that has at least newsize bytes aligned at align in it, and that has the same contents as the old block up to the lesser of oldsize and newsize. align is guaranteed to be a power of 2. The actual size of the piece of memory should be returned in *actual_sizep. Should return NULL if it cannot allocate any more memory, or if it cannot reclaim the old block in a meaningful way. In that case, Prolog will use the other functions.
free_hook
is called with a pointer to the piece of memory to be freed and its size. Should return non-zero iff the function was able to free this piece of memory. Otherwise, Prolog will keep using the memory as if it were not freed.

The default bottom layers look at the environment variables PROLOGINITSIZE, PROLOGINCSIZE, PROLOGKEEPSIZE and PROLOGMAXSIZE. They are useful to customize the default memory manager. If users redefine the bottom layer, they can choose to ignore these environment variables. See section Environment Variables.

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 *filename)

Alternatively, you can restore a saved state with the call SP_restore(), which is the C equivalent of the Prolog predicate restore/1:

int SP_restore(char *filename)

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

Prolog error handling is mostly done by raising and catching exceptions. However, some faults are of a nature such that when they occur, the internal program state may be corrupted, and it is not safe to merely raise an exception. Memory allocation failures are examples of faults. In Runtime Systems, the following C macro provides an environment for handling faults:

int SP_on_fault(Stmt, Message, Cleanup)

which should occur in the scope of a char *Message declaration. Stmt is run, and if a fault occurs, Stmt is aborted, Message gets assigned a value explaining the fault, the Prolog internal state is cleaned, all queries and SP_term_refs become invalid, and Cleanup run. If Stmt terminates normally, Message is left unchanged. For example, a "fault-proof" Runtime System could have the structure:

int main(int argc, char **argv)
{
  char *message;

  SP_initialize(argc, argv, "/usr/local/lib/sicstus37/bin");
loop:
  SP_on_fault(main_loop(), message, 
              {printf("ERROR: %s\n",message); goto loop;});
  exit(0);
}

main_loop()
{...}

Faults that occur outside the scope of SP_on_fault() cause the Runtime System to halt with an error message.

The following function can be used to raise a fault. For example, it can be used in a signal handler for SIGSEGV to prevent the program from dumping core in the event of a segmentation violation (Runtime Systems have no predefined signal handling):

void SP_raise_fault(char *message)

Creating the Executable

This describes how to create a Runtime System with a set of statically linked foreign resources. For the SICStus library modules containing C-code, such linked foreign resources already exist in `$SP_PATH/library'. For user foreign code, you must first create linked foreign resources for them.

A utility command is provided for the generation of such systems. This command, located in `$SP_PATH/bin', is configurable as described in the Release Notes.

spmkrs [--help] [-S] Executable [+c SourceFiles] [+o ObjectFiles] [+l Libraries] [+r Resources]
Generates a Runtime System with any foreign resources mentioned in Resources statically linked. The resources must be created with the +static option. Note however that a statically linked dynamic resource is not, in general, statically linked from the operating system's point of view. It must therefore be accessible at runtime as well as at build-time. Compiles SourceFiles and links the resulting object files with ObjectFiles, Libraries, and the foreign resources to produce Executable. The main program of the Runtime System must be provided by SourceFiles or ObjectFiles. The option -S causes the SICStus Runtime Kernel to be statically linked into the executable, thus eliminating the need to load these at runtime. This produces a significantly larger executable. Observe that this option has no effect on the resources and libraries spefified after by +r and +l. Note: This option is not available on Win32. The option --help prints out usage info.

For example, to compile and link a Runtime System `ex_rt' with the linked foreign resource ex statically linked, first create the linked foreign resource (see section Creating the Linked Foreign Resource) using the +static option. Assuming the Runtime System's main program is in the file `main.c', then type:

% spmkrs ex_rt +c main.c +r ex

If you prefer not to use the utility commands, an alternative method is described in section Glue Code Generator.

Running the Executable

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

the executable
This is your application program created as described above.
the runtime kernel
This is a shared object or a DLL, usually `$SP_PATH/../libsprt37.so' under UNIX, or `%SP_PATH%\..\sprt37.dll' under Windows.
the runtime library
The saved state `$SP_PATH/bin/sprt.sav' contains the built-in predicates written in Prolog. It is restored into the program at runtime by the function SP_initialize().
your Prolog code
Either as a saved state, object code (`.ql' files), or source code (`.pl' files). They must be explicitly loaded by the program at runtime.
your linked foreign resources
Any linked foreign resources which are not statically linked to the executable, including any linked foreign resources for library modules which are located in `$SP_PATH/library'.

Also, on most UNIX platforms, it is necessary to set an environment variable, typically LD_LIBRARY_PATH, for shared objects to be found at runtime. See section Finding Libraries at Runtime.

Development Systems

This describes how to create a Development System with a set of linked foreign resources. For the SICStus library modules containing C-code, such linked foreign resources already exist in `$SP_PATH/library'. For user foreign code, you must first create linked foreign resources for them.

A utility command is provided for the generation of such systems. This command, located in `$SP_PATH/bin', is configurable as described in the Release Notes.

spmkds [--help] [-S] Executable [Resources]
Generates a Development System with any foreign resources mentioned in Resources statically linked. The resources must be created with the +static option. Note however that a statically linked dynamic resource is not, in general, statically linked from the operating system's point of view. It must therefore be accessible at runtime as well as at build-time. The option -S causes the SICStus Runtime Kernel and development system extensions to be statically linked into the executable, thus eliminating the need to load these at runtime. This produces a significantly larger executable. Observe that this option has no effect on the resources. Note: This option is not available on Win32. The option --help prints out usage info.

For example, to compile and link a Development System `ex_stat' with the linked foreign resource ex statically linked, first create the linked foreign resource (see section Creating the Linked Foreign Resource) using the +static option. Then type:

% spmkds ex_stat ex

The executable can then be run as:

% sicstus -base ./ex_stat

and optionally more command-line options as for the standard sicstus command.

The generated executable will run the usual Prolog top level, and the full develoment environment is available as usual.

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('Stockholm', 'Uppsala').
connection('Uppsala', '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 <stdio.h>
#include <sicstus/sicstus.h>

void write_path(SP_term_ref path)
{
  char *text = NULL;
  SP_term_ref 
    tail = SP_new_term_ref(),
    via = SP_new_term_ref();

  SP_put_term(tail,path);

  while (SP_get_list(tail,via,tail))
  {
    if (text) 
      printf(" -> ");

    SP_get_string(via, &text);
    printf("%s",text);
  }
  printf("\n");  
}

int main(int argc, char **argv)
{
  int rval;
  SP_pred_ref pred;
  SP_qid goal;
  SP_term_ref from, to, path;
  
  /* Initialize Prolog engine. This call looks up SP_PATH in order to
   * find the Runtime Library.  */
  if (SP_FAILURE == SP_initialize(argc, argv, NULL))
    {
      fprintf(stderr, "SP_initialize failed: %s\n", SP_error_message(SP_errno));
      exit(1);
    }

  rval = SP_restore("train.sav");

  if (rval == SP_ERROR || rval == SP_FAILURE)
    {
      fprintf(stderr, "Could not restore \"train.sav\".\n");
      exit(1);
    }

  /* Look up connected/3. */
  if (!(pred = SP_predicate("connected",3,"")))
    {
      fprintf(stderr, "Could not find connected/3.\n");
      exit(1);
    }

  /* Create the three arguments to connected/3. */
  SP_put_string(from = SP_new_term_ref(), "Stockholm");
  SP_put_string(to = SP_new_term_ref(), "Orebro");
  SP_put_variable(path = SP_new_term_ref());

  /* Open the query. In a development system, the query would look like:
   * 
   * | ?- connected('Stockholm','Orebro',X).
   */
  if (!(goal = SP_open_query(pred,from,to,path)))
    {
      fprintf(stderr, "Failed to open query.\n");
      exit(1);
    }

  /* 
   * Loop through all the solutions.
   */
  while (SP_next_solution(goal))
    {
      printf("Path: ");
      write_path(path);
    }
  
  SP_close_query(goal);

  exit(0);
}

Create the saved-state containing the Prolog code:

% sicstus
SICStus 3.7: Tue Jun 02 12:29:02 MET DST 1998
| ?- compile(train).
{compiling /home/jojo/sicstus/examples/train.pl...}
{/home/jojo/sicstus/examples/train.pl compiled, 40 msec 2848 bytes}

yes
| ?- save_program('train.sav').
{SICStus state saved in /home/jojo/sicstus/examples/train.sav}

yes
| ?- halt.

Create the executable using spmkrs:

% spmkrs train +c train.c
SICStus 3.7: Tue Jun 02 12:29:02 MET DST 1998
{/var/tmp/aaa0ch9LZ.c generated, 0 msec}

yes

And finally, run the executable:

% ./train
Path: Stockholm -> Katrineholm -> Hallsberg -> Kumla -> Orebro
Path: Stockholm -> Vasteras -> Orebro
Path: Stockholm -> Uppsala -> Vasteras -> Orebro

I/O 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_resource(example, [open_buf_stream,stream_to_chars]).

:- load_foreign_resource(example).

%% 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_resource(math, [sqrt]).

foreign(sqrt_check, c, sqrt(+float, [-float])).

:- load_foreign_resource(math).

A linked foreign resource is created:

% splfr math math.pl +c math.c +l -lm

A simple session using this function could be:

% sicstus
SICStus 3  #5: Sat Feb 24 00:35:37 MET 1996
| ?- [math].
{consulting /home/san/pl/math.pl...}
{/home/san/pl/math.pl consulted, 10 msec 816 bytes}

yes
| ?- sqrt(5.0,X).

X = 2.23606797749979 ? 

yes
| ?- sqrt(a,X).
{TYPE ERROR: sqrt(a,_30) - 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:

% splfr math math.pl +static +c math.c +l -lm
SICStus 3  #5: Sat Feb 24 00:35:37 MET 1996
{/tmp/00249aaa.c generated, 10 msec}

yes
% spmkds mathsp math               
SICStus 3  #5: Sat Feb 24 00:35:37 MET 1996
{/tmp/00274aaa.c generated, 0 msec}

yes
% sicstus -base ./mathsp
SICStus 3  #5: Sat Feb 24 00:35:37 MET 1996
| ?- [math].
{consulting /home/san/pl/math.pl...}
{/home/san/pl/math.pl consulted, 0 msec 864 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 first, previous, next, last section, table of contents.