• Places
    • Home
    • Graphs
    • Prefixes
  • Admin
    • Users
    • Settings
    • Plugins
    • Statistics
  • CPACK
    • Home
    • List packs
    • Submit pack
  • Repository
    • Load local file
    • Load from HTTP
    • Load from library
    • Remove triples
    • Clear repository
  • Query
    • YASGUI SPARQL Editor
    • Simple Form
    • SWISH Prolog shell
  • Help
    • Documentation
    • Tutorial
    • Roadmap
    • HTTP Services
  • Login

1 A C++ interface to SWI-Prolog
All Application Manual Name SummaryHelp

  • 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

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:

  • 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.

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 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.
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()

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 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.

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 PlTerm to a long and perform standard C-comparison between the two long integers. If PlTerm cannot be converted a type_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 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 1.11.12.2 for direct manipulation of atoms in their internal representation.

A1 < 0Test 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 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 * PlTerm::name()
Return a const char * holding the name of the functor of the compound term. Raises a type_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.

1.11.5 Miscellaneous

bool is_null()
t.is_null() is the same as t.unwrap() == PlTerm::null
bool not_null()
t.not_null() is the same as t.unwrap() != PlTerm::null
bool reset()
t.reset() is the same as t.unwrap() = PlTerm::null
bool reset(term_t)
t.reset(x) is the same as t.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 or PL_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_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 term hello(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_tail is created by making a new term-reference pointing to the same object. As PlTerm_tail is 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_tail 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 PlTerm_tail 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);
  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_tail and advance PlTerm_tail. Returns true on success and false if PlTerm_tail represents the empty list. If PlTerm_tail 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)
{ 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 as PlTermv(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_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 or false. 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 do return 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 with detect_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 for enc 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().

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 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).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.

ClioPatria (version V3.1.1-51-ga0b30a5)