- Documentation
- Reference manual
- Packages
- A C++ interface to SWI-Prolog
- A C++ interface to SWI-Prolog (Version 2)
- Summary of changes between Versions 1 and 2
- Introduction (version 2)
- The life of a PREDICATE (version 2)
- Overview (version 2)
- Examples (version 2)
- Rational for changes from version 1 (version 2)
- Porting from version 1 to version 2
- The class PlFail (version 2)
- The class PlTerm (version 2)
- The class PlTermv (version 2)
- The class PlAtom - Supporting Prolog constants (version 2)
- Unification and foreign frames (version 2)
- The class PlRegister (version 2)
- The class PlQuery (version 2)
- The PREDICATE and PREDICATE_NONDET macros (version 2)
- Exceptions (version 2)
- Embedded applications (version 2)
- Considerations (version 2)
- Conclusions (version 2)
- A C++ interface to SWI-Prolog (Version 2)
- A C++ interface to SWI-Prolog
2.15 The PREDICATE and PREDICATE_NONDET macros (version 2)
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:14There
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.
2.15.1 Variations of the PREDICATE macro (version 2)
The PREDICATE() macros have a number of variations that deal with special cases.
- PREDICATE0(name)
- This is the same as PREDICATE(name, 0). It avoids a compiler warning
about that
PL_av
is not used. - NAMED_PREDICATE(plname, cname, arity)
- This version can be used to create predicates whose name is not a valid
C++ identifier. Here is a ---hypothetical--- example, which unifies the
second argument with a stringified version of the first. The‘cname'
is used to create a name for the functions. The concrete name does not
matter, but must be unique. Typically it is a descriptive name using the
limitations imposed by C++ indentifiers.
NAMED_PREDICATE("#", hash, 2) { A2 = (wchar_t*)A1; }
- PREDICATE_NONDET(name, arity)
- Define a non-deterministic Prolog predicate in C++. See also section 2.15.2.
- NAMED_PREDICATE_NONDET(plname, cname, arity)
- Define a non-deterministic Prolog predicate in C++, whose name is not a
valid C++ identifier. See also section
2.15.2.
2.15.2 Non-deterministic predicates (version 2)
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 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. To simplify this, a template helper class
PlForeignContextPtr<ContextType>
provides a
"smart pointer" that frees the context on normal return or an exception;
if PlForeignContextPtr<ContextType>::keep() is called, the
pointer isn't freed on return or exception.
The skeleton for a typical non-deterministic predicate is:
struct PredContext { ... }; // The "context" for retries PREDICATE_NONDET(pred, <arity>) { PlForeignContextPtr<PredContext> ctxt(handle); switch( PL_foreign_control(handle) ) { case PL_FIRST_CALL: ctxt.set(new PredContext(...)); ... break; case PL_REDO: break; case PL_PRUNED: return true; } if ( ... ) return false; // Failure (and no more solutions) // or throw PlFail(); if ( ... ) return true; // Success (and no more solutions) ... ctxt.keep(); PL_retry_address(ctxt.get()); // Succeed with a choice point }
2.15.3 Controlling the Prolog destination module (version 2)
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