When combining foreign code with Prolog, it can be necessary to make data represented in the foreign language available to Prolog. For example, to pass it to another foreign function. At the end of this section, there is a partial implementation of using foreign functions to manage bit-vectors. Another example is the SGML/XML library that manages a‘parser' object, an object that represents the current state of the parser and that can be directed to perform actions such as parsing a document or make queries about the document content.
This section provides some hints for handling foreign data in Prolog. There are four options for storing such data:
- Natural Prolog data
Uses the representation one would choose if no foreign interface was required. For example, a bitvector representing a list of small integers can be represented as a Prolog list of integers.
- Opaque packed data on the stacks
It is possible to represent the raw binary representation of the foreign object as a Prolog string (see section 5.2). Strings may be created from foreign data using PL_put_string_nchars() and retrieved using PL_get_string_chars(). It is good practice to wrap the string in a compound term with arity 1, so Prolog can identify the type. The hook portray/1 rules may be used to streamline printing such terms during development.
- Opaque packed data in a blob
Similar to the above solution, binary data can be stored in an atom. The blob interface (section 12.4.8) provides additional facilities to assign a type and hook-functions that act on creation and destruction of the underlying atom.
- Natural foreign data, passed as a pointer
An alternative is to pass a pointer to the foreign data. Again, the pointer is often wrapped in a compound term.
The choice may be guided using the following distinctions
- Is the data opaque to Prolog
With‘opaque' data, we refer to data handled in foreign functions, passed around in Prolog, but where Prolog never examines the contents of the data itself. If the data is opaque to Prolog, the selection will be driven solely by simplicity of the interface and performance.
- What is the lifetime of the data
With‘lifetime' we refer to how it is decided that the object is (or can be) destroyed. We can distinguish three cases:
- The object must be destroyed on backtracking and normal Prolog
garbage collection (i.e., it acts as a normal Prolog term). In this
case, representing the object as a Prolog string (second option above)
is the only feasible solution.
- The data must survive Prolog backtracking. This leaves two options.
One is to represent the object using a pointer and use explicit creation
and destruction, making the programmer responsible. The alternative is
to use the blob-interface, leaving destruction to the (atom) garbage
- The data lives as during the lifetime of a foreign function that implements a predicate. If the predicate is deterministic, foreign automatic variables are suitable. If the predicate is non-deterministic, the data may be allocated using malloc() and a pointer may be passed. See section 18.104.22.168.
- The object must be destroyed on backtracking and normal Prolog garbage collection (i.e., it acts as a normal Prolog term). In this case, representing the object as a Prolog string (second option above) is the only feasible solution.
In this section, we outline some examples, covering typical cases. In the first example, we will deal with extending Prolog's data representation with integer sets, represented as bit-vect