Did you know ... | Search Documentation: |
The PREDICATE and PREDICATE_NONDET macros |
The PREDICATE macro is there to make your code look nice, taking care of the interface to the C-defined SWI-Prolog kernel as well as mapping exceptions. Using the macro
PREDICATE(hello, 1)
is the same as writing:30There
are a few more details, such as catching std::bad_alloc
.:
static foreign_t pl_hello__1(PlTermv PL_av); static foreign_t _pl_hello__1(term_t t0, int arity, control_t ctx) { (void)arity; (void)ctx; try { return pl_hello__1(PlTermv(1, t0)); } catch( PlFail& ) { return false; } catch ( PlException& ex ) { return ex.plThrow(); } } static PlRegister _x_hello__1("hello", 1, _pl_hello__1); static foreign_t pl_hello__1(PlTermv PL_av)
The first function converts the parameters passed from the Prolog
kernel to a PlTermv
instance and maps exceptions raised in
the body to simple failure or Prolog exceptions. The PlRegister
global constructor registers the predicate. Finally, the function header
for the implementation is created.
The PREDICATE() macros have a number of variations that deal with special cases.
A1
, A2
,
etc.PL_av
is not used.NAMED_PREDICATE("#", hash, 2) { return A2.unify_string(A1.as_string()); }
Non-deterministic predicates are defined using PREDICATE_NONDET(plname, cname, arity) or NAMED_PREDICATE_NONDET(plname, cname, arity).
A non-deterministic predicate returns a “context” , which
is passed to a subsequent retry. Typically, this context is allocated on
the first call to the predicate and freed when the predicate either
fails or does its last successful return (the context is nullptr
on the first call). To simplify this, a template helper function
PlControl::context_unique_ptr<ContextType>() provides a “smart
pointer” that frees the context on normal return or an exception;
when used with PL_retry_address(), the context's std:unique_ptr<ContextType>::release()
is used to pass the context to Prolog for the next retry, and to prevent
the context from being freed. If the predicate is called with PL_PRUNE
,
the normal return true
will implicitly free the context.
The skeleton for a typical non-deterministic predicate is as follows.
The test for PL_PRUNED
is done first to avoid an unneeded
PlFrame
and also to ensure that A1, A2,
etc. aren't used when they have the value
PlTerm::null
.31This
code could be structured as a switch
statement, but
typically the PL_FIRST_CALL
case falls through to the PL_REDO
case. There are a number of examples of non-deterministic
predicates in the test code test_cpp.cpp
.
struct PredContext { ... }; // The "context" for retries PREDICATE_NONDET(pred, <arity>) { // "ctxt" must be acquired so that the destructor deletes it auto ctxt = handle.context_unique_ptr<PredContext>(); const auto control = handle.foreign_control(); if ( control == PL_PRUNED ) return true; // Can use A1, A2, etc. after we know control != PL_PRUNED if ( ... ) // deterministic result { assert(control == PL_FIRST_CALL); if ( ... ) return true; // Success (and no more solutions) else return fase; } if ( control = PL_FIRST_CALL ) { ctxt.reset(new PredContext(...)); ... } else { assert(control == PL_REDO); } PlFrame fr; for ( ; ctxt->valid(...) ; ctxt->next() ) { if ( ... unify a result ... ) { ctxt->next(); if ( ctxt->valid(...) ) PL_retry_addresss(ctxt.release()); // Succeed with a choice point else return true; // deterministic success } fr.rewind(); } return false; }
With no special precautions, the predicates are defined into the
module from which load_foreign_library/1
was called, or in the module
user
if there is no Prolog context from which to deduce the
module such as while linking the extension statically with the Prolog
kernel.
Alternatively, before loading the SWI-Prolog include file, the macro PROLOG_MODULE may be defined to a string containing the name of the destination module. A module name may only contain alpha-numerical characters (letters, digits, _). See the example below:
#define PROLOG_MODULE "math" #include <SWI-Prolog.h> #include <math.h> PREDICATE(pi, 1) { A1 = M_PI; }
?- math:pi(X). X = 3.14159