2 A C++ interface to SWI-Prolog (Version 2)
AllApplicationManualNameSummaryHelp

  • 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)
          • Classes for the recorded database: PlRecord and PlRecordExternalCopy
          • 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)

2.1 Summary of changes between Versions 1 and 2

Version 1 is in SWI-cpp.h; version 2 is in SWI-cpp2.h, SWI-cpp2.cpp, and SWI-cpp2-plx.h.

The overall structure of the API has been retained - that is, it is a thin layer on top of the interface provided by SWI-Prolog.h. Based on experience with the API, most of the conversion operators and some of the comparison operators have been removed or deprecated, and replaced by "getter" methods. The overloaded constructors have been replaced by subclasses for the various types. Some changes were also made to ensure that the [] operator for PlTerm and PlTermv doesn't cause unexpected implicit conversions. 2If there is an implicit conversion operator from PlTerm to term_t and also to char*, then the [] operator is ambiguous if f is overloaded to accept a term_t or char* in the code PlTerm t=...; f(t[0])

Prolog exceptions are now converted to C++ exceptions (which contain the exception term rather being a subclass of PlTerm as in version 1), where they can be caught and thrown using the usual C++ mechanisms; and the subclasses that create exceptions have been changed to functions. In addition, a PlFail has been added, to allow "short circuit" return to Prolog on failure.

More specifically:

  • SWI-cpp2.cpp has been added, containing the implementation of some functions that are too long to inline. The user must either #include <SWI-cpp2.cpp> or compile it separately and link it with the other foreign function code.
  • The constructor PlTerm() is restricted to a few unambiguous cases - instead, you should use the appropriate subclass' constructors (PlTerm_var(), PlTerm_atom(a), PlTerm_term_t(t), PlTerm_integer(i), PlTerm_int64(i), PlTerm_uint64(i), PlTerm_size_t(i), PlTerm_float(v), or PlTerm_pointer(p)).
  • Wrapper functions have been provided for almost all the PL_*() functions in SWI-Prolog.h, and have the same names with the “PL'' replaced by “Plx''.3 “Pl'' is used throughout the SWI-cpp2.h interface, and the “x'' is for “eXtended with eXception handling.'' Where appropriate, these check return codes and throw a C++ exception (created from the Prolog error). See section 2.4.4 Many of these wrapper functions have been added to the PlAtom and PlTerm classes, with the arguments changed from atom_t and term_t to PlAtom and PlTerm. These wrappers are available if you include SWI-cpp2.h (they are in a separate SWI-cpp2-plx.h file for ease of maintenance).
  • Instead of returning false from a foreign predicate to indicate failure, you can use throw PlFail(). The convenience function PlCheckFail(rc) can be used to throw PlFail() if false is returned from a function in SWI-Prolog.h. If the wrapper functions or class methods are used, Prolog errors result in a C++ PlException exception.4If a “Plx_'' wrapper is used to call a SWI-Prolog.h function, a Prolog error will have already resulted in throwing PlException;‘cfuncrefPlCheckFailrc is used to additionally throw PlFail, similar to returning false from the top-level of a foreign predicate.
  • The PlException class is a subclass of std::excxeption and encapsulates a Prolog error. Prolog errors are converted into throw PlException(...). If the user code does not catch the PlException, the PREDICATE() macro converts the error to a Prolog error upon return to the Prolog caller.
  • The C++ constructors, functions, and methods use the wrapper functions to a C++ exception on error (and the C++ exception is converted to a Prolog exception when control returns to Prolog).
  • The "cast" operators (e.g., (char*)t, (int64_t)t) have been deprecated, replaced by "getters" (e.g., t.as_string(), t.as_int64_t()).5The form (char*)t is a C-style cast; C++'s preferred form is more verbose: static_cast<char*>(t).
  • The overloaded assignment operator for unification is deprecated; replaced by unify_term(), unify_atom(), etc., and the helper PlCheckFail().
  • Many of the equality and inequality operators are deprecated; replaced by the as_string() method and the associated std::string, comparison operators. The as_string() method allows specifying the encoding to use whereas the == and similar operators do not allow for this.
  • Methods that return char* have been replaced by methods that return std::string to ensure that lifetime issues don't cause subtle bugs.6If you want to return a char* from a function, you should not do return t.as_string().c_str() because that will return a pointer to local or stack memory. Instead, you should change your interface to return a std::string and apply the c_str() method to it. These lifetime errors can sometimes be caught by specifying the Gnu C++ or Clang options -Wreturn-stack-address or -Wreturn-local-addr - as of 2023-04, Clang seems to do a better analysis.
  • Most constructors, methods, and functions that accept char* arguments also accept std::string or std::wstring arguments. Where possible, encoding information can also be specified.
  • Type-checking methods have been added: type(), is_variable(), is_atom(), etc.
  • PlString has been renamed to PlTerm_string to make it clear that it's a term that contains a Prolog string.
  • More PL_...(term_t, ...) methods have been added to PlTerm, and PL_...(atom_t, ...) methods have been added to PlAtom. Where appopriate, the arguments use PlTerm, PlAtom, etc. instead of term_t, atom_t, etc.
  • std::string and std::wstring are now supported in most places where char* or wchar_t* are allowed.
  • Most functions/methods that return an int for true/false now return a C++ bool.
  • The wrapped C types fields (term_t, atom_t, etc.) have been renamed from handle, ref, etc. to C_.7This is done by subclassing from Wrapped<term_t>, Wrapped<atom_t>, etc., which define the field C_, standard constructors, the methods is_null(), not_null(), reset(), and reset(v), plus the constant null.
  • A convenience class PlForeignContextPtr<ContextType> has been added, to simplify dynamic memory allocation in non-deterministic predicates.
  • A convenience function PlRewindOnFail() has been added, to simplify non-deterministic code that does backtracking by checking unification results.
  • PlStringBuffers provides a simpler interface for allocating strings on the stack than PL_STRINGS_MARK() and PL_STRINGS_RELEASE().
  • Wrapper classes for record_t have been added. The PlRecordExternalCopy class contains the opaque handle, as a convenience.
  • Wrapper class for control_t has been added and the PREDICATE_NONDET() has been modified to use it.

More details are given in section 2.6 and section 2.7.