If Prolog encounters a foreign predicate at run time it will call a
function specified in the predicate definition of the foreign predicate.
The arguments 1, ... , <arity> pass the
Prolog arguments to the goal as Prolog terms. Foreign functions should
be declared of type
foreign_t. Deterministic foreign functions have two
alternatives to return control back to Prolog:
return TRUE.
return FALSE.
By default foreign predicates are deterministic. Using the
PL_FA_NONDETERMINISTIC attribute (see PL_register_foreign())
it is possible to register a predicate as a non-deterministic predicate.
Writing non-deterministic foreign predicates is slightly more
complicated as the foreign function needs context information for
generating the next solution. Note that the same foreign function should
be prepared to be simultaneously active in more than one goal. Suppose
the natural_number_below_n/2 is a non-deterministic foreign predicate,
backtracking over all natural numbers lower than the first argument. Now
consider the following predicate:
quotient_below_n(Q, N) :-
natural_number_below_n(N, N1),
natural_number_below_n(N, N2),
Q =:= N1 / N2, !.
|
In this predicate the function natural_number_below_n/2 simultaneously generates solutions for both its invocations.
Non-deterministic foreign functions should be prepared to handle three different calls from Prolog:
PL_FIRST_CALL)PL_REDO)PL_CUTTED)Both the context information and the type of call is provided by an
argument of type control_t appended to the argument list
for deterministic foreign functions. The macro PL_foreign_control()
extracts the type of call from the control argument. The foreign
function can pass a context handle using the PL_retry*() macros
and extract the handle from the extra argument using the
PL_foreign_context*() macro.
PL_CUTTED case and should be aware that the other
arguments are not valid in this case.
PL_FIRST_CALL the context value is 0L. Otherwise it is the
value returned by the last PL_retry()
associated with this goal (both if the call type is PL_REDO
as PL_CUTTED).
Note: If a non-deterministic foreign function returns using
PL_succeed or PL_fail, Prolog assumes the foreign function has cleaned
its environment. No call with control argument PL_CUTTED
will follow.
The code of figure 6 shows a skeleton for a non-deterministic foreign predicate definition.
typedef struct /* define a context structure */
{ ...
} context;
foreign_t
my_function(term_t a0, term_t a1, foreign_t handle)
{ struct context * ctxt;
switch( PL_foreign_control(handle) )
{ case PL_FIRST_CALL:
ctxt = malloc(sizeof(struct context));
...
PL_retry_address(ctxt);
case PL_REDO:
ctxt = PL_foreign_context_address(handle);
...
PL_retry_address(ctxt);
case PL_CUTTED:
free(ctxt);
PL_succeed;
}
}
|
| Figure 6 : Skeleton for non-deterministic foreign functions |
The following functions provide for communication using atoms and functors.
term_t
normally use
PL_get_atom_chars()
or PL_get_atom_nchars().
With the introduction of atom-garbage collection in version 3.3.0, atoms no longer live as long as the process. Instead, their lifetime is guaranteed only as long as they are referenced. In the single-threaded version, atom garbage collections are only invoked at the call-port. In the multi-threaded version (see section 6, they appear asynchronously, except for the invoking thread.
For dealing with atom garbage collection, two additional functions are provided:
Please note that the following two calls are different with respect to atom garbage collection:
PL_unify_atom_chars(t, "text");
PL_unify_atom(t, PL_new_atom("text"));
|
The latter increments the reference count of the atom text,
which effectively ensures the atom will never be collected. It is
advised to use the *_chars() or *_nchars() functions whenever
applicable.
Each argument of a foreign function (except for the control argument)
is of type term_t, an opaque handle to a Prolog term. Three
groups of functions are available for the analysis of terms. The first
just validates the type, like the Prolog predicates var/1, atom/1,
etc and are called PL_is_*(). The second group attempts to
translate the argument into a C primitive type. These predicates take a term_t
and a pointer to the appropriate C-type and return TRUE or
FALSE depending on successful or unsuccessful translation.
If the translation fails, the pointed-to data is never modified.
if ( PL_is_atom(t) )
{ char *s;
PL_get_atom_chars(t, &s);
...;
}
or
char *s;
if ( PL_get_atom_chars(t, &s) )
{ ...;
}
|
PL_VARIABLE | An unbound variable. The value of term as such is a unique identifier for the variable. |
PL_ATOM | A Prolog atom. |
PL_STRING | A Prolog string. |
PL_INTEGER | A Prolog integer. |
PL_FLOAT | A Prolog floating point number. |
PL_TERM | A compound term. Note that a list is a compound term ./2 . |
The functions PL_is_<type> are an alternative to PL_term_type().
The test PL_is_variable(term)
is equivalent to
PL_term_type(term)
== PL_VARIABLE, but the first is considerably faster. On the
other hand, using a switch over PL_term_type()
is faster and more readable then using an if-then-else using the
functions below. All these functions return either TRUE or FALSE.
.
The functions PL_get_*() read information from a Prolog term. Most of them take two arguments. The first is the input term and the second is a pointer to the output value or a term-reference.
BUF_RING
implies, if the data is not static (as from an atom), the data is copied
to the next buffer from a ring of 16 buffers. This is a convenient way
of converting multiple arguments passed to a foreign predicate to
C-strings. If BUF_MALLOC is used, the data must be freed using PL_free()
when not needed any longer.
CVT_ATOM | Convert if term is an atom |
CVT_STRING | Convert if term is a string |
CVT_LIST | Convert if term is a list of integers between 1 and 255 |
CVT_INTEGER | Convert if term is
an integer (using %d) |
CVT_FLOAT | Convert if term is a
float (using %f) |
CVT_NUMBER | Convert if term is a integer or float |
CVT_ATOMIC | Convert if term is atomic |
CVT_VARIABLE | Convert variable to print-name |
CVT_WRITE | Convert any term that
is not converted by any of the other flags using write/1.
If no BUF_* is provided, BUF_RING is implied. |
CVT_ALL | Convert if term is any
of the above, except for
CVT_VARIABLE and CVT_WRITE |
BUF_DISCARDABLE | Data must copied immediately |
BUF_RING | Data is stored in a ring of buffers |
BUF_MALLOC | Data is copied to a new buffer returned by PL_malloc(3). When no longer needed the user must call PL_free() on the data. |
PL_get_chars(l, s,
CVT_LIST|flags), provided flags
contains no of the CVT_* flags.
true or false,
set val to the C constant TRUE or FALSE
and return success. otherwise return failure.
All internal text-representation of SWI-Prolog is represented using
char * plus length and allow for 0-bytes in them.
The foreign library supports this by implementing a *_nchars() function
for each applicable *_chars() function. Below we briefly present the
signatures of these functions. For full documentation consult the
*_chars() function.
In addition, the following functions are available for creating and inspecting atoms:
The functions from this section are intended to read a Prolog list from C. Suppose we expect a list of atoms, the following code will print the atoms, each on a line:
foreign_t
pl_write_atoms(term_t l)
{ term_t head = PL_new_term_ref(); /* variable for the elements */
term_t list = PL_copy_term_ref(l); /* copy as we need to write */
while( PL_get_list(list, head, list) )
{ char *s;
if ( PL_get_atom_chars(head, &s) )
Sprintf("%s\n", s);
else
PL_fail;
}
return PL_get_nil(list); /* test end for [] */
}
|
assign a term-reference
to the head to h and to the tail to t.
assign a term-reference
to the head to h.
assign a term-reference
to the tail to t.
.
Figure 7 shows a simplified definition of write/1 to illustrate the described functions. This simplified version does not deal with operators. It is called display/1, because it mimics closely the behaviour of this Edinburgh predicate.
foreign_t
pl_display(term_t t)
{ functor_t functor;
int arity, len, n;
char *s;
switch( PL_term_type(t) )
{ case PL_VARIABLE:
case PL_ATOM:
case PL_INTEGER:
case PL_FLOAT:
PL_get_chars(t, &s, CVT_ALL);
Sprintf("%s", s);
break;
case PL_STRING:
PL_get_string_chars(t, &s, &len);
Sprintf("\"%s\"", s);
break;
case PL_TERM:
{ term_t a = PL_new_term_ref();
PL_get_name_arity(t, &name, &arity);
Sprintf("%s(", PL_atom_chars(name));
for(n=1; n<=arity; n++)
{ PL_get_arg(n, t, a);
if ( n > 1 )
Sprintf(", ");
pl_display(a);
}
Sprintf(")");
break;
default:
PL_fail; /* should not happen */
}
PL_succeed;
}
|
| Figure 7 : A Foreign definition of display/1 |
Terms can be constructed using functions from the PL_put_*() and PL_cons_*() families. This approach builds the term `inside-out', starting at the leaves and subsequently creating compound terms. Alternatively, terms may be created `top-down', first creating a compound holding only variables and subsequently unifying the arguments. This section discusses functions for the first approach. This approach is generally used for creating arguments for PL_call() and PL_open_query.
Put a string, represented by a length/start pointer pair in the term-reference. The data will be copied. This interface can deal with 0-bytes in the string. See also section 7.6.17.
PL_put_functor(l,
PL_new_functor(PL_new_atom("."), 2)).
PL_put_atom_chars("[]").
animal(gnu, 50), use:
{ term_t a1 = PL_new_term_ref();
term_t a2 = PL_new_term_ref();
term_t t = PL_new_term_ref();
functor_t animal2;
/* animal2 is a constant that may be bound to a global
variable and re-used
*/
animal2 = PL_new_functor(PL_new_atom("animal"), 2);
PL_put_atom_chars(a1, "gnu");
PL_put_integer(a2, 50);
PL_cons_functor(t, animal2, a1, a2);
}
|
After this sequence, the term-references a1 and a2 may be used for other purposes.
char **. The list
is built tail-to-head. The PL_unify_*() functions can be used
to build a list head-to-tail.
void
put_list(term_t l, int n, char **words)
{ term_t a = PL_new_term_ref();
PL_put_nil(l);
while( --n >= 0 )
{ PL_put_atom_chars(a, words[n]);
PL_cons_list(l, a, l);
}
}
|
Note that l can be redefined within a PL_cons_list call as shown here because operationally its old value is consumed before its new value is set.
The functions of this sections unify terms with other terms or translated C-data structures. Except for PL_unify(), the functions of this section are specific to SWI-Prolog. They have been introduced to make translation of old code easier, but also because they provide for a faster mechanism for returning data to Prolog that requires less term-references. Consider the case where we want a foreign function to return the host name of the machine Prolog is running on. Using the PL_get_*() and PL_put_*() functions, the code becomes:
foreign_t
pl_hostname(term_t name)
{ char buf[100];
if ( gethostname(buf, sizeof(buf)) )
{ term_t tmp = PL_new_term_ref();
PL_put_atom_chars(tmp, buf);
return PL_unify(name, tmp);
}
PL_fail;
}
|
Using PL_unify_atom_chars(), this becomes:
foreign_t
pl_hostname(term_t name)
{ char buf[100];
if ( gethostname(buf, sizeof(buf)) )
return PL_unify_atom_chars(name, buf);
PL_fail;
}
|
char **. We could use the example described by
PL_put_list(), followed
by a call to PL_unify(), or
we can use the code below. If the predicate argument is unbound, the
difference is minimal (the code based on PL_put_list()
is probably slightly faster). If the argument is bound, the code below
may fail before reaching the end of the word-list, but even if the
unification succeeds, this code avoids a duplicate (garbage) list and a
deep unification.
foreign_t
pl_get_environ(term_t env)
{ term_t l = PL_copy_term_ref(env);
term_t a = PL_new_term_ref();
extern char **environ;
char **e;
for(e = environ; *e; e++)
{ if ( !PL_unify_list(l, a, l) ||
!PL_unify_atom_chars(a, *e) )
PL_fail;
}
return PL_unify_nil(l);
}
|
.
Special attention is required when passing numbers. C `promotes' any
integral smaller than int to int. I.e. the
types
char, short and int are all
passed as int. In addition, on most 32-bit platforms int
and long are the same. Upto version 4.0.5, only PL_INTEGER
could be specified which was taken from the stack as long.
Such code fails when passing small integral types on machines where int
is smaller than long. It is advised to use PL_SHORT, PL_INT
or PL_LONG as appropriate. Similar, C compilers promote
float to double and therefore PL_FLOAT
and
PL_DOUBLE are synonyms.
The type identifiers are:
PL_VARIABLE nonePL_FUNCTOR.
PL_ATOM atom_t
PL_CHARS const char *
PL_NCHARS unsigned int, const char *
PL_SHORT shortshort is promoted to int, PL_SHORT
is a synonym for PL_INT.
PL_INT int
PL_LONG long
PL_INTEGER long
PL_DOUBLE double
PL_FLOAT double
PL_POINTER void *
PL_STRING const char *
PL_TERM term_t
PL_CHARS const char *char *,
as in PL_unify_atom_chars().
PL_FUNCTOR functor_t, ...
PL_FUNCTOR_CHARS const char *name, int arity,
...PL_FUNCTOR.
PL_LIST int length, ...For example, to unify an argument with the term language(dutch),
the following skeleton may be used:
static functor_t FUNCTOR_language1;
static void
init_constants()
{ FUNCTOR_language1 = PL_new_functor(PL_new_atom("language"), 1);
}
foreign_t
pl_get_lang(term_t r)
{ return PL_unify_term(r,
PL_FUNCTOR, FUNCTOR_language1,
PL_CHARS, "dutch");
}
install_t
install()
{ PL_register_foreign("get_lang", 1, pl_get_lang, 0);
init_constants();
}
|
FALSE
if a syntax error was encountered and TRUE after successful
completion. In addition to returning FALSE, the
exception-term is returned in t on a syntax error. See also term_to_atom/2.
The following example build a goal-term from a string and calls it.
int
call_chars(const char *goal)
{ fid_t fid = PL_open_foreign_frame();
term_t g = PL_new_term_ref();
BOOL rval;
if ( PL_string_to_term(goal, g) )
rval = PL_call(goal, NULL);
else
rval = FALSE;
PL_discard_foreign_frame(fid);
return rval;
}
...
call_chars("consult(load)");
...
|
'\'', the result is a quoted atom. If chr is
'"', the result is a string. The result string is stored in
the same ring of buffers as described with the BUF_RING
argument of PL_get_chars();
In the current implementation, the string is surrounded by
chr and any occurence of chr is doubled. In the
future the behaviour will depend on the character_escape
prolog-flag. See current_prolog_flag/2.
The Prolog engine can be called from C. There are two interfaces for this. For the first, a term is created that could be used as an argument to call/1 and next PL_call() is used to call Prolog. This system is simple, but does not allow to inspect the different answers to a non-deterministic goal and is relatively slow as the runtime system needs to find the predicate. The other interface is based on PL_open_query(), PL_next_solution() and PL_cut_query() or PL_close_query(). This mechanism is more powerful, but also more complicated to use.
This section discusses the functions used to communicate about
predicates. Though a Prolog predicate may defined or not, redefined,
etc., a Prolog predicate has a handle that is not destroyed, nor moved.
This handle is known by the type predicate_t.
This section discusses the functions for creating and manipulating queries from C. Note that a foreign context can have at most one active query. This implies it is allowed to make strictly nested calls between C and Prolog (Prolog calls C, calls Prolog, calls C, etc., but it is not allowed to open multiple queries and start generating solutions for each of them by calling PL_next_solution(). Be sure to call PL_cut_query() or PL_close_query() on any query you opened before opening the next or returning control back to Prolog.
Opens a query and returns an identifier for it. This function always
succeeds, regardless whether the predicate is defined or not. ctx
is the context module of the goal. When NULL, the
context module of the calling context will be used, or user
if there is no calling context (as may happen in embedded systems). Note
that the context module only matters for module_transparent
predicates. See context_module/1
and module_transparent/1.
The p argument specifies the predicate, and should be the
result of a call to PL_pred()
or PL_predicate(). Note
that it is allowed to store this handle as global data and reuse it for
future queries. The term-reference t0 is the first of a
vector of term-references as returned by
PL_new_term_refs(n).
The flags arguments provides some additional options concerning debugging and exception handling. It is a bitwise or of the following values:
PL_Q_NORMAL
PL_Q_NODEBUG
PL_Q_CATCH_EXCEPTION
PL_Q_PASS_EXCEPTIONPL_Q_CATCH_EXCEPTION, but do not invalidate the
exception-term while calling PL_close_query().
This option is experimental.
The example below opens a query to the predicate is_a/2 to find the ancestor of for some name.
char *
ancestor(const char *me)
{ term_t a0 = PL_new_term_refs(2);
static predicate_t p;
if ( !p )
p = PL_predicate("is_a", 2, "database");
PL_put_atom_chars(a0, me);
PL_open_query(NULL, PL_Q_NORMAL, p, a0);
...
}
|
TRUE if a solution was found, or FALSE to
indicate the query could not be proven. This function may be called
repeatedly until it fails to generate all solutions to the query.
TRUE if the call succeeds, FALSE
otherwise.
Figure 8 shows an example
to obtain the number of defined atoms. All checks are omitted to improve
readability.
The Prolog data created and term-references needed to setup the call and/or analyse the result can in most cases be discarded right after the call. PL_close_query() allows for destructing the data, while leaving the term-references. The calls below may be used to destroy term-references and data. See figure 8 for an example.
It is obligatory to call either of the two closing functions to discard a foreign frame. Foreign frames may be nested.
int
count_atoms()
{ fid_t fid = PL_open_foreign_frame();
term_t goal = PL_new_term_ref();
term_t a1 = PL_new_term_ref();
term_t a2 = PL_new_term_ref();
functor_t s2 = PL_new_functor(PL_new_atom("statistics"), 2);
int atoms;
PL_put_atom_chars(a1, "atoms");
PL_cons_functor(goal, s2, a1, a2);
PL_call(goal, NULL); /* call it in current module */
PL_get_integer(a2, &atoms);
PL_discard_foreign_frame(fid);
return atoms;
}
|
| Figure 8 : Calling Prolog |
Modules are identified via a unique handle. The following functions are available to query and manipulate modules.
NULL it will be set to the
context module. Otherwise it will be left untouched. The following
example shows how to obtain the plain term and module if the default
module is the user module:
{ module m = PL_new_module(PL_new_atom("user"));
term_t plain = PL_new_term_ref();
PL_strip_module(term, &m, plain);
...
|
This section discusses PL_exception(), PL_throw()
and
PL_raise_exception(),
the interface functions to detect and generate Prolog exceptions from
C-code. PL_throw() and PL_raise_exception()
from the C-interface to raise an exception from foreign code. PL_throw()
exploits the C-function longjmp() to return immediately to the innermost
PL_next_solution(). PL_raise_exception()
registers the exception term and returns FALSE. If a
foreign predicate returns FALSE, while and exception-term is registered
a Prolog exception will be raised by the virtual machine.
Calling these functions outside the context of a function implementing a foreign predicate results in undefined behaviour.
PL_exception() may be used after a call to PL_next_solution() fails, and returns a term reference to an exception term if an exception was raised, and 0 otherwise.
If a C-function, implementing a predicate calls Prolog and detects an exception using PL_exception(), it can handle this exception, or return with the exception. Some caution is required though. It is not allowed to call PL_close_query() or PL_discard_foreign_frame() afterwards, as this will invalidate the exception term. Below is the code that calls a Prolog defined arithmetic function (see arithmethic_function/1).
If PL_next_solution() succeeds, the result is analysed and translated to a number, after which the query is closed and all Prolog data created after PL_open_foreign_frame() is destroyed. On the other hand, if PL_next_solution() fails and if an exception was raised, just pass it. Otherwise generate an exception (PL_error() is an internal call for building the standard error terms and calling PL_raise_exception()). After this, the Prolog environment should be discarded using PL_cut_query() and PL_close_foreign_frame() to avoid invalidating the exception term.
static int
prologFunction(ArithFunction f, term_t av, Number r)
{ int arity = f->proc->definition->functor->arity;
fid_t fid = PL_open_foreign_frame();
qid_t qid;
int rval;
qid = PL_open_query(NULL, PL_Q_NORMAL, f->proc, av);
if ( PL_next_solution(qid) )
{ rval = valueExpression(av+arity-1, r);
PL_close_query(qid);
PL_discard_foreign_frame(fid);
} else
{ term_t except;
if ( (except = PL_exception(qid)) )
{ rval = PL_throw(except); /* pass exception */
} else
{ char *name = stringAtom(f->proc->definition->functor->name);
/* generate exception */
rval = PL_error(name, arity-1, NULL, ERR_FAILED, f->proc);
}
PL_cut_query(qid); /* donot destroy data */
PL_close_foreign_frame(fid); /* same */
}
return rval;
}
|
FALSE. Below is an example returning an
exception from foreign predicate:
foreign_t
pl_hello(term_t to)
{ char *s;
if ( PL_get_atom_chars(to, &s) )
{ Sprintf("Hello \"%s\"\n", s);
PL_succeed;
} else
{ term_t except = PL_new_term_ref();
PL_unify_term(except,
PL_FUNCTOR_CHARS, "type_error", 2,
PL_CHARS, "atom",
PL_TERM, to);
return PL_raise_exception(except);
}
}
|
SWI-Prolog offers both a C and Prolog interface to deal with software interrupts (signals). The Prolog mapping is defined in section 4.10. This subsection deals with handling signals from C.
If a signal is not used by Prolog and the handler does not call Prolog in any way, the native signal interface routines may be used.
Some versions of SWI-Prolog, notably running on popular Unix
platforms, handle SIG_SEGV for guarding the Prolog stacks.
If the application whishes to handle this signal too, it should use PL_signal()
to install its handler after initialisating Prolog. SWI-Prolog will pass
SIG_SEGV to the user code if it detected the signal is not
related to a Prolog stack overflow.
Any handler that wishes to call one of the Prolog interface functions should call PL_signal() for its installation.
After a signal handler is registered using this function, the native signal interface redirects the signal to a generic signal handler inside SWI-Prolog. This generic handler validates the environment, creates a suitable environment for calling the interface functions described in this chapter and finally calls the registered user-handler.
By default, signals are handled asynchronously (i.e. at the time they
arrive). It is inheritly dangerous to call extensive code fragments, and
especially exception related code from asynchronous handlers. The
interface allows for synchronous handling of signals. In this
case the native OS handler just schedules the signal using PL_raise(),
which is checked by PL_handle_signals()
at the call- and redo-port. This behaviour is realised by or-ing sig
with the constant
PL_SIGSYNC. (63)
Signal handling routines may raise exceptions using PL_raise_exception(). The use of PL_throw() is not safe. If a synchronous handler raises an exception, the exception is delayed to the next call to PL_handle_signals();
The user may call this function inside long-running foreign functions
to handle scheduled interrupts. This routine returns the number of
signals handled. If a handler raises an exception, the return value is
-1 and the calling routine should return with FALSE as soon
as possible.
TRUE if t1 and t2 refer to
physically the same compound term and FALSE otherwise.
In some applications it is useful to store and retreive Prolog terms from C-code. For example, the XPCE graphical environment does this for storing arbitrary Prolog data as slot-data of XPCE objects.
Please note that the returned handles have no meaning at the Prolog level and the recorded terms are not visible from Prolog. The functions PL_recorded() and PL_erase() are the only functions that can operate on the stored term.
Two groups of functions are provided.The first group (PL_record() and friends) store Prolog terms on the Prolog heap for retrieval during the same session. These functions are also used by recorda/3 and friends. The recorded database may be used to communicate Prolog terms between threads.
The second group (headed by PL_record_external()) provides the same functionality, but the returned data has properties that enable storing the data on an external device. It has been designed to make it possible to store Prolog terms fast an compact in an external database. Here are the main features:
It is obligatory to call either of the two closing functions to discard a foreign frame. Foreign frames may be nested.
int
count_atoms()
{ fid_t fid = PL_open_foreign_frame();
term_t goal = PL_new_term_ref();
term_t a1 = PL_new_term_ref();
term_t a2 = PL_new_term_ref();
functor_t s2 = PL_new_functor(PL_new_atom("statistics"), 2);
int atoms;
PL_put_atom_chars(a1, "atoms");
PL_cons_functor(goal, s2, a1, a2);
PL_call(goal, NULL); /* call it in current module */
PL_get_integer(a2, &atoms);
PL_discard_foreign_frame(fid);
return atoms;
}
|
| Figure 8 : Calling Prolog |
Modules are identified via a unique handle. The following functions are available to query and manipulate modules.
NULL it will be set to the
context module. Otherwise it will be left untouched. The following
example shows how to obtain the plain term and module if the default
module is the user module:
{ module m = PL_new_module(PL_new_atom("user"));
term_t plain = PL_new_term_ref();
PL_strip_module(term, &m, plain);
...
|
This section discusses PL_exception(), PL_throw()
and
PL_raise_exception(),
the interface functions to detect and generate Prolog exceptions from
C-code. PL_throw() and PL_raise_exception()
from the C-interface to raise an exception from foreign code. PL_throw()
exploits the C-function longjmp() to return immediately to the innermost
PL_next_solution(). PL_raise_exception()
registers the exception term and returns FALSE. If a
foreign predicate returns FALSE, while and exception-term is registered
a Prolog exception will be raised by the virtual machine.
Calling these functions outside the context of a function implementing a foreign predicate results in undefined behaviour.
PL_exception() may be used after a call to PL_next_solution() fails, and returns a term reference to an exception term if an exception was raised, and 0 otherwise.
If a C-function, implementing a predicate calls Prolog and detects an exception using PL_exception(), it can handle this exception, or return with the exception. Some caution is required though. It is not allowed to call PL_close_query() or PL_discard_foreign_frame() afterwards, as this will invalidate the exception term. Below is the code that calls a Prolog defined arithmetic function (see arithmethic_function/1).
If PL_next_solution() succeeds, the result is analysed and translated to a number, after which the query is closed and all Prolog data created after PL_open_foreign_frame() is destroyed. On the other hand, if PL_next_solution() fails and if an exception was raised, just pass it. Otherwise generate an exception (PL_error() is an internal call for building the standard error terms and calling PL_raise_exception()). After this, the Prolog environment should be discarded using PL_cut_query() and PL_close_foreign_frame() to avoid invalidating the exception term.
static int
prologFunction(ArithFunction f, term_t av, Number r)
{ int arity = f->proc->definition->functor->arity;
fid_t fid = PL_open_foreign_frame();
qid_t qid;
int rval;
qid = PL_open_query(NULL, PL_Q_NORMAL, f->proc, av);
if ( PL_next_solution(qid) )
{ rval = valueExpression(av+arity-1, r);
PL_close_query(qid);
PL_discard_foreign_frame(fid);
} else
{ term_t except;
if ( (except = PL_exception(qid)) )
{ rval = PL_throw(except); /* pass exception */
} else
{ char *name = stringAtom(f->proc->definition->functor->name);
/* generate exception */
rval = PL_error(name, arity-1, NULL, ERR_FAILED, f->proc);
}
PL_cut_query(qid); /* donot destroy data */
PL_close_foreign_frame(fid); /* same */
}
return rval;
}
|
FALSE. Below is an example returning an
exception from foreign predicate:
foreign_t
pl_hello(term_t to)
{ char *s;
if ( PL_get_atom_chars(to, &s) )
{ Sprintf("Hello \"%s\"\n", s);
PL_succeed;
} else
{ term_t except = PL_new_term_ref();
PL_unify_term(except,
PL_FUNCTOR_CHARS, "type_error", 2,
PL_CHARS, "atom",
PL_TERM, to);
return PL_raise_exception(except);
}
}
|
SWI-Prolog offers both a C and Prolog interface to deal with software interrupts (signals). The Prolog mapping is defined in section 4.10. This subsection deals with handling signals from C.
If a signal is not used by Prolog and the handler does not call Prolog in any way, the native signal interface routines may be used.
Some versions of SWI-Prolog, notably running on popular Unix
platforms, handle SIG_SEGV for guarding the Prolog stacks.
If the application whishes to handle this signal too, it should use PL_signal()
to install its handler after initialisating Prolog. SWI-Prolog will pass
SIG_SEGV to the user code if it detected the signal is not
related to a Prolog stack overflow.
Any handler that wishes to call one of the Prolog interface functions should call PL_signal() for its installation.
After a signal handler is registered using this function, the native signal interface redirects the signal to a generic signal handler inside SWI-Prolog. This generic handler validates the environment, creates a suitable environment for calling the interface functions described in this chapter and finally calls the registered user-handler.
By default, signals are handled asynchronously (i.e. at the time they
arrive). It is inheritly dangerous to call extensive code fragments, and
especially exception related code from asynchronous handlers. The
interface allows for synchronous handling of signals. In this
case the native OS handler just schedules the signal using PL_raise(),
which is checked by PL_handle_signals()
at the call- and redo-port. This behaviour is realised by or-ing sig
with the constant
PL_SIGSYNC. (63)
Signal handling routines may raise exceptions using PL_raise_exception(). The use of PL_throw() is not safe. If a synchronous handler raises an exception, the exception is delayed to the next call to PL_handle_signals();
The user may call this function inside long-running foreign functions
to handle scheduled interrupts. This routine returns the number of
signals handled. If a handler raises an exception, the return value is
-1 and the calling routine should return with FALSE as soon
as possible.
TRUE if t1 and t2 refer to
physically the same compound term and FALSE otherwise.
In some applications it is useful to store and retreive Prolog terms from C-code. For example, the XPCE graphical environment does this for storing arbitrary Prolog data as slot-data of XPCE objects.
Please note that the returned handles have no meaning at the Prolog level and the recorded terms are not visible from Prolog. The functions PL_recorded() and PL_erase() are the only functions that can operate on the stored term.
Two groups of functions are provided.The first group (PL_record() and friends) store Prolog terms on the Prolog heap for retrieval during the same session. These functions are also used by recorda/3 and friends. The recorded database may be used to communicate Prolog terms between threads.
The second group (headed by PL_record_external()) provides the same functionality, but the returned data has properties that enable storing the data on an external device. It has been designed to make it possible to store Prolog terms fast an compact in an external database. Here are the main features:
It is obligatory to call either of the two closing functions to discard a foreign frame. Foreign frames may be nested.
int
count_atoms()
{ fid_t fid = PL_open_foreign_frame();
term_t goal = PL_new_term_ref();
term_t a1 = PL_new_term_ref();
term_t a2 = PL_new_term_ref();
functor_t s2 = PL_new_functor(PL_new_atom("statistics"), 2);
int atoms;
PL_put_atom_chars(a1, "atoms");
PL_cons_functor(goal, s2, a1, a2);
PL_call(goal, NULL); /* call it in current module */
PL_get_integer(a2, &atoms);
PL_discard_foreign_frame(fid);
return atoms;
}
|
| Figure 8 : Calling Prolog |
Modules are identified via a unique handle. The following functions are available to query and manipulate modules.
NULL it will be set to the
context module. Otherwise it will be left untouched. The following
example shows how to obtain the plain term and module if the default
module is the user module:
{ module m = PL_new_module(PL_new_atom("user"));
term_t plain = PL_new_term_ref();
PL_strip_module(term, &m, plain);
...
|
This section discusses PL_exception(), PL_throw()
and
PL_raise_exception(),
the interface functions to detect and generate Prolog exceptions from
C-code. PL_throw() and PL_raise_exception()
from the C-interface to raise an exception from foreign code. PL_throw()
exploits the C-function longjmp() to return immediately to the innermost
PL_next_solution(). PL_raise_exception()
registers the exception term and returns FALSE. If a
foreign predicate returns FALSE, while and exception-term is registered
a Prolog exception will be raised by the virtual machine.
Calling these functions outside the context of a function implementing a foreign predicate results in undefined behaviour.
PL_exception() may be used after a call to PL_next_solution() fails, and returns a term reference to an exception term if an exception was raised, and 0 otherwise.
If a C-function, implementing a predicate calls Prolog and detects an exception using PL_exception(), it can handle this exception, or return with the exception. Some caution is required though. It is not allowed to call PL_close_query() or PL_discard_foreign_frame() afterwards, as this will invalidate the exception term. Below is the code that calls a Prolog defined arithmetic function (see arithmethic_function/1).
If PL_next_solution() succeeds, the result is analysed and translated to a number, after which the query is closed and all Prolog data created after PL_open_foreign_frame() is destroyed. On the other hand, if PL_next_solution() fails and if an exception was raised, just pass it. Otherwise generate an exception (PL_error() is an internal call for building the standard error terms and calling PL_raise_exception()). After this, the Prolog environment should be discarded using PL_cut_query() and PL_close_foreign_frame() to avoid invalidating the exception term.
static int
prologFunction(ArithFunction f, term_t av, Number r)
{ int arity = f->proc->definition->functor->arity;
fid_t fid = PL_open_foreign_frame();
qid_t qid;
int rval;
qid = PL_open_query(NULL, PL_Q_NORMAL, f->proc, av);
if ( PL_next_solution(qid) )
{ rval = valueExpression(av+arity-1, r);
PL_close_query(qid);
PL_discard_foreign_frame(fid);
} else
{ term_t except;
if ( (except = PL_exception(qid)) )
{ rval = PL_throw(except); /* pass exception */
} else
{ char *name = stringAtom(f->proc->definition->functor->name);
/* generate exception */
rval = PL_error(name, arity-1, NULL, ERR_FAILED, f->proc);
}
PL_cut_query(qid); /* donot destroy data */
PL_close_foreign_frame(fid); /* same */
}
return rval;
}
|
FALSE. Below is an example returning an
exception from foreign predicate:
foreign_t
pl_hello(term_t to)
{ char *s;
if ( PL_get_atom_chars(to, &s) )
{ Sprintf("Hello \"%s\"\n", s);
PL_succeed;
} else
{ term_t except = PL_new_term_ref();
PL_unify_term(except,
PL_FUNCTOR_CHARS, "type_error", 2,
PL_CHARS, "atom",
PL_TERM, to);
return PL_raise_exception(except);
}
}
|
SWI-Prolog offers both a C and Prolog interface to deal with software interrupts (signals). The Prolog mapping is defined in section 4.10. This subsection deals with handling signals from C.
If a signal is not used by Prolog and the handler does not call Prolog in any way, the native signal interface routines may be used.
Some versions of SWI-Prolog, notably running on popular Unix
platforms, handle SIG_SEGV for guarding the Prolog stacks.
If the application whishes to handle this signal too, it should use PL_signal()
to install its handler after initialisating Prolog. SWI-Prolog will pass
SIG_SEGV to the user code if it detected the signal is not
related to a Prolog stack overflow.
Any handler that wishes to call one of the Prolog interface functions should call PL_signal() for its installation.
After a signal handler is registered using this function, the native signal interface redirects the signal to a generic signal handler inside SWI-Prolog. This generic handler validates the environment, creates a suitable environment for calling the interface functions described in this chapter and finally calls the registered user-handler.
By default, signals are handled asynchronously (i.e. at the time they
arrive). It is inheritly dangerous to call extensive code fragments, and
especially exception related code from asynchronous handlers. The
interface allows for synchronous handling of signals. In this
case the native OS handler just schedules the signal using PL_raise(),
which is checked by PL_handle_signals()
at the call- and redo-port. This behaviour is realised by or-ing sig
with the constant
PL_SIGSYNC. (63)
Signal handling routines may raise exceptions using PL_raise_exception(). The use of PL_throw() is not safe. If a synchronous handler raises an exception, the exception is delayed to the next call to PL_handle_signals();
The user may call this function inside long-running foreign functions
to handle scheduled interrupts. This routine returns the number of
signals handled. If a handler raises an exception, the return value is
-1 and the calling routine should return with FALSE as soon
as possible.
TRUE if t1 and t2 refer to
physically the same compound term and FALSE otherwise.
In some applications it is useful to store and retreive Prolog terms from C-code. For example, the XPCE graphical environment does this for storing arbitrary Prolog data as slot-data of XPCE objects.
Please note that the returned handles have no meaning at the Prolog level and the recorded terms are not visible from Prolog. The functions PL_recorded() and PL_erase() are the only functions that can operate on the stored term.
Two groups of functions are provided.The first group (PL_record() and friends) store Prolog terms on the Prolog heap for retrieval during the same session. These functions are also used by recorda/3 and friends. The recorded database may be used to communicate Prolog terms between threads.
The second group (headed by PL_record_external()) provides the same functionality, but the returned data has properties that enable storing the data on an external device. It has been designed to make it possible to store Prolog terms fast an compact in an external database. Here are the main features:
It is obligatory to call either of the two closing functions to discard a foreign frame. Foreign frames may be nested.
int
count_atoms()
{ fid_t fid = PL_open_foreign_frame();
term_t goal = PL_new_term_ref();
term_t a1 = PL_new_term_ref();
term_t a2 = PL_new_term_ref();
functor_t s2 = PL_new_functor(PL_new_atom("statistics"), 2);
int atoms;
PL_put_atom_chars(a1, "atoms");
PL_cons_functor(goal, s2, a1, a2);
PL_call(goal, NULL); /* call it in current module */
PL_get_integer(a2, &atoms);
PL_discard_foreign_frame(fid);
return atoms;
}
|
| Figure 8 : Calling Prolog |
Modules are identified via a unique handle. The following functions are available to query and manipulate modules.