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:
foreign/(2-3)
declarations.
void *
in the function definitions may
be changed to char *
on non ANSI conforming C compilers.
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.
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_errnoThe 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 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.
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.
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 declaration predicates:
foreign_resource(+ResourceName,+Functions)
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)
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.
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
long
and
passed to the C function.
Prolog: +float
C: double
double
and
passed to the C function.
Prolog: +atom
C: unsigned long
Prolog: +chars
C: char *
Prolog: +string
C: char *
Prolog: +string(N)
C: char *
Prolog: +address
C: void *
void *
pointer.
Prolog: +address(TypeName)
C: TypeName *
TypeName *
pointer.
Prolog: +term
C: SP_term_ref
Prolog: -integer
C: long *
long
.
The value returned will be converted to a Prolog integer. Prolog: -float
C: double *
double
.
The value returned will be converted to a Prolog float. Prolog: -atom
C: unsigned long *
unsigned
long
. The value returned should be the canonical representation of a
Prolog atom. Prolog: -chars
C: char **
char *
.
The returned string will be converted to a Prolog list of character
codes. Prolog: -string
C: char **
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 *
Prolog: -address
C: void **
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 **
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
Prolog: [-integer]
C: long F()
long
. The value returned will be
converted to a Prolog integer. Prolog: [-float]
C: double F()
double
. The value returned will
be converted to a Prolog float. Prolog: [-atom]
C: unsigned long F()
unsigned long
. The value
returned must be the canonical representation of a Prolog atom. Prolog: [-chars]
C: char *F()
char *
. The returned string will
be converted to a Prolog list of character codes. Prolog: [-string]
C: char *F()
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()
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()
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()
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()
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)
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]
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)
+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)
The following predicates are provided for backwards compatibility and should be avoided in new code:
foreign_file(+File,+Functions)
load_foreign_files/2
.
load_foreign_files(:ObjectFiles,+Libraries)
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
load_foreign_resource/1
.
SP_WHEN_RESTORE
For deinit functions:
SP_WHEN_EXPLICIT
unload_foreign_resource/1
.
SP_WHEN_SAVE
SP_WHEN_EXIT
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.
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 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.
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)
t
a new Prolog variable.
int SP_put_integer(SP_term_ref t, long l)
t
a Prolog integer from a C long integer.
int SP_put_float(SP_term_ref t, double d)
t
a Prolog float from a C double.
int SP_put_atom(SP_term_ref t, unsigned long a)
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)
t
a Prolog atom from a C string.
int SP_put_address(SP_term_ref t, void *pointer)
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)
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)
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)
t
a Prolog number by parsing the string in s
.
int SP_put_functor(SP_term_ref t, unsigned long name, int arity)
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)
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, ...)
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)
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.
int SP_get_integer(SP_term_ref t, long *l)
*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)
*d
the C double
corresponding to a Prolog
number.
int SP_get_atom(SP_term_ref t, unsigned long *a)
*a
the canonical representation of a Prolog atom.
int SP_get_string(SP_term_ref t, char **name)
*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)
*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)
*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)
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)
*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)
*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)
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)
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.
int SP_term_type(SP_term_ref t)
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)
int SP_is_integer(SP_term_ref t)
int SP_is_float(SP_term_ref t)
int SP_is_atom(SP_term_ref t)
int SP_is_compound(SP_term_ref t)
int SP_is_list(SP_term_ref t)
int SP_is_atomic(SP_term_ref t)
int SP_is_number(SP_term_ref t)
int SP_unify(SP_term_ref x, SP_term_ref y)
int SP_compare(SP_term_ref x, SP_term_ref y)
-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:
void *SP_malloc(unsigned int size)
size
bytes. In Muse, the blocks are allocated in shared memory.
void *SP_realloc(void *ptr, unsigned int size)
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)
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 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); }
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:
stream_code(+Stream,?StreamCode)
stream_code(?Stream,+StreamCode)
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
user_input
in
Prolog. Which stream is referenced by user_input
is controlled by the
flag user_input
(see prolog_flag/3
) .
SP_stdout
user_output
in
Prolog. Which stream is referenced by user_output
is controlled by
the flag user_output
(see prolog_flag/3
).
SP_stderr
user_error
in
Prolog. Which stream is referenced by user_error
is controlled by
the flag user_error
(see prolog_flag/3
).
SP_curin
SP_stdin
.
It can be changed with the predicates see/1
and set_input/1
.
SP_curout
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 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)
int my_fputc(char c, int handle)
c
and return the character written.
int my_flush(void *handle)
int my_eof(void *handle)
void my_clrerr(void *handle)
int my_close(void *handle)
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_stream *backward;
SP_stream *forward;
SP_make_stream()
and the deletion is done from the Prolog
predicate close/1
.
char *filename;
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;
void SP_set_tty(SP_stream *s)
int fd;
void *user_handle;
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); }
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)
hook
.
SP_UserStreamPostHook *SP_set_user_stream_post_hook(SP_UserStreamPostHook *hook)
hook
.
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
SP_STREAMHOOK_STDOUT
SP_STREAMHOOK_STDERR
The hook should return a standard SICStus I/O stream, as described in section Defining a New Stream.
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.
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()
.
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)
int muse_num_workers(void)
int muse_worker_id(void)
void muse_init_lock(int *lock)
void muse_lock(int *lock)
void muse_un_lock(int *lock)
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()
).
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 *)
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 *)
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 *)
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:
SP_signal()
halt/0
, abort/0
and reinitialise/0
will return to C
SP_initialize()
.
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
malloc()
/free()
.
The other arguments are ignored.
Same as the `-m' option in Development Systems. (see section Getting Started).
MM_USE_SBRK
sbrk()
.
The default for UNIX; not available for Windows.
The other arguments are ignored.
MM_USE_SPARSE
VirtualAlloc()
/VirtualFree()
.
The default for Windows; not available for UNIX.
The other arguments are ignored.
MM_USE_OTHER
In the latter case, the other arguments should be functions as specified below:
alloc_hook
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
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
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
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.
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)
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]
+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.
To execute a Runtime System on a target machine you need:
SP_initialize()
.
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.
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]
+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.
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
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); }
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
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.