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 shall 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 shall 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 defined as
'$aborted').time_limit_exceeded (see call_with_time_limit/2).resource_error exceptions.error(Formal, ImplDef) exceptions.
This function is rarely used directly. Instead, errors are typically
raised using the functions in section
12.4.6 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) )
{ Sprintf("Hello \"%s\"\n", s);
return TRUE;
} 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) )
{ Sprintf("Hello \"%s\"\n", s);
return TRUE;
}
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.225Provided 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().