- 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
- Sample code (version 2)
- Introduction (version 2)
- The life of a PREDICATE (version 2)
- Overview (version 2)
- Examples (version 2)
- Rationale for changes from version 1 (version 2)
- Porting from version 1 to version 2
- The class PlFail (version 2)
- Overview of accessing and changing values (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.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.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 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.cpp
has been added, containing the implementation of some functions. This is included by default fromSWI-cpp2.h
or 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” .3 “Pl” is used throughout theSWI-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.5.4. Many of these wrapper functions are also methods in thePlAtom
andPlTerm
classes, with the arguments changed fromatom_t
andterm_t
toPlAtom
andPlTerm
and in some caseschar*
andwchar_t*
changed tostd::string
andstd::wstring
. These wrappers are available if you includeSWI-cpp2.h
(they are in a separateSWI-cpp2-plx.h
file for ease of maintenance). - Instead of returning
false
from a foreign predicate to indicate failure, you can throw PlFail(). The convenience function PlCheckFail(rc) can be used to throw PlFail() iffalse
is returned from a function inSWI-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 aSWI-Prolog.h
function, a Prolog error will have already resulted in throwingPlException
; PlCheckFail(rc) is used to additionally throwPlFail
, similar to returningfalse
from the top-level of a foreign predicate - Prolog will check for an error and call throw/1 if appropriate. - The
PlException
class is now a subclass ofstd::exception
and 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 the
and similar operators do not allow for this.==
- Methods that return
char*
have been replaced by methods that returnstd::string
to ensure that lifetime issues don't cause subtle bugs.5If 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::string
and apply thec_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*
orwchar_t*
arguments also acceptstd::string
orstd::wstring
arguments. Where possible, encoding information can also be specified. - Type-checking methods have been added: PlTerm::type(), PlTerm::is_variable(), PlTerm::is_atom(), etc.
PlString
has been renamed toPlTerm_string
to 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
int
for true/false now return a C++bool
. - The wrapped C types fields (
term_t
,atom_t
, etc.) have been renamed fromhandle
,ref
, etc. toC_
.6This 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.
PlStringBuffers
provides 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 2.5.8.1.PlStream
provides a simpler interface for streams than PL_get_stream(), PL_acquire_stream(), and PL_release_stream(). See section 2.5.8.2.- Wrapper classes for
record_t
have been added. ThePlRecordExternalCopy
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 on the rationale and how to port from version 1 to version 1 are given in section 2.7 and section 2.8.