- 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
- Exceptions
- Embedded applications
- Considerations
- Conclusions
- A C++ interface to SWI-Prolog
- A C++ interface to SWI-Prolog
1.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, SWI-cpp2-plx.h, and SWI-cpp2-atommap.h.
The overall structure of the API has been retained - that is, it is a
thin layer of lightweight classes 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.1If
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 errors 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, an exception type PlFail has
been added, together with PlCheckFail, to allow more
compact code by “short circuit” return to Prolog on failure.
A convenience class for creating blobs has been added, so that an existing structure can be converted to a blob with only a few lines of code. More specifically:
SWI-cpp2.cpphas been added, containing the implementation of some functions. This is included by default fromSWI-cpp2.hor can be compiled separately.- The constructor PlTerm() is restricted to a few unambiguous cases - instead, you should use the appropriate subclass constructors that specify the type (PlTerm_var(), PlTerm_atom(), etc.).
- 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” .2 “Pl” is used throughout theSWI-cpp2.hinterface, 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 1.6.4. Many of these wrapper functions are also methods in thePlAtomandPlTermclasses, with the arguments changed fromatom_tandterm_ttoPlAtomandPlTermand in some caseschar*andwchar_t*changed tostd::stringandstd::wstring. These wrappers are available if you includeSWI-cpp2.h(they are in a separateSWI-cpp2-plx.hfile for ease of maintenance). - Instead of returning
falsefrom a foreign predicate to indicate failure, you can throw PlFail(). The convenience function PlCheckFail(rc) can be used to throw PlFail() iffalseis returned from a function inSWI-Prolog.h. If the wrapper functions or class methods are used, Prolog errors result in a C++PlExceptionexception.3If a “Plx_” wrapper is used to call aSWI-Prolog.hfunction, a Prolog error will have already resulted in throwingPlException; PlCheckFail(rc) is used to additionally throwPlFail, similar to returningfalsefrom the top-level of a foreign predicate - Prolog will check for an error and call throw/1 if appropriate. - The
PlExceptionclass is now a subclass ofstd::exceptionand encapsulates a Prolog error. Prolog errors are converted intothrow PlException(...). If the user code does not catch thePlException, 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 throw a C++ exception on error (this C++ exception is converted to a Prolog exception when control returns to Prolog).
- The “cast” operators (e.g.,
(char*)t,(int64_t)t,static_cast<char*>(t)) have been deprecated, replaced by “getters” (e.g.,t.as_string(),t.as_int64_t()). - The overloaded assignment operator for unification is deprecated, replaced by PlTerm::unify_term(), PlTerm::unify_atom(), etc., and the helper PlCheckFail().
- Many of the equality and inequality operators are deprecated;
replaced by the PlTerm::as_string()
or PlTerm::get_nchars()
methods and the associated
std::string, comparison operators. The PlTerm::as_string() method allows specifying the encoding to use whereas theand similar operators do not allow for this.== - Methods that return
char*have been replaced by methods that returnstd::stringto ensure that lifetime issues don't cause subtle bugs.4If you want to return achar*from a function, you should not doreturn t.as_string().c_str()because that will return a pointer to local or stack memory. Instead, you should change your interface to return astd::stringand apply thec_str()method to it. These lifetime errors can sometimes be caught by specifying the Gnu C++ or Clang options-Wreturn-stack-addressor-Wreturn-local-addr- as of 2023-04, Clang seems to do a better analysis. - Most constructors, methods, and functions that accept
char*orwchar_t*arguments also acceptstd::stringorstd::wstringarguments. Where possible, encoding information can also be specified. - Type-checking methods have been added: PlTerm::type(), PlTerm::is_variable(), PlTerm::is_atom(), etc.
PlStringhas been renamed toPlTerm_stringto make it clear that it's a term that contains a Prolog string.- More
PL_...(term_t, ...)methods have been added toPlTerm, andPL_...(atom_t, ...)methods have been added toPlAtom. Where appropriate, the arguments usePlTerm,PlAtom, etc. instead ofterm_t,atom_t, etc. - Most functions/methods that return an
intfor true/false now return a C++bool. - The wrapped C types fields (
term_t,atom_t, etc.) have been renamed fromhandle,ref, etc. toC_.5This is done by subclassing fromWrapped<term_t>,Wrapped<atom_t>, etc., which define the fieldC_, standard constructors, the methods is_null(), not_null(), reset(), reset(v), reset_wrapped(v), plus the constantnull. This value can be accessed by the unwrap() and unwrap_as_ptr() methods. There is also a “friend” function PlUnwrapAsPtr(). - A convenience function
PlControl::context_unique_ptr<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.
PlStringBuffersprovides a simpler interface for allocating strings on the stack than PL_STRINGS_MARK() and PL_STRINGS_RELEASE(). However, this is mostly not needed because most functions now usestd::string: see section 1.6.9.1.PlStreamprovides a simpler interface for streams than PL_get_stream(), PL_acquire_stream(), and PL_release_stream(). See section 1.6.9.2.- Wrapper classes for
record_thave been added. ThePlRecordExternalCopyclass contains the opaque handle, as a convenience. - Wrapper class for
control_thas been added and the PREDICATE_NONDET() has been modified to use it.
More details on the rationale and how to port from version 1 to version 1 are given in section 1.8 and section 1.9.