- 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.4 Overview (version 2)
The most useful area for exploiting C++ features is type-conversion.
Prolog variables are dynamically typed and all information is passed
around using the C-interface type term_t
. In C++, term_t
is embedded in the lightweight class PlTerm
.
Constructors and operator definitions provide flexible operations and
integration with important C-types (char *
, wchar_t*
,
long
and double
), plus the C++-types (std::string
,
std::wstring
).
2.4.1 Design philosophy of the classes
See also section 2.4.3.
The general philosophy for C++ classes is that a "half-created" object should not be possible - that is, the constructor should either succeed with a completely usable object or it should throw an exception. This API tries to follow that philosophy, but there are some important exceptions and caveats. (For more on how the C++ and Prolog exceptions interrelate, see section 2.16.)
The various classes (PlAtom
, PlTerm
, etc.)
are thin wrappers around the C interface's types (atom_t
,
term_t
, etc.). As such they inherit the concept of "null"
from these types (which is abstracted as PlAtom::null
,
PlTerm::null
, etc., which typically is equivalent to
0
). You can check whether the object is "fully created" by
using the verify() method - it will throw an exception if the
object is null
.
However, most of the classes have constructors that create a "complete" object. For example,
PlAtom foo("foo");
will ensure that the object foo
is useable and will
throw an exception if the atom can't be created.
To help avoid programming errors, most of the classes do not have a
default "empty" constructor. For example, if you with to create a
PlAtom
that is uninitialized, you must explicitly use
PlAtom(PlAtom::null)
. This make some code a bit more
cumbersome because you can't omit the default constructors in struct
initalizers.
Many of the classes wrap long-lived items, such as atoms, functors,
predicates, or modules. For these, it's often a good idea to define them
as static
variables that get created at load time, so that
a lookup for each use isn't needed (atoms are unique, so
PlAtom("foo")
requires a lookup for an atom foo
and creates one if it isn't found). Sometimes, it's desirable to create
them "lazily", such as:
static PlAtom foo(PlAtom::null}; ... if ( foo.is_null() ) foo = PlAtom("foo");
The class PlTerm
(which wraps term_t
) is
the most used. Although a PlTerm
object can be created from
a term_t
value, it is intended to be used with a
constructor that gives it an initial value. The default constructor
calls PL_new_term_ref() and throws an exception if this fails.
The various constructors are described in
section 2.9.1. Note that the
default constructor is not public; to create a "variable" term, you
should use the subclass constructor PlTerm_var().
2.4.2 Summary of classes
The list below summarises the classes defined in the C++ interface.
- PlTerm
- Generic Prolog term that wraps
term_t
(for more details onterm_t
, see Interface Data Types). This is a "base class" whose constructor is protected; subclasses specify the actual contents. Additional methods allow checking the Prolog type, unification, comparison, conversion to native C++-data types, etc. See section 2.9.3.The subclass constructors are as follows. If a constructor fails (e.g., out of memory), a
PlException
is thrown.- PlTerm_atom
- Subclass of
PlTerm
with constructors for building a term that contains an atom. - PlTerm_var
- Subclass of
PlTerm
with constructors for building a term that contains an uninstantiated variable. Typically this term is then unified with another object. - PlTerm_term_t
- Subclass of
PlTerm
with constructors for building a term from a Cterm_t
. - PlTerm_integer
- Subclass of
PlTerm
with constructors for building a term that contains a Prolog integer from along
.9PL_put_integer() takes along
argument. - PlTerm_int64
- Subclass of
PlTerm
with constructors for building a term that contains a Prolog integer from aint64_t
. - PlTerm_uint64
- Subclass of
PlTerm
with constructors for building a term that contains a Prolog integer from auint64_t
. - PlTerm_size_t
- Subclass of
PlTerm
with constructors for building a term that contains a Prolog integer from asize_t
. - PlTerm_float
- Subclass of
PlTerm
with constructors for building a term that contains a Prolog float. - PlTerm_pointer
- Subclass of
PlTerm
with constructors for building a term that contains a raw pointer. This is mainly for backwards compatibility; new code should use blobs. - PlTerm_string
- Subclass of
PlTerm
with constructors for building a term that contains a Prolog string object. - PlTerm_list_codes
- Subclass of
PlTerm
with constructors for building Prolog lists of character integer values. - PlTerm_chars
- Subclass of
PlTerm
with constructors for building Prolog lists of one-character atoms (as atom_chars/2). - PlTerm_tail
- SubClass of
PlTerm
for building and analysing Prolog lists.
Additional subclasses of
PlTerm
are:- PlCompound
- Subclass of
PlTerm
with constructors for building compound terms. If there is a single string argument, then PL_chars_to_term() or PL_wchars_to_term() is used to parse the string and create the term. If the constructor has two arguments, the first is name of a functor and the second is aPlTermv
with the arguments. - PlTermv
- Vector of Prolog terms. See PL_new_term_refs(). The
operator is overloaded to access elements in this vector.[]
PlTermv
is used to build complex terms and provide argument-lists to Prolog goals. - PlException
- Subclass of
PlTerm
representing a Prolog exception. Provides methods for the Prolog communication and mapping to human-readable text representation. - PlTypeError
- Subclass of
PlException
for representing a Prologtype_error
exception. - PlDomainError
- Subclass of
PlException
for representing a Prologdomain_error
exception. - PlExistenceError
- Subclass of
PlException
for representing a Prologexistence_error
exception. - PlPermissionError
- Subclass of
PlException
for representing a Prologpermission_error
exception.
- PlAtom
- Allow for manipulating atoms (
atom_t
) in their internal Prolog representation for fast comparison. (For more details onatom_t
, see Interface Data Types). - PlFunctor
- A wrapper for
functor_t
, which maps to the internal representation of a name/arity pair. - PlPredicate
- A wrapper for
predicate_t
, which maps to the internal representation of a Prolog predicate. - PlModule
- A wrapper for
module_t
, which maps to the internal representation of a Prolog module. - PlQuery
- Represents opening and enumerating the solutions to a Prolog query.
- PlFail
- Can be thrown to short-circuit processing and return failure to Prolog.
Performance-critical code should use
return false
instead if failure is expected. - PlFrame
- This utility-class can be used to discard unused term-references as well as to do‘data-backtracking’.
- PlEngine
- This class is used in embedded applications (applications where the main control is held in C++). It provides creation and destruction of the Prolog environment.
- PlRegister
- The encapsulation of PL_register_foreign() is defined to be able to use C++ global constructors for registering foreign predicates.
The required C++ function header and registration of a predicate is arranged through a macro called PREDICATE().
2.4.3 Naming conventions, utility functions and methods (version 2)
See also section 2.4.1.
The classes all have names starting with "Pl", using CamelCase; this contrasts with the C functions that start with "PL_" and use underscores.
The wrapper classes (PlFunctor
, PlAtom
, PlTerm
)
all contain a field C_
that contains the wrapped value (functor_t
, atom_t
, term_t
respectively).
The wrapper classes (which subclass WrappedC< ...
)
all define the following methods and constants:
- default constructor (sets the wrapped value to
null
) - constructor that takes the wrapped value (e.g., for
PlAtom
, the constructor takes anatom_t
value). C_
- the wrapped value. This can be used directly when calling C functions, for example, ift
anda
are of typePlTerm
andPlAtom
:Plcheck(PL_put_atom(t.C_,a.C_))
.null
- the null value (typically0
, but code should not rely on this)is_null()
,not_null()
- test for the wrapped value beingnull
.reset()
- set the wrapped value tonull
reset(new_value)
- set the wrapped valueverify()
- if the wrapped value (C_
) isnull
, throw a PlFail() exception. Typically, this check is done after an allocation function such as Plnew_term_ref() returns a null value, so the PlFail() is turned into a a resource error. However, if there is no pending exception, this results in simple failure (see section 2.18.2).- The
bool
operator is turned off - you should use not_null() instead.10The reason: abool
conversion causes ambiguity withPlAtom(PlTterm)
andPlAtom(atom_t)
.
The C_
field can be used wherever a atom_t
or
term_t
is used. For example, the PL_scan_options()
example code can be written as follows. Note the use of &callback.C_
to pass a pointer to the wrapped term_t
value.
PREDICATE(mypred, 2) { auto options = A2; int quoted = false; size_t length = 10; PlTerm_var callback; PlCheck(PL_scan_options(options, 0, "mypred_options", mypred_options, "ed, &length, &callback.C_)); callback.record(); // Needed if callback is put in a blob that Prolog doesn't know about. // If it were an atom (OPT_ATOM): register_ref(). <implement mypred> }
For functions in SWI-Prolog.h
that don't have a C++
equivalent in SWI-cpp2.h
, PlCheck() is a convenience
function that checks the return code and throws a PlFail
exception on failure. The
PREDICATE() code catches PlFail
exceptions and
converts them to the foreign_t
return code for failure. If
the failure from the C function was due to an exception (e.g.,
unification failed because of an out-of-memory condition), the foreign
function caller will detect that situation and convert the failure to an
exception.
The "getter" methods for PlTerm
all throw an exception
if the term isn't of the expected Prolog type. Where possible, the
"getters" have the same name as the underlying type; but this isn't
possible for types such as int
or float
, so
for these the name is prepended with "as_".
"Getters" for integers have an additionnal problem, in that C++
doesn't define the sizes of int
and long
, nor
for
size_t
. It seems to be impossible to make an overloaded
method that works for all the various combinations of integer types on
all compilers, so there are specific methods for int64_t
,
uint64_t
, size_t
.
In some cases,it is possible to overload methods; for example, this
allows the following code without knowing the exact definition of
size_t
:
PREDICATE(p, 1) { size_t sz; A1.integer(&sz); ... }
It is strongly recommended that you enable conversion checking.
For example, with GNU C++, these options (possibly with -Werror
:
-Wconversion -Warith-conversion -Wsign-conversion
-Wfloat-conversion
.
There is an additional problem with characters - C promotes them to int
but C++ doesn't. In general, this shouldn't cause any problems, but care
must be used with the various getters for integers.