- Documentation
- Reference manual
- Foreign Language Interface
- The Foreign Include File
- Argument Passing and Control
- Atoms and functors
- Analysing Terms via the Foreign Interface
- Constructing Terms
- Unifying data
- Convenient functions to generate Prolog exceptions
- 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.14 Prolog exceptions in foreign code
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 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 an 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 arithmetic_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); /* do not destroy data */ PL_close_foreign_frame(fid); /* same */ } return rval; }
- int PL_raise_exception(term_t exception)
- Generate an exception (as throw/1)
and return
FALSE
. Below is an example returning an exception from a 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); } }
- int PL_throw(term_t exception)
- Similar to PL_raise_exception(), but returns using the C longjmp() function to the innermost PL_next_solution().
- term_t PL_exception(qid_t qid)
- If PL_next_solution()
fails, this can be due to normal failure of the Prolog call, or because
an exception was raised using throw/1.
This function returns a handle to the exception term if an exception was
raised, or 0 if the Prolog goal simply failed. If there is an exception,
PL_exception()
allocates a term-handle using PL_new_term_ref()
that is used to return the exception term.
Additionally,
PL_exception(0)
returns the pending exception in the current query or 0 if no exception is pending. This can be used to check the error status after a failing call to, e.g., one of the unification functions. - 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.197This 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.