- 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
- Converting PlTerm to native C and C++ types
- Unification
- Comparison
- Analysing compound terms
- Miscellaneous
- The class PlTerm_string
- The class PlCodeList
- The class PlCharList
- The class PlCompound
- The class PlTerm_tail
- The class PlTermv
- The class PlAtom - Supporting Prolog constants
- Classes for the recorded database: PlRecord and PlRecordExternalCopy
- 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.11 Overview of accessing and changing values
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:
falsereturn 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.
1.11.1 Converting PlTerm to native C and C++ types
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
longif thePlTermis a Prolog integer or float that can be converted without loss to a long. Throws atype_errorexception 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
PlTermrepresents 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()
1.11.2 Unification
See also section 1.13.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
stringis shown, you can use:char*whar_t*std::stringstd::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.
1.11.3 Comparison
- 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
PlTermto alongand perform standard C-comparison between the two long integers. IfPlTermcannot be converted atype_erroris raised. - bool PlTerm::operator ==(const wchar_t *)
- bool PlTerm::operator ==(const char *)
- bool PlTerm::operator ==(std::wstring)
- bool PlTerm::operator ==(std::string)
- Yields
trueif thePlTermis an atom or string representing the same text as the argument,falseif the conversion was successful, but the strings are not equal and antype_errorexception if the conversion failed.
Below are some typical examples. See section 1.11.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” . |
1.11.4 Analysing compound terms
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
PlTermis a compound term and arg is between 1 and the arity of the term, return a newPlTermrepresenting the arg-th argument of the term. IfPlTermis not compound, atype_erroris raised. Id arg is out of range, adomain_erroris 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_errorif the argument is not compound. - size_t PlTerm::arity()
- Returns the arity of the compound term. Raises a
type_errorif the argument is not compound.
1.11.5 Miscellaneous
- 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_STRINGorPL_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.
1.11.6 The class PlTerm_string
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.
1.11.7 The class PlCodeList
- PlCodeList :: PlCodeList(const wchar_t *text)
- PlCodeList :: PlCodeList(const char *text)
- Create a Prolog list of ASCII codes from a 0-terminated C-string.
1.11.8 The class PlCharList
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.
1.11.9 The class PlCompound
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_errorexception 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
PlTermvfor details. The example below creates the Prolog termhello(world).PlCompound("hello", PlTermv(PlAtom("world")))
1.11.10 The class PlTerm_tail
The class PlTerm_tail27This
was named PlTail in version 1 of the API. is
both for analysing and constructing lists. It is called PlTerm_tail
as enumeration-steps make the term-reference follow the “tail” of
the list.
- PlTerm_tail :: PlTerm_tail(PlTerm list)
- A
PlTerm_tailis created by making a new term-reference pointing to the same object. AsPlTerm_tailis used to enumerate or build a Prolog list, the initial list term-reference keeps pointing to the head of the list. - int PlTerm_tail::append(const PlTerm &element)
- Appends element to the list and make the
PlTerm_tailreference 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 thePlTerm_tailobject now points to the new variable B.This function returns
trueif the unification succeeded andfalseotherwise. 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); PlTerm_tail 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 PlTerm_tail::close()
- Unifies the term with
and returns the result of the unification.[] - int PlTerm_tail::next(PlTerm &)
- Bind t to the next element of the list
PlTerm_tailand advancePlTerm_tail. Returnstrueon success andfalseifPlTerm_tailrepresents the empty list. IfPlTerm_tailis neither a list nor the empty list, atype_erroris thrown. The example below prints the elements of a list.PREDICATE(write_list, 1) { PlTerm_tail tail(A1); PlTerm_var e; while(tail.next(e)) cout << e.as_string() << endl; return tail.close(); }
1.11.11 The class PlTermv
The class PlTermv represents an array of
term-references. This type is used to pass the arguments to a foreign
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. Note that these can be
error-prone because there's no distinction between term_t
and
size_t; the form of the constructor is determined by
whether the first argument is an integer (term_t or size_t)
or
PlTerm.
- PlTermv :: PlTermv(size_t size)
- Create a new array of term-references, all holding variables.
- PlTermv :: PlTermv(size_t size, term_t t0)
- Convert a C-interface defined term-array into an instance. Typyically, t0 was created using Pl_new_term_refs(size).
- PlTermv :: PlTermv(PlTerm ...)
- Create a vector from 1 to 5 initialising arguments. For example:
load_file(const char *file) { return PlCall("compile", PlTermv(PlAtom(file))); }If the vector has to contain more than 5 elements, the following construction should be used:
{ PlTermv av(10); av[0].put_term(PlTerm_atom("hello")); av[1].put_term(PlTerm_integer(666)); ... }Important: be sure that all the arguments are of type
PlTerm-PlTermv(i)is not the same asPlTermv(PlTerm_integer(i)), and will result in a runtime error.
1.11.12 The class PlAtom - Supporting Prolog constants
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:
1.11.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.
1.11.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.
1.11.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 1.11.12.2, but in nested if-then-else the extraction of the atom from the term is done only once.
1.11.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_erroris 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
trueif the atom represents text,falseotherwise. Performs a strcmp() or similar for this. - int PlAtom::operator ==(const PlAtom &)
- Compares the two atom-handles, returning
trueorfalse. 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.28If
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-addressor-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 forencare:EncLatin1- throws an exception if cannot be represented in ASCII.EncUTF8EncLocale- 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().
1.11.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
PlRecordobject. - 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).29The
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 (which may contain nulls). It
supports the following methods.
- PlRecordExternalCopy :: PlRecordExternalCopy(PlTerm t)
- Creates a string using Pl_record_external(), copies it into the object then deletes the reference using PL_erase_external().
- PlRecordExternalCopy :: PlRecordExternalCopy(const std::string& external)
- Saves the external string (which is assumed to have been created using PL_record_external()).
- PlRecordExternalCopy :: PlRecordExternalCopy(const char* external, size_t len)
- Saves the external string (which is assumed to have been created using PL_record_external()).
- PlTerm term()
- creates a term from the saved external record string, using PL_recorded_external()).
- static PlTerm term(const std::string& external)
- Creates a term from the external record string. Equivalent to PlRecordExternalCopy(external).term().
- static PlTerm term(const char* external)
- Creates a term from the external record string. Equivalent to PlRecordExternalCopy(external,len).term() except the length is inferred from external’s contents.
- const std::string& data()
- Gets the external string that was created by the constructor.