- 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)
- Converting PlTerm to native C and C++ types (version 2)
- Unification (version 2)
- Comparison (version 2)
- Analysing compound terms (version 2)
- Miscellaneous (version 2)
- The class PlTerm_string (version 2)
- The class PlCodeList (version 2)
- The class PlCharList (version 2)
- The class PlCompound (version 2)
- The class PlTail (version 2)
- The class PlTermv (version 2)
- The class PlAtom - Supporting Prolog constants (version 2)
- Classes for the recorded database: PlRecord and PlRecordExternalCopy
- 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.10 Overview of accessing and changing values (version 2)
The SWI-Prolog.h
header provides various functions for
accessing, setting, and unifying terms, atoms and other types.
Typically, these functions return a 0
(false
)
or
1
(true
) value for whether they succeeded or
not. For failure, there might also be an exception created - this can be
tested by calling PL_excpetion(0).
There are three major groups of methods:
- Put (set) a value, corresponding to the PL_put_*() functions.
- Get a value, corresponding to the PL_get_*() and PL_get_*_ex() functions.
- Unify a value, corresponding to the PL_unify_*() and PL_unify_*_ex() functions.
The “put” operations are typically done on an uninstantiated term (see the PlTerm_var() constructor). These are expected to succeed, and typically raise an exception failure (e.g., resource exception) - for details, see the corresponding PL_put_*() functions in Constructing Terms.
For the “get” and “unify” operations, there are three possible failures:
false
return code- unification failure
- exception (value of unexpected type or out of resources)
Each of these is communicated to Prolog by returning false
from the top level; exceptions also set a “global” exception
term (using PL_raise_exception()). The C++ programmer usually
doesn't have to worry about this; instead they can throw PlFail()
for failure or throw PlException()
(or one of PlException
’s
subclasses) and the C++ API will take care of everything.
2.10.1 Converting PlTerm to native C and C++ types (version 2)
These are deprecated and replaced by the various as_*()
methods.
PlTerm
can be converted to the following types:
- PlTerm ::operator term_t(void)
- This cast is used for integration with the C-interface primitives.
- PlTerm ::operator long(void)
- Yields a
long
if thePlTerm
is a Prolog integer or float that can be converted without loss to a long. Throws atype_error
exception otherwise. - PlTerm ::operator int(void)
- Same as for
long
, but might represent fewer bits. - PlTerm ::operator double(void)
- Yields the value as a C double if
PlTerm
represents a Prolog integer or float. - PlTerm ::operator wchar_t *(void)
- PlTerm ::operator char *(void)
- Converts the Prolog argument using PL_get_chars() using the flags
CVT_ALL|CVT_WRITE|BUF_RING
, which implies Prolog atoms and strings are converted to the represented text. All other data is handed to write/1. If the text is static in Prolog, a direct pointer to the string is returned. Otherwise the text is saved in a ring of 16 buffers and must be copied to avoid overwriting. - PlTerm ::operator void *(void)
- Extracts pointer value from a term. The term should have been created by PlTerm::PlTerm(void*).
In addition, the Prolog type (`PL_VARIABLE`,‘PL_ATOM`, ...‘PL_DICT`) can be determined using the type() method. There are also boolean methods that check the type:
- int PlTerm::type()
- See PL_term_type()
- bool PlTerm::is_variable()
- See PL_is_variable()
- bool PlTerm::is_ground()
- See PL_is_ground()
- bool PlTerm::is_atom(S)
- ee PL_is_atom()
- bool PlTerm::is_integer(S)
- ee PL_is_integer()
- bool PlTerm::is_string(S)
- ee PL_is_string()
- bool PlTerm::is_atom_or_string(I)
- s true if either PlTerm::is_atom() or PlTerm::is_string() is true.
- bool PlTerm::is_float(S)
- ee PL_is_float()
- bool PlTerm::is_rational(S)
- ee PL_is_rational()
- bool PlTerm::is_compound(S)
- ee PL_is_compound()
- bool PlTerm::is_callable(S)
- ee PL_is_callable()
- bool PlTerm::is_list(S)
- ee PL_is_list()
- bool PlTerm::is_dict(S)
- ee PL_is_dict()
- bool PlTerm::is_pair(S)
- ee PL_is_pair()
- bool PlTerm::is_atomic(S)
- ee PL_is_atomic()
- bool PlTerm::is_number(S)
- ee PL_is_number()
- bool PlTerm::is_acyclic(S)
- ee PL_is_acyclic()
- bool PlTerm::is_functor(PlFunctor)
- See PL_is_functor()
2.10.2 Unification (version 2)
See also section 2.12.1.
- bool PlTerm::unify_term(PlTerm)
- bool PlTerm::unify_atom(PlAtom)
- bool PlTerm::unify_atom(string)
- bool PlTerm::unify_list_codes(string)
- bool PlTerm::unify_list_chars(string)
- bool PlTerm::unify_integer(int)
- bool PlTerm::unify_float(double)
- bool PlTerm::unify_string(string)
- bool PlTerm::unify_functor(PlFunctor)
- bool PlTerm::unify_pointer(void *)
- bool PlTerm::unify_nil()
- bool PlTerm::unify_blob(PlBlob* blob)
- bool PlTerm::unify_blob(std::unique_ptr<PlBlob>* blob)
- Does a call to PL_unify_blob() and, if successful, calls
std::unique_ptr<PlBlob>::release() to pass
ownership to the Prolog blob; on failure or error, deletes the pointer
(ad calls its destructor). After either success and failure,
*blob==nullptr
. - bool PlTerm::unify_blob(void *blob, size_t len, PL_blob_t *type)
- bool PlTerm::unify_chars(int flags, size_t len, const char *s)
-
A family of unification methods are defined for the various Prolog types and C++ types. Wherever
string
is shown, you can use:char*
whar_t*
std::string
std::wstring
Here is an example:
PREDICATE(hostname, 1) { char buf[256]; if ( gethostname(buf, sizeof buf) == 0 ) return A1.unify_atom(buf); return false; }
An alternative way of writing this would use the PlCheckFail() to raise an exception if the unification fails.
PREDICATE(hostname2, 1) { char buf[256]; PlCheckFail(gethostname(buf, sizeof buf) == 0); PlCheckFail(A1.unify_atom(buf)); return true; }
Of course, in a real program, the failure of
gethostname(buf)sizeof buf should create an error term than
contains information from errno
.
2.10.3 Comparison (version 2)
- int PlTerm::compare(const PlTerm &t2)
- bool PlTerm::operator ==(const PlTerm &)
- bool PlTerm::operator !=(const PlTerm &)
- bool PlTerm::operator <(const PlTerm &)
- bool PlTerm::operator >(const PlTerm &)
- bool PlTerm::operator <=(const PlTerm &)
- bool PlTerm::operator >=(const PlTerm &)
- Compare the instance with t and return the result according to the Prolog defined standard order of terms.
- bool PlTerm::operator ==(long num)
- bool PlTerm::operator !=(long num)
- bool PlTerm::operator <(long num)
- bool PlTerm::operator >(long num)
- bool PlTerm::operator <=(long num)
- bool PlTerm::operator >=(long num)
- Convert
PlTerm
to along
and perform standard C-comparison between the two long integers. IfPlTerm
cannot be converted atype_error
is raised. - bool PlTerm::operator ==(const wchar_t *)
- bool PlTerm::operator ==(const char *)
- bool PlTerm::operator ==(std::wstring)
- bool PlTerm::operator ==(std::string)
- Yields
true
if thePlTerm
is an atom or string representing the same text as the argument,false
if the conversion was successful, but the strings are not equal and antype_error
exception if the conversion failed.
Below are some typical examples. See section 2.10.12.2 for direct manipulation of atoms in their internal representation.
A1 < 0 | Test A1 to hold a Prolog integer or float that can be transformed lossless to an integer less than zero. |
A1 < PlTerm(0) | A1
is before the term‘0’in the‘standard order of terms’.
This means that if A1 represents an atom, this test yields true . |
A1 == PlCompound("a(1)") | Test A1
to represent the term
a(1) . |
A1 == "now" | Test A1 to be an atom or string holding the text “now” . |
2.10.4 Analysing compound terms (version 2)
Compound terms can be viewed as an array of terms with a name and
arity (length). This view is expressed by overloading the
operator.
[]
A type_error
is raised if the argument is not compound
and a
domain_error
if the index is out of range.
In addition, the following functions are defined:
- PlTerm PlTerm::operator[](int arg)
- If the
PlTerm
is a compound term and arg is between 1 and the arity of the term, return a newPlTerm
representing the arg-th argument of the term. IfPlTerm
is not compound, atype_error
is raised. Id arg is out of range, adomain_error
is raised. Please note the counting from 1 which is consistent to Prolog's arg/3 predicate, but inconsistent to C's normal view on an array. See also classPlCompound
. The following example tests x to represent a term with first-argument an atom or string equal tognat
...., if ( x[1] == "gnat" ) ...
- const char * PlTerm::name()
- Return a
const char *
holding the name of the functor of the compound term. Raises atype_error
if the argument is not compound. - size_t PlTerm::arity()
- Returns the arity of the compound term. Raises a
type_error
if the argument is not compound.
2.10.5 Miscellaneous (version 2)
- bool is_null()
t.is_null()
is the same ast.unwrap() == PlTerm::null
- bool not_null()
t.not_null()
is the same ast.unwrap() != PlTerm::null
- bool reset()
t.reset()
is the same ast.unwrap() = PlTerm::null
- bool reset(term_t)
t.reset(x)
is the same ast.unwrap() = x
- int PlTerm::type()
- Yields the actual type of the term as PL_term_type(). Return
values are
PL_VARIABLE
,PL_FLOAT
,PL_INTEGER
,PL_ATOM
,PL_STRING
orPL_TERM
- std::string as_string(PlEncoding enc=EncLocale)
- Returns the string representation of the atom. See PlAtom::as_string() for an explanation of the encodings and caveats about std::string::c_str().
- std::string atomic_as_string(PlEncoding enc=EncLocale)
- As PlTerm::as_string(), but throws an exception if the term isn't atomic (see atomic/1).
- std::string atom_or_string_as_string(PlEncoding enc=EncLocale)
- As PlTerm::as_string(),
but throws an exception if the term isn't an atom or a string.
To avoid very confusing combinations of constructors and therefore
possible undesirable effects a number of subclasses of PlTerm
have been defined that provide constructors for creating special Prolog
terms. These subclasses are defined below.
2.10.6 The class PlTerm_string (version 2)
A SWI-Prolog string represents a byte-string on the global stack. Its
lifetime is the same as for compound terms and other data living on the
global stack. Strings are not only a compound representation of text
that is garbage-collected, but as they can contain 0-bytes, they can be
used to contain arbitrary C-data structures. However, it is generally
preferred to use blobs for storing arbitrary C-data structures (see also PlTerm_pointer(void
*ptr)
).
- PlTerm_string :: PlTerm_string(const wchar_t *text)
- PlTerm_string :: PlTerm_string(const char *text)
- Create a SWI-Prolog string object from a 0-terminated C-string. The text is copied.
- PlTerm_string :: PlTerm_string(const wchar_t *text, size_t len)
- PlTerm_string :: PlTerm_string(const char *text, size_t len)
- Create a SWI-Prolog string object from a C-string with specified length. The text may contain 0-characters and is copied.
2.10.7 The class PlCodeList (version 2)
- PlCodeList :: PlCodeList(const wchar_t *text)
- PlCodeList :: PlCodeList(const char *text)
- Create a Prolog list of ASCII codes from a 0-terminated C-string.
2.10.8 The class PlCharList (version 2)
Character lists are compliant to Prolog's atom_chars/2 predicate.
- PlCharList :: PlCharList(const wchar_t *text)
- PlCharList :: PlCharList(const char *text)
- Create a Prolog list of one-character atoms from a 0-terminated C-string.
2.10.9 The class PlCompound (version 2)
The PlCompound
class is a convenience class for creating
a term from a string; it is similar to (=..)/2
- PlCompound :: PlCompound(const wchar_t *text)
- PlCompound :: PlCompound(const char *text)
- PlCompound :: PlCompound(const std::wstring& text)
- PlCompound :: PlCompound(const std::string& text)
- PlEncoding enc=ENC_INPUT Create a term by parsing (as read/1)
the text. If the text is not valid Prolog syntax,
a
syntax_error
exception is raised. Otherwise a new term-reference holding the parsed text is created. - PlCompound :: PlCompound(const wchar_t *functor, PlTermv args)
- PlCompound :: PlCompound(const char *functor, PlTermv args)
- Create a compound term with the given name from the given vector of
arguments. See
PlTermv
for details. The example below creates the Prolog termhello(world)
.PlCompound("hello", PlTermv("world"))
2.10.10 The class PlTail (version 2)
The class PlTail
is both for analysing and constructing
lists. It is called PlTail
as enumeration-steps make the
term-reference follow the‘tail’of the list.
- PlTail :: PlTail(PlTerm list)
- A
PlTail
is created by making a new term-reference pointing to the same object. AsPlTail
is used to enumerate or build a Prolog list, the initial list term-reference keeps pointing to the head of the list. - int PlTail::append(const PlTerm &element)
- Appends element to the list and make the
PlTail
reference point to the new variable tail. If A is a variable, and this function is called on it using the argument"gnat"
, a list of the form[gnat|B]
is created and thePlTail
object now points to the new variable B.This function returns
true
if the unification succeeded andfalse
otherwise. No exceptions are generated.The example below translates the main() argument vector to Prolog and calls the prolog predicate entry/1 with it.
int main(int argc, char **argv) { PlEngine e(argv[0]); PlTermv av(1); PlTail l(av[0]); for(int i=0; i<argc; i++) PlCheckFail(l.append(argv[i])); PlCheckFail(l.close()); PlQuery q("entry", av); return q.next_solution() ? 0 : 1; }
- int PlTail::close()
- Unifies the term with
and returns the result of the unification.[]
- int PlTail::next(PlTerm &)
- Bind t to the next element of the list
PlTail
and advancePlTail
. Returnstrue
on success andfalse
ifPlTail
represents the empty list. IfPlTail
is neither a list nor the empty list, atype_error
is thrown. The example below prints the elements of a list.PREDICATE(write_list, 1) { PlTail tail(A1); PlTerm e; while(tail.next(e)) cout << e.as_string() << endl; return true; }
2.10.11 The class PlTermv (version 2)
The class PlTermv
represents an array of
term-references. This type is used to pass the arguments to a foreignly
defined predicate, construct compound terms (see
PlTerm::PlTerm(const char *name)PlTermv arguments ) and to create
queries (see PlQuery
).
The only useful member function is the overloading of
,
providing (0-based) access to the elements. Range checking is performed
and raises a []
domain_error
exception.
The constructors for this class are below.
- PlTermv :: PlTermv(int size)
- Create a new array of term-references, all holding variables.
- PlTermv :: PlTermv(int size, term_t t0)
- Convert a C-interface defined term-array into an instance.
- PlTermv :: PlTermv(PlTerm ...)
- Create a vector from 1 to 5 initialising arguments. For example:
load_file(const char *file) { return PlCall("compile", PlTermv(file)); }
If the vector has to contain more than 5 elements, the following construction should be used:
{ PlTermv av(10); av[0] = "hello"; ... }
2.10.12 The class PlAtom - Supporting Prolog constants (version 2)
Both for quick comparison as for quick building of lists of atoms, it is desirable to provide access to Prolog's atom-table, mapping handles to unique string-constants. If the handles of two atoms are different it is guaranteed they represent different text strings.
Suppose we want to test whether a term represents a certain atom, this interface presents a large number of alternatives:
2.10.12.1 Direct comparision to char *
Example:
PREDICATE(test, 1) { if ( A1 == "read" ) ...; }
This writes easily and is the preferred method is performance is not critical and only a few comparisons have to be made. It validates A1 to be a term-reference representing text (atom, string, integer or float) extracts the represented text and uses strcmp() to match the strings.
2.10.12.2 Direct comparision to PlAtom
Example:
static PlAtom ATOM_read("read"); PREDICATE(test, 1) { if ( A1 == ATOM_read ) ...; }
This case raises a type_error
if A1 is not an
atom. Otherwise it extacts the atom-handle and compares it to the
atom-handle of the global PlAtom
object. This approach is
faster and provides more strict type-checking.
2.10.12.3 Extraction of the atom and comparison to PlAtom
Example:
static PlAtom ATOM_read("read"); PREDICATE(test, 1) { PlAtom a1(A1); if ( a1 == ATOM_read ) ...; }
This approach is basically the same as section 2.10.12.2, but in nested if-then-else the extraction of the atom from the term is done only once.
2.10.12.4 Extraction of the atom and comparison to char *
Example:
PREDICATE(test, 1) { PlAtom a1(A1); if ( a1 == "read" ) ...; }
This approach extracts the atom once and for each test extracts the represented string from the atom and compares it. It avoids the need for global atom constructors.
- PlAtom :: PlAtom(atom_t handle)
- Create from C-interface atom handle (
atom_t
). Used internally and for integration with the C-interface. - PlAtom :: PlAtom(const char_t *text)
- PlAtom :: PlAtom(const wchar *text)
- PlAtom :: PlAtom(const std::string& text)
- PlAtom :: PlAtom(const std::wstring& text)
- Create an atom from a string. The text is copied if a new atom is created. See PL_new_atom(), PL_new_atom_wchars(), PL_new_atom_nchars(), PL_new_atom_wchars().
- PlAtom :: PlAtom(const PlTerm &)
- If t represents an atom, the new instance represents this
atom. Otherwise a
type_error
is thrown. - int PlAtom::operator ==(const wchar_t *text)
- int PlAtom::operator ==(const char *text)
- int PlAtom::operator ==(const std::string& text)
- int PlAtom::operator ==(const std::wstring& text)
- Yields
true
if the atom represents text,false
otherwise. Performs a strcmp() or similar for this. - int PlAtom::operator ==(const PlAtom &)
- Compares the two atom-handles, returning
true
orfalse
. Because atoms are unique, there is no need to use strcmp() for this. - int PlAtom::operator !=(const wchar_t *text)
- int PlAtom::operator !=(const char *text)
- int PlAtom::operator !=(const std::string& text)
- int PlAtom::operator !=(const std::wstring& text)
- int PlAtom::operator !=(const PlAtom &)
- The inverse of the
operator.==
- bool is_valid()
- Verifies that the handle is valid. This can be used after calling a function that returns an atom handle, to check that a new atom was created.
- void reset()
- Sets the handle to an invalid valid - a subsequent call to is_null()
will return
true
. - const std::string as_string(PlEncoding enc=EncLocale)
- Returns the string representation of the atom.25If
you wish to return a
char*
from a function, you should not doreturn t.as_string().c_str()
because that will return a pointer into the stack (Gnu C++ or Clang options-Wreturn-stack-address
or-Wreturn-local-addr
) can sometimes catch this, as can the runtime address sanitizer when run withdetect_stack_use_after_return=1
. This does not quote or escape any characters that would need to be escaped if the atom were to be input to the Prolog parser. The possible values forenc
are:EncLatin1
- throws an exception if cannot be represented in ASCII.EncUTF8
EncLocale
- uses the locale to determine the representation.
- const std:wstring as_wstring()
- Returns the string representation of the atom. This does not quote or escape any characters that would need to be escaped if the atom were to be input to the Prolog parser.
- void register_atom()
- See PL_register_atom().
- void unregister_atom()
- See PL_unregister_atom().
- void* blob_data(size_t *len, struct PL_blob_t **type)
- See PL_blob_data().
2.10.13 Classes for the recorded database: PlRecord and PlRecordExternalCopy
The recorded database is has two wrappers, for supporting the internal records and external records.
Currently, the interface to internal records requires that
the programmer explicitly call the dupicate() and erase()
methods - in future, it is intended that this will be done automatically
by a new
PlRecord
class, so that the internal records behave like “smart
pointers” ; in the meantime, the PlRecord
provides a
trivial wrapper around the various recorded database functions.
The class PlRecord
supports the following methods:
- PlRecord(PlTerm)
- Constructor.
- PlRecord(PlRecord)
- Copy and move constructors. Currently these do not do any reference counting. The assignment operator is currently not supported.
- PlRecord()
- Destructor. Currently this does not call PL_erase().
- PlTerm term()
- creates a term from the record, using PL_recorded().
- void erase()
- decrements the reference count of the record and deletes it if the count
goes to zero, using PL_erase(). It is safe to do this multiple
times on the same
PlRecord
object. - PlRecord duplicate()
- increments the reference count of the record, using PL_duplicate_record().
The class PlRecord
provides direct access to the
reference counting aspects of the recorded term (through the duplicate()
and
erase() methods), but does not
connect these with C++'s copy constructor, assignment operator, or
destructor. If the recorded term is encapsulated within an object, then
the containing object can use the duplicate()
and erase() methods in its copy and
move constructors and assignment operators (and the erase()
method in the destructor).26The
copy constructor and assignment use the duplicate()
method; the move constructor and assignment use the duplicate()
method to assign to the destination and the erase()
method on the source; and the destructor uses erase().
Alternatively, the std::shared_ptr
or std::unique_ptr
can be used with the supplied PlrecordDeleter
, which calls
the
erase() method when the shared_ptr
reference count goes to zero or when the std::unique_ptr
goes out of scope.
For example:
std::shared_ptr<PlRecord> r(new PlRecord(t.record()), PlRecordDeleter()); assert(t.unify_term(r->term()));
The class PlRecordExternalCopy
keeps the external
record as an uninterpreted string. It supports the following
methods.
- PlRecordExternalCopy()
- Constructor. Creates a string using Pl_record_external(), copies it into the object, then deletes the reference using PL_erase_external().
- PlTerm term()
- creates a term from the record, using PL_recorded_external()).