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); /* donot destroy data */
PL_close_foreign_frame(fid); /* same */
}
return rval;
}
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);
}
}
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.