- Documentation
- Reference manual
- Foreign Language Interface
- The Foreign Include File
- Argument Passing and Control
- Atoms and functors
- Input and output
- Analysing Terms via the Foreign Interface
- Constructing Terms
- Unifying data
- Convenient functions to generate Prolog exceptions
- Foreign language wrapper support functions
- Serializing and deserializing Prolog terms
- BLOBS: Using atoms to store arbitrary binary data
- Exchanging GMP numbers
- Calling Prolog from C
- Discarding Data
- String buffering
- Foreign Code and Modules
- Prolog exceptions in foreign code
- Catching Signals (Software Interrupts)
- Miscellaneous
- Errors and warnings
- Environment Control from Foreign Code
- Querying Prolog
- Registering Foreign Predicates
- Foreign Code Hooks
- Storing foreign data
- Embedding SWI-Prolog in other applications
- The Foreign Include File
- Foreign Language Interface
- Packages
- Reference manual
12.4.16 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.
- int PL_raise_exception(term_t exception)
- Generate an exception (as throw/1)
and return
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 defined as- abort (
'$aborted'
). time_limit_exceeded
(see call_with_time_limit/2).resource_error
exceptions.- Other
error(Formal, ImplDef)
exceptions. - Other exceptions.
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 otherCVT_
flags define the admissible types andREP_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, arepresentation_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; }
- abort (
- int PL_throw(term_t exception)
- Similar to PL_raise_exception(), but returns using the C longjmp() function to the innermost PL_next_solution(). This function is deprecated as it does not provide the opportunity to cleanup.
- term_t PL_exception(qid_t qid)
- Return the pending exception. Exceptions may be raised by most
of the API calls described in this chapter, a common possibility being
resource_error
exceptions. Some returntype_error
ordomain_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 returnsFALSE
when the query was created using thePL_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.230Provided 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 returnFALSE
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(). - void PL_clear_exception(void)
- Tells Prolog that the encountered exception must be ignored. This function must be called if control remains in C after a previous API call fails with an exception.231This feature is non-portable. Other Prolog systems (e.g., YAP) have no facilities to ignore raised exceptions, and the design of YAP's exception handling does not support such a facility. If there is no pending exception, PL_clear_exception() does nothing.