Did you know ... | Search Documentation: |
Prolog exceptions in foreign code |
This section discusses PL_exception()
and PL_raise_exception(),
the interface functions to detect and generate Prolog exceptions from C
code. PL_raise_exception()
from the C interface registers the exception term and returns FALSE
.
If a foreign predicate returns
FALSE
, while an exception term is registered, a Prolog
exception will be raised by the virtual machine. This implies for a
foreign function that implements a predicate and wishes to raise an
exception, the function must call PL_raise_exception(),
perform any necessary cleanup, and return the return code of PL_raise_exception()
or explicitly
FALSE
. Calling PL_raise_exception()
outside the context of a function implementing a foreign predicate
results in undefined behaviour.
Note that many of the C API functions may call PL_raise_exception()
and return FALSE
. The user must test for this, cleanup, and
make the foreign function return FALSE
.
PL_exception()
may be used to inspect the currently registered exception. It is
normally called after a call to PL_next_solution()
returns FALSE
, and returns a term reference to an exception
term if an exception is pending, and (term_t)0
otherwise.
It may also be called after, e.g., PL_unify()
to distinguish a normal failing unification from a unification that
raised an resource error exception.
PL_exception()
must only be called after a function such as
PL_next_solution()
or PL_unify()
returns failure; if called elsewhere, the return value is undefined.
If a C function implementing a predicate that calls Prolog should use
PL_open_query()
with the flag PL_Q_PASS_EXCEPTION
and make the function
return FALSE if PL_next_solution()
returns FALSE
and
PL_exception()
indicates an exception is pending.
Both for C functions implementing a predicate and when Prolog is
called while the main control of the process is in C, user code should
always check for exceptions. As explained above, C functions
implementing a predicate should normally cleanup and return with FALSE
.
If the C function whishes to continue it may call PL_clear_exception().
Note that this may cause any exception to be ignored, including time
outs and abort. Typically the user should check the
exception details before ignoring an exception (using PL_exception(0)
or
PL_exception(qid)
as appropriate). If the C code does not implement a predicate it
normally prints the exception and calls
PL_clear_exception()
to discard it. Exceptions may be printed by calling
print_message/2
through the C interface.
FALSE
. If there is already a pending exception,
the most urgent exception is kept; and if both are of the same urgency,
the new exception is kept. Urgency of exceptions is described in secrefurgentexceptions.
This function is rarely used directly. Instead, errors are typically
raised using the functions in section
12.4.7 or the C api functions that end in _ex
such as PL_get_atom_ex().
Below we give an example returning an exception from a foreign predicate
the verbose way. Note that the exception is raised in a sequence of
actions connected using &&
. This ensures that a
proper exception is raised should any of the calls used to build or
raise the exception themselves raise an exception. In this simple case PL_new_term_ref()
is guaranteed to succeed because the system guarantees at least 10
available term references before entering the foreign predicate. PL_unify_term()
however may raise a resource exception for the global stack.
foreign_t pl_hello(term_t to) { char *s; if ( PL_get_atom_chars(to, &s) ) { return Sfprintf(Scurrent_output, "Hello \"%s\"\n", s); } else { term_t except; return ( (except=PL_new_term_ref()) && PL_unify_term(except, PL_FUNCTOR_CHARS, "type_error", 2, PL_CHARS, "atom", PL_TERM, to) && PL_raise_exception(except) ); } }
For reference, the preferred implementation of the above is below.
The
CVT_EXCEPTION
tells the system to generate an exception if
the conversion fails. The other CVT_
flags define the
admissible types and REP_MB
requests the string to be
provided in the current locale representation. This implies
that Unicode text is printed correctly if the current environment can
represent it. If not, a representation_error
is raised.
foreign_t pl_hello(term_t to) { char *s; if ( PL_get_chars(to, &s, CVT_ATOM|CVT_STRING|CVT_EXCEPTION|REP_MB) ) { return Sfprintf(Scurrent_output, "Hello \"%s\"\n", s); } return FALSE; }
resource_error
exceptions. Some return type_error
or domain_error
exceptions. A call to Prolog using
PL_next_solution()
may return any exception, including those thrown by explicit calls to throw/1.
If no exception is pending this function returns (term_t)0
.
Normally qid should be 0
. An explicit qid
must be used after a call to PL_next_solution()
that returns FALSE
when the query was created using the PL_Q_PASS_EXCEPTION
flag (see PL_open_query()).
Note that an API may only raise an exception when it fails; if the
API call succeeds, the result of PL_exception(0)
will be 0.231Provided no exception
was pending before calling the API function. As clients must deal with
exceptions immediately after an API call raises one, this can not happen
in a well behaved client. The implementation of a foreign
predicate should normally cleanup and return
FALSE
after an exception is raised (and typically also
after an API call failed for logical reasons; see PL_unify()
for an elaboration on this topic). If the call to Prolog is not the
implementation of a foreign predicate, e.g., when the overall process
control is in some other language, exceptions may be printed by calling print_message/2
and should be discarded by calling PL_clear_exception().