Go to the previous, next section.
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:
foreign/(2-3)
declarations.
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:
SP_PATH
is assumed to be set to a relevant directory
name (the default is `/usr/local/lib/sicstus').
user:library_directory/1
is derived from this environment variable.
SP_PATH
.
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)
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.
To perform either dynamic or static linking, the following predicates are used:
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'.
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);
.
'-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'], []).
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.
long
and
passed to the C function.
double
and
passed to the C function.
void *
pointer.
TypeName *
pointer.
long
.
The value returned will be converted to a Prolog integer. double
.
The value returned will be converted to a Prolog float. unsigned
long
. The value returned should be the canonical representation of a
Prolog atom. char *
.
The returned string will be converted to a Prolog list of character
codes. 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. void *
.
The returned value, which is constrained according to (see section Creating Prolog Terms, SP_put_address()), will be converted to a Prolog integer.
TypeName *
. The returned value, which is constrained
according to (see section Creating Prolog Terms, SP_put_address()), will be
converted to a Prolog integer.
long
. The value returned will be
converted to a Prolog integer. double
. The value returned will
be converted to a Prolog float. unsigned long
. The value
returned must be the canonical representation of a Prolog atom. char *
. The returned string will
be converted to a Prolog list of character codes. 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. 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. void *
. The returned value, which
is constrained according to (see section Creating Prolog Terms,
SP_put_address()), will be converted to a Prolog integer. TypeName *
. The returned
value, which is constrained according to (see section Creating Prolog Terms,
SP_put_address()), will be converted to a Prolog integer.
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
foreign/(2-3)
and
foreign_file/2
declarations.
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.
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.
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)
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.
t
a new Prolog variable.
t
a Prolog integer from a C long integer.
t
a Prolog float from a C double.
t
a Prolog atom from a
,
which must be the canonical representation of a Prolog atom.
(see section Calling C from Prolog).
t
a Prolog atom from a C string.
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
.
t
a Prolog list of the character codes in s
. The
list is terminated by the value of tail
.
t
a Prolog number by parsing the string in s
.
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.
t
a Prolog list whose head and tail are both unbound
variables.
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.
t
a Prolog list whose head and tail are the values of
head
and tail
.
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.
*l
the C long
corresponding to a Prolog
number. The value must fit in *l
for the operation to succeed.
*d
the C double
corresponding to a Prolog
number.
*a
the canonical representation of a Prolog atom.
**name
a pointer to the string that is the name of a
Prolog atom. This string must not be modified.
**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()).
**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.
**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.
*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.
*head
and *tail
the head and tail of a Prolog list.
*arg
the i
:th argument of a Prolog compound
term. This is similar to calling arg/3
with the third argument
unbound.
There is one general function for type testing of Prolog terms and a set of specialized, more efficient, functions, one for each term type.
SP_TYPE_VARIABLE
, SP_TYPE_INTEGER
, SP_TYPE_FLOAT
,
SP_TYPE_ATOM
, or SP_TYPE_COMPOUND
is returned.
-1
if x
@< y
, 0
if x
==
y
and 1
if x
@> y
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:
size
bytes. In Muse, the blocks are allocated in shared memory.
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.
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.
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)
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, ...)
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.
SP_open_query()
SP_next_solution()
to find a solution. Call this predicate
again to find more solutions if there are any.
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)
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); }
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)
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:
From the Prolog level there is a unique number that identifies a stream. This identifier can be converted from/to a Prolog stream:
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:
user_input
in
Prolog. Which stream is referenced by user_input
is controlled by the
flag user_input
(see prolog_flag/3
) .
user_output
in
Prolog. Which stream is referenced by user_output
is controlled by
the flag user_output
(see prolog_flag/3
).
user_error
in
Prolog. Which stream is referenced by user_error
is controlled by
the flag user_error
(see prolog_flag/3
).
SP_stdin
.
It can be changed with the predicates see/1
and set_input/1
.
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.
The following steps are required to define a new stream in C:
SP_make_stream()
.
SP_stream
structure than the default values set by
SP_make_stream()
.
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.
c
and return the character written.
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:
SP_stream
structure
SP_stream
structure with default values
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.
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_make_stream()
and the deletion is done from the Prolog
predicate close/1
.
SP_make_stream()
.
May be set to a suitable string, provided the string will not be
overwritten until the stream is closed.
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); }
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:
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()
).
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.
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.
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.)
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:
ensure_loaded/1
, use_module/1
, and
use_module/2
can only load `.ql' files.
SP_signal()
halt/0
, abort/0
and reinitialise/0
will return to C
SP_initialize()
.
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)
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.
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:
load_foreign_files/2
, or `$SP_PATH/bin/flinkage.c' if no
functions will be called from Prolog (see section Calling C from Prolog).
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
To execute a Runtime System on a target machine you need:
SP_initialize()
.
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.
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
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); }
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
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.