- Documentation
- Reference manual
- Foreign Language Interface
- The Foreign Include File
- Argument Passing and Control
- Atoms and functors
- Analysing Terms via the Foreign Interface
- Constructing Terms
- Unifying data
- Convenient functions to generate Prolog exceptions
- Foreign language wrapper support functions
- Serializing and deserializing Prolog terms
- BLOBS: Using atoms to store arbitrary binary data
- Exchanging GMP numbers
- Calling Prolog from C
- Discarding Data
- String buffering
- Foreign Code and Modules
- Prolog exceptions in foreign code
- Catching Signals (Software Interrupts)
- Miscellaneous
- Errors and warnings
- Environment Control from Foreign Code
- Querying Prolog
- Registering Foreign Predicates
- Foreign Code Hooks
- Storing foreign data
- Embedding SWI-Prolog in other applications
- The Foreign Include File
- Foreign Language Interface
- Packages
- Reference manual
NULL
is returned. The
argument of the called hook is the atom that is to be garbage collected.
The return value is an int
. If the return value is zero,
the atom is not reclaimed. The hook may invoke any Prolog
predicate.
The example below defines a foreign library for printing the garbage collected atoms for debugging purposes.
#include <SWI-Stream.h> #include <SWI-Prolog.h> static int atom_hook(atom_t a) { Sdprintf("AGC: deleting %s\n", PL_atom_chars(a)); return TRUE; } static PL_agc_hook_t old; install_t install() { old = PL_agc_hook(atom_hook); } install_t uninstall() { PL_agc_hook(old); }
12.4.23 Storing foreign data
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.9) 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
collector.
- 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 12.4.1.1.
- 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.
12.4.23.1 Examples for storing foreign data
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-vectors. Then, we discuss the outline of the DDE interface.
Integer sets with not-too-far-apart upper- and lower-bounds can be represented using bit-vectors. Common set operations, such as union, intersection, etc., are reduced to simple and’ing and or’ing the bit-vectors. This can be done using Prolog's unbounded integers.
For really demanding applications, foreign representation will
perform better, especially time-wise. Bit-vectors are naturally
expressed using string objects. If the string is wrapped in bitvector/1
,
the lower-bound of the vector is 0 and the upper-bound is not defined;
an implementation for getting and putting the sets as well as the union
predicate for it is below.
#include <SWI-Prolog.h> #define max(a, b) ((a) > (b) ? (a) : (b)) #define min(a, b) ((a) < (b) ? (a) : (b)) static functor_t FUNCTOR_bitvector1; static int get_bitvector(term_t in, int *len, unsigned char **data) { if ( PL_is_functor(in, FUNCTOR_bitvector1) ) { term_t a = PL_new_term_ref(); PL_get_arg(1, in, a); return PL_get_string(a, (char **)data, len); } PL_fail; } static int unify_bitvector(term_t out, int len, const unsigned char *data) { if ( PL_unify_functor(out, FUNCTOR_bitvector1) ) { term_t a = PL_new_term_ref(); PL_get_arg(1, out, a); return PL_unify_string_nchars(a, len, (const char *)data); } PL_fail; } static foreign_t pl_bitvector_union(term_t t1, term_t t2, term_t u) { unsigned char *s1, *s2; int l1, l2; if ( get_bitvector(t1, &l1, &s1) && get_bitvector(t2, &l2, &s2) ) { int l = max(l1, l2); unsigned char *s3 = alloca(l); if ( s3 ) { int n; int ml = min(l1, l2); for(n=0; n<ml; n++) s3[n] = s1[n] | s2[n]; for( ; n < l1; n++) s3[n] = s1[n]; for( ; n < l2; n++) s3[n] = s2[n]; return unify_bitvector(u, l, s3); } return PL_warning("Not enough memory"); } PL_fail; } install_t install() { PL_register_foreign("bitvector_union", 3, pl_bitvector_union, 0); FUNCTOR_bitvector1 = PL_new_functor(PL_new_atom("bitvector"), 1); }
The DDE interface (see section 4.44) represents another common usage of the foreign interface: provi