• Places
    • Home
    • Graphs
    • Prefixes
  • Admin
    • Users
    • Settings
    • Plugins
    • Statistics
  • CPACK
    • Home
    • List packs
    • Submit pack
  • Repository
    • Load local file
    • Load from HTTP
    • Load from library
    • Remove triples
    • Clear repository
  • Query
    • YASGUI SPARQL Editor
    • Simple Form
    • SWISH Prolog shell
  • Help
    • Documentation
    • Tutorial
    • Roadmap
    • HTTP Services
  • Login

1 A C++ interface to SWI-Prolog
All Application Manual Name SummaryHelp

  • Documentation
    • Reference manual
    • Packages
      • A C++ interface to SWI-Prolog
        • A C++ interface to SWI-Prolog
          • Summary of changes between Versions 1 and 2
          • A simple example
          • Sample code
          • Introduction
          • The life of a PREDICATE
          • Overview
          • Examples
          • Rationale for changes from version 1
          • Porting from version 1 to version 2
          • The class PlFail
          • Overview of accessing and changing values
          • The class PlRegister
          • The class PlQuery
          • The PREDICATE and PREDICATE_NONDET macros
            • Variations of the PREDICATE macro
            • Non-deterministic predicates
            • Controlling the Prolog destination module
          • Exceptions
          • Embedded applications
          • Considerations
          • Conclusions

1.14 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.

1.14.1 Variations of the PREDICATE macro

The PREDICATE() macros have a number of variations that deal with special cases.

PREDICATE(name, arity)
Create a predicate with an automatically generated internal name, and register it with Prolog. The various term arguments are accessible as A1, A2, etc.
PREDICATE0(name)
This is the same as PREDICATE(name, 0). It avoids a compiler warning 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)
    { return A2.unify_string(A1.as_string());
    }
    
PREDICATE_NONDET(name, arity)
Define a non-deterministic Prolog predicate in C++. See also section 1.14.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 1.14.2.

1.14.2 Non-deterministic predicates

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;
}

1.14.3 Controlling the Prolog destination module

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

ClioPatria (version V3.1.1-51-ga0b30a5)