As we have seen from the examples, the PlTerm
class
plays a central role in conversion and operating on Prolog data. This
section provides complete documentation of this class.
The constructors are defined as subclasses of PlTerm
,
with a name that reflects the Prolog type of what is being created
(e.g., PlTerm_atom
creates an atom; PlTerm_string
creates a Prolog string). All of the constructors are "explicit" because
implicit creation of PlTerm
objects can lead to subtle and
difficult to debug errors.
PlTerm
. Note that, being
a lightweight class, this is a no-op at the machine-level!void *
. Also note that in general blobs
are a better way of doing this (see the section on blobs in the
Foreign Language Interface part of the SWI-Prolog manual).
PREDICATE(make_my_object, 1) { auto myobj = new MyClass(); return A1.unify_pointer(myobj); } PREDICATE(my_object_contents, 2) { auto myobj = static_cast<MyClass*>(A1.pointer()); return A2.unify_string(myobj->contents); } PREDICATE(free_my_object, 1) { auto myobj = static_cast<MyClass*>(A1.pointer()); delete myobj; return true; }
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:
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
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.
These are deprecated and replaced by the various as_*()
methods.
PlTerm
can be converted to the following types:
long
if the PlTerm
is a Prolog
integer or float that can be converted without loss to a long. throws a
type_error
exception otherwise.long
, but might represent fewer bits.PlTerm
represents a
Prolog integer or float.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.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:
See also section 2.14.
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
.
PlTerm
to a long
and perform standard
C-comparison between the two long integers. If PlTerm
cannot be converted a type_error
is raised.true
if the PlTerm
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 an
type_error
exception if the conversion failed.Below are some typical examples. See section 2.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''. |
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
is a compound term and arg is
between 1 and the arity of the term, return a new PlTerm
representing the arg-th argument of the term. If PlTerm
is
not compound, a
type_error
is raised. Id arg is out of range, a
domain_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
class PlCompound
. The following example tests x
to represent a term with first-argument an atom or string equal to gnat
.
..., if ( x[1] == "gnat" ) ...
const char *
holding the name of the functor of
the compound term. Raises a type_error
if the argument is
not compound.type_error
if the argument is not compound.
t.is_null()
is the same as t.unwrap() == PlTerm::null
t.not_null()
is the same as t.unwrap() !=
PlTerm::null
t.reset()
is the same as t.unwrap() = PlTerm::null
t.reset(x)
is the same as t.unwrap() = x
PL_VARIABLE
, PL_FLOAT
, PL_INTEGER
,
PL_ATOM
, PL_STRING
or PL_TERM
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.
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)
).
Character lists are compliant to Prolog's atom_chars/2 predicate.
syntax_error
exception is raised. Otherwise a new
term-reference holding the parsed text is created.PlTermv
for details. The example below
creates the Prolog term hello(world)
.
PlCompound("hello", PlTermv("world"))
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
is created by making a new term-reference pointing
to the same object. As PlTail
is used to enumerate or build
a Prolog list, the initial list term-reference keeps pointing
to the head of the list.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 the PlTail
object now points to the new variable B.
This function returns true
if the unification succeeded
and
false
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; }
[]
and returns the
result of the unification.PlTail
and advance
PlTail
. Returns true
on success and false
if
PlTail
represents the empty list. If PlTail
is
neither a list nor the empty list, a type_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; }