12.4.12.2 Initiating a query from C
This section discusses the functions for creating and manipulating queries from C. Note that a foreign context can have at most one active query. This implies that it is allowed to make strictly nested calls between C and Prolog (Prolog calls C, calls Prolog, calls C, etc.), but it is not allowed to open multiple queries and start generating solutions for each of them by calling PL_next_solution(). Be sure to call PL_cut_query() or PL_close_query() on any query you opened before opening the next or returning control back to Prolog. Failure to do so results in "undefined behavior" (typically, a crash).
- qid_t PL_open_query(module_t ctx, int flags, predicate_t p, term_t +t0)
-
Opens a query and returns an identifier for it. ctx is the context module of the goal. When
NULL
, the context module of the calling context will be used, oruser
if there is no calling context (as may happen in embedded systems). Note that the context module only matters for meta-predicates. See meta_predicate/1, context_module/1 and module_transparent/1. The term reference t0 is the first of a vector of term references as returned by PL_new_term_refs(n). Raise a resource exception and returns(qid_t)0
on failure.Every use of PL_open_query() must have a corresponding call to PL_cut_query() or PL_close_query() before the foreign predicate returns either
TRUE
orFALSE
.The flags arguments provides some additional options concerning debugging and exception handling. It is a bitwise or of the following values below. Note that exception propagation is defined by the flags
PL_Q_NORMAL
,PL_Q_CATCH_EXCEPTION
andPL_Q_PASS_EXCEPTION
. Exactly one of these flags must be specified (if none of them is specified, the behavior is as ifPL_Q_NODEBUG
is specified)..PL_Q_NORMAL
- Normal operation. It is named "normal" because it makes a call to Prolog behave as it did before exceptions were implemented, i.e., an error (now uncaught exception) triggers the debugger. See also the Prolog flag debug_on_error. This mode is still useful when calling Prolog from C if the C code is not willing to handle exceptions.
PL_Q_NODEBUG
- Switch off the debugger while executing the goal. This option is used by
many calls to hook-predicates to avoid tracing the hooks. An example is print/1
calling portray/1
from foreign code. This is the default if flags is
0
. PL_Q_CATCH_EXCEPTION
- If an exception is raised while executing the goal, make it available by
calling
PL_exception(qid)
, whereqid
is theqid_t
returned by PL_open_query(). The exception is implicitly cleared from the environment when the query is closed and the exception term returned fromPL_exception(qid)
becomes invalid. UsePL_Q_PASS_EXCEPTION
if you wish to propagate the exception. PL_Q_PASS_EXCEPTION
- As
PL_Q_CATCH_EXCEPTION
, making the exception on the inner environment available usingPL_exception(0)
in the parent environment. If PL_next_solution() returnsFALSE
, you must call PL_cut_query() or PL_close_query(). After that you may verify whether failure was due to logical failure of the called predicate or an exception by callingPL_exception(0)
. If the predicate failed due to an exception you should return withFALSE
from the foreign predicate or call PL_clear_exception() to clear it. If you wish to process the exception in C, it is advised to usePL_Q_CATCH_EXCEPTION
instead, but only if you have no need to raise an exception or re-raise the caught exception.Note that
PL_Q_PASS_EXCEPTION
is used by the debugger to decide whether the exception is caught. If there is no matching catch/3 call in the current query and the query was started usingPL_Q_PASS_EXCEPTION
the debugger searches the parent queries until it either finds a matching catch/3, a query withPL_Q_CATCH_EXCEPTION
(in which case it considers the exception handled by C) or the top of the query stack (in which case it considers the exception uncaught). Uncaught exceptions use thelibrary(library(prolog_stack))
to add a backtrace to the exception and start the debugger as soon as possible if the Prolog flag debug_on_error istrue
. PL_Q_ALLOW_YIELD
- Support the
I_YIELD
instruction for engine-based coroutining. See $engine_yield/2 inboot/init.pl
for details. PL_Q_EXT_STATUS
- Make PL_next_solution()
return extended status. Instead of only
TRUE
orFALSE
extended status as illustrated in the following table:Extended Normal PL_S_EXCEPTION FALSE Exception available through PL_exception() PL_S_FALSE FALSE Query failed PL_S_TRUE TRUE Query succeeded with choicepoint PL_S_LAST TRUE Query succeeded without choicepoint
PL_open_query() can return the query identifier
0
if there is not enough space on the environment stack (and makes the exception available throughPL_exception(0)
). This function succeeds, even if the referenced predicate is not defined. In this case, running the query using PL_next_solution() may return an existence_error. See PL_exception().The example below opens a query to the predicate is_a/2 to find the ancestor of‘me’. The reference to the predicate is valid for the duration of the process or until PL_cleanup() is called (see PL_predicate() for details) and may be cached by the client.
char * ancestor(const char *me) { term_t a0 = PL_new_term_refs(2); static predicate_t p; if ( !p ) p = PL_predicate("is_a", 2, "database"); PL_put_atom_chars(a0, me); PL_open_query(NULL, PL_Q_PASS_EXCEPTION, p, a0); ... }
- int PL_next_solution(qid_t qid)
- Generate the first (next) solution for the given query. The return value
is
TRUE
if a solution was found, orFALSE
to indicate the query could not be proven. This function may be called repeatedly until it fails to generate all solutions to the query. The return valuePL_S_NOT_INNER
is returned if qid is not the innermost query.If the PL_open_query() had the flag
PL_Q_EXT_STATUS
, there are additional return values (see section 12.4.1.2). - int PL_cut_query(qid_t qid)
- Discards the query, but does not delete any of the data created by the
query. It just invalidates qid, allowing for a new call to
PL_open_query()
in this context. PL_cut_query()
may invoke cleanup handlers (see setup_call_cleanup/3)
and therefore may experience exceptions. If an exception occurs the
return value is
FALSE
and the exception is accessible throughPL_exception(0)
.An example of a handler that can trigger an exception in PL_cut_query() is:
test_setup_call_cleanup(X) :- setup_call_cleanup( true, between(1, 5, X), throw(error)).
where PL_next_solution() returns
TRUE
on the first result and thethrow(error)
will only run when PL_cut_query() or PL_close_query() is run. On the other hand, if the goal in setup_call_cleanup/3 has completed (failure, exception, determinitic success), the cleanup handler will have done its work before control gets back to Prolog and therefore PL_next_solution() will have generated the exception. The return valuePL_S_NOT_INNER
is returned if qid is not the innermost query. - int PL_close_query(qid_t qid)
- As PL_cut_query(),
but all data and bindings created by the query are destroyed as if the
query is called as
\+ \+ Goal
. This reduces the need for garbage collection, but also rewinds side effects such as setting global variables using b_setval/2. The return valuePL_S_NOT_INNER
is returned if qid is not the innermost query. - qid_t PL_current_query(void)
- Returns the query id of the current query or
0
if the current thread is not executing any queries. - PL_engine_t PL_query_engine(qid_t qid)
- Return the engine to which qid belongs. Note that interacting with a query or the Prolog terms associated with a query requires the engine to be current. See PL_set_engine().
- int PL_call_predicate(module_t m, int flags, predicate_t pred, term_t +t0)
- Shorthand for PL_open_query(), PL_next_solution(), PL_cut_query(), generating a single solution. The arguments are the same as for PL_open_query(), the return value is the same as PL_next_solution().
- int PL_call(term_t t, module_t m)
- Call term t just like the Prolog predicate once/1. t
is called in the module m, or in the context module if m
== NULL. Returns
TRUE
if the call succeeds,FALSE
otherwise. If the goal raises an exception the return value isFALSE
and the exception term is available using PL_exception(0).228Up to version 9.1.11 the debugger was started and the exception was not propagated. Figure 7 shows an example to obtain the number of defined atoms.