- 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
- 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
12.4.3 Analysing Terms via the Foreign Interface
Each argument of a foreign function (except for the control argument)
is of type term_t
, an opaque handle to a Prolog term. Three
groups of functions are available for the analysis of terms. The first
just validates the type, like the Prolog predicates var/1, atom/1,
etc., and are called PL_is_*()
. The second group attempts
to translate the argument into a C primitive type. These predicates take
a term_t
and a pointer to the appropriate C type and return TRUE
or
FALSE
depending on successful or unsuccessful translation.
If the translation fails, the pointed-to data is never modified.
12.4.3.1 Testing the type of a term
- int PL_term_type(term_t)
- Obtain the type of a term, which should be a term returned by one of the
other interface predicates or passed as an argument. The function
returns the type of the Prolog term. The type identifiers are listed
below. Note that the extraction functions
PL_get_*()
also validate the type and thus the two sections below are equivalent.if ( PL_is_atom(t) ) { char *s; PL_get_atom_chars(t, &s); ...; } or char *s; if ( PL_get_atom_chars(t, &s) ) { ...; }
Version 7 added
PL_NIL
,PL_BLOB
,PL_LIST_PAIR
andPL_DICT
. Older versions classifyPL_NIL
andPL_BLOB
asPL_ATOM
,PL_LIST_PAIR
asPL_TERM
and do not have dicts.PL_VARIABLE
A variable or attributed variable PL_ATOM
A Prolog atom PL_NIL
The constant []
PL_BLOB
A blob (see section 12.4.8.2) PL_STRING
A string (see section 5.2) PL_INTEGER
A integer PL_RATIONAL
A rational number PL_FLOAT
A floating point number PL_TERM
A compound term PL_LIST_PAIR
A list cell ( [H|T]
)PL_DICT
A dict (see section 5.4))
The functions PL_is_<type> are an alternative to PL_term_type().
The test PL_is_variable(term)
is equivalent to
PL_term_type(term)
== PL_VARIABLE
, but the first is considerably faster. On the
other hand, using a switch over PL_term_type()
is faster and more readable then using an if-then-else using the
functions below. All these functions return either TRUE
or FALSE
.
- int PL_is_variable(term_t)
- Returns non-zero if term is a variable.
- int PL_is_ground(term_t)
- Returns non-zero if term is a ground term. See also ground/1. This function is cycle-safe.
- int PL_is_atom(term_t)
- Returns non-zero if term is an atom.
- int PL_is_string(term_t)
- Returns non-zero if term is a string.
- int PL_is_integer(term_t)
- Returns non-zero if term is an integer.
- int PL_is_rational(term_t)
- Returns non-zero if term is a rational number (P/Q). Note that all integers are considered rational and this test thus succeeds for any term for which PL_is_integer() succeeds. See also PL_get_mpq() and PL_unify_mpq().
- int PL_is_float(term_t)
- Returns non-zero if term is a float. Note that the corresponding PL_get_float() converts rationals (and thus integers).
- int PL_is_callable(term_t)
- Returns non-zero if term is a callable term. See callable/1 for details.
- int PL_is_compound(term_t)
- Returns non-zero if term is a compound term.
- int PL_is_functor(term_t, functor_t)
- Returns non-zero if term is compound and its functor is functor. This test is equivalent to PL_get_functor(), followed by testing the functor, but easier to write and faster.
- int PL_is_list(term_t)
- Returns non-zero if term is a compound term using the list constructor or the list terminator. See also PL_is_pair() and PL_skip_list().
- int PL_is_pair(term_t)
- Returns non-zero if term is a compound term using the list constructor. See also PL_is_list() and PL_skip_list().
- int PL_is_dict(term_t)
- Returns non-zero if term is a dict. See also PL_put_dict() and PL_get_dict_key().
- int PL_is_atomic(term_t)
- Returns non-zero if term is atomic (not a variable or compound).
- int PL_is_number(term_t)
- Returns non-zero if term is an rational (including integers) or float.
- int PL_is_acyclic(term_t)
- Returns non-zero if term is acyclic (i.e. a finite tree).
12.4.3.2 Reading data from a term
The functions PL_get_*()
read information from a Prolog
term. Most of them take two arguments. The first is the input term and
the second is a pointer to the output value or a term reference.
- int PL_get_atom(term_t +t, atom_t *a)
- If t is an atom, store the unique atom identifier over a. See also PL_atom_chars() and PL_new_atom(). If there is no need to access the data (characters) of an atom, it is advised to manipulate atoms using their handle. As the atom is referenced by t, it will live at least as long as t does. If longer live-time is required, the atom should be locked using PL_register_atom().
- int PL_get_atom_chars(term_t +t, char **s)
- If t is an atom, store a pointer to a 0-terminated C-string in s. It is explicitly not allowed to modify the contents of this string. Some built-in atoms may have the string allocated in read-only memory, so‘temporary manipulation' can cause an error.
- int PL_get_string_chars(term_t +t, char **s, size_t *len)
- If t is a string object, store a pointer to a 0-terminated C-string in s and the length of the string in len. Note that this pointer is invalidated by backtracking, garbage collection and stack-shifts, so generally the only safe operations are to pass it immediately to a C function that doesn't involve Prolog.
- int PL_get_chars(term_t +t, char **s, unsigned flags)
- Convert the argument term t to a 0-terminated C-string.
flags is a bitwise disjunction from two groups of constants.
The first specifies which term types should be converted and the second
how the argument is stored. Below is a specification of these constants.
BUF_STACK
implies, if the data is not static (as from an atom), that the data is pushed on a stack. If BUF_MALLOC is used, the data must be freed using PL_free() when no longer needed.With the introduction of wide characters (see section 2.19.1), not all atoms can be converted into a
char*
. This function fails if t is of the wrong type, but also if the text cannot be represented. See theREP_*
flags below for details.- CVT_ATOM
- Convert if term is an atom.
- CVT_STRING
- Convert if term is a string.
- CVT_LIST
- Convert if term is a list of of character codes.
- CVT_INTEGER
- Convert if term is an integer.
- CVT_FLOAT
- Convert if term is a float. The characters returned are the same as write/1 would write for the floating point number.
- CVT_NUMBER
- Convert if term is an integer or float.
- CVT_ATOMIC
- Convert if term is atomic.
- CVT_VARIABLE
- Convert variable to print-name
- CVT_WRITE
- Convert any term that is not converted by any of the other flags using
write/1.
If no
BUF_*
is provided,BUF_STACK
is implied. - CVT_WRITE_CANONICAL
- As
CVT_WRITE
, but using write_canonical/2. - CVT_WRITEQ
- As
CVT_WRITE
, but using writeq/2. - CVT_ALL
- Convert if term is any of the above, except for
CVT_VARIABLE
andCVT_WRITE*
. - CVT_EXCEPTION
- If conversion fails due to a type error, raise a Prolog type error exception in addition to failure
- BUF_DISCARDABLE
- Data must copied immediately
- BUF_STACK
- Data is stored on a stack. The older
BUF_RING
is an alias forBUF_STACK
. See section 12.4.12. - BUF_MALLOC
- Data is copied to a new buffer returned by PL_malloc(3). When no longer needed the user must call PL_free() on the data.
- REP_ISO_LATIN_1
- Text is in ISO Latin-1 encoding and the call fails if text cannot be represented. This flag has the value 0 and is thus the default.
- REP_UTF8
- Convert the text to a UTF-8 string. This works for all text.
- REP_MB
- Convert to default locale-defined 8-bit string. Success depends on the locale. Conversion is done using the wcrtomb() C library function.
- int PL_get_list_chars(+term_t l, char **s, unsigned flags)
- Same as
PL_get_chars(l, s, CVT_LIST|flags)
, provided flags contains none of theCVT_*
flags. - int PL_get_integer(+term_t t, int *i)
- If t is a Prolog integer, assign its value over i. On 32-bit machines, this is the same as PL_get_long(), but avoids a warning from the compiler. See also PL_get_long().
- int PL_get_long(term_t +t, long *i)
- If t is a Prolog integer that can be represented as a long,
assign its value over i. If t is an integer that
cannot be represented by a C long, this function returns
FALSE
. If t is a floating point number that can be represented as a long, this function succeeds as well. See also PL_get_int64(). - int PL_get_int64(term_t +t, int64_t *i)
- If t is a Prolog integer or float that can be represented as
a
int64_t
, assign its value over i. - int PL_get_intptr(term_t +t, intptr_t *i)
- Get an integer that is at least as wide as a pointer. On most platforms this is the same as PL_get_long(), but on Win64 pointers are 8 bytes and longs only 4. Unlike PL_get_pointer(), the value is not modified.
- int PL_get_bool(term_t +t, int *val)
- If t has the value
true
orfalse
, set val to the C constantTRUE
orFALSE
and return success, otherwise return failure. - int PL_get_pointer(term_t +t, void **ptr)
- In the current system, pointers are represented by Prolog integers, but need some manipulation to make sure they do not get truncated due to the limited Prolog integer range. PL_put_pointer() and PL_get_pointer() guarantee pointers in the range of malloc() are handled without truncating.
- int PL_get_float(term_t +t, double *f)
- If t is a float, integer or rational number, its value is assigned over f. Note that if t is an integer or rational conversion may fail because the number cannot be represented as a float.
- int PL_get_functor(term_t +t, functor_t *f)
- If t is compound or an atom, the Prolog representation of the name-arity pair will be assigned over f. See also PL_get_name_arity() and PL_is_functor().
- int PL_get_name_arity(term_t +t, atom_t *name, size_t *arity)
- If t is compound or an atom, the functor name will be assigned over name and the arity over arity. See also PL_get_functor() and PL_is_functor(). See section 12.3.2.1.
- int PL_get_compound_name_arity(term_t +t, atom_t *name, size_t *arity)
- If t is compound term, the functor name will be assigned over name and the arity over arity. This is the same as PL_get_name_arity(), but this function fails if t is an atom.
- int PL_get_module(term_t +t, module_t *module)
- If t is an atom, the system will look up or create the corresponding module and assign an opaque pointer to it over module.
- int PL_get_arg(size_t index, term_t +t, term_t -a)
- If t is compound and index is between 1 and arity (inclusive), assign a with a term reference to the argument.
- int _PL_get_arg(size_t index, term_t +t, term_t -a)
- Same as PL_get_arg(), but no checking is performed, neither whether t is actually a term nor whether index is a valid argument index.
- int PL_get_dict_key(atom_t key, term_t +dict, term_t -value)
- If dict is a dict, get the associated value in value. Fails silently if key does not appear in dict and with an exception if dict is not a dict.
12.4.3.3 Exchanging text using length and string
All internal text representation in SWI-Prolog is represented using
char *
plus length and allow for 0-bytes in them.
The foreign library supports this by implementing a *_nchars() function
for each applicable *_chars() function. Below we briefly present the
signatures of these functions. For full documentation consult the
*_chars() function.
- int PL_get_atom_nchars(term_t t, size_t *len, char **s)
- See PL_get_atom_chars().
- int PL_get_list_nchars(term_t t, size_t *len, char **s)
- See PL_get_list_chars().
- int PL_get_nchars(term_t t, size_t *len, char **s, unsigned int flags)
- See PL_get_chars().
- int PL_put_atom_nchars(term_t t, size_t len, const char *s)
- See PL_put_atom_chars().
- int PL_put_string_nchars(term_t t, size_t len, const char *s)
- See PL_put_string_chars().
- int PL_put_list_ncodes(term_t t, size_t len, const char *s)
- See PL_put_list_codes().
- int PL_put_list_nchars(term_t t, size_t len, const char *s)
- See PL_put_list_chars().
- int PL_unify_atom_nchars(term_t t, size_t len, const char *s)
- See PL_unify_atom_chars().
- int PL_unify_string_nchars(term_t t, size_t len, const char *s)
- See PL_unify_string_chars().
- int PL_unify_list_ncodes(term_t t, size_t len, const char *s)
- See PL_unify_codes().
- int PL_unify_list_nchars(term_t t, size_t len, const char *s)
- See PL_unify_list_chars().
In addition, the following functions are available for creating and inspecting atoms:
- atom_t PL_new_atom_nchars(size_t len, const char *s)
- Create a new atom as PL_new_atom(),
but using the given length and characters. If len is
(size_t)-1
, it is computed from s using strlen(). - const char * PL_atom_nchars(atom_t a, size_t *len)
- Extract the text and length of an atom.
12.4.3.4 Wide-character versions
Support for exchange of wide-character strings is still under
consideration. The functions dealing with 8-bit character strings return
failure when operating on a wide-character atom or Prolog string object.
The functions below can extract and unify both 8-bit and wide atoms and
string objects. Wide character strings are represented as C arrays of
objects of the type pl_wchar_t
, which is guaranteed to be
the same as wchar_t
on platforms supporting this type. For
example, on MS-Windows, this represents 16-bit UCS2 characters, while
using the GNU C library (glibc) this represents 32-bit UCS4 characters.
- atom_t PL_new_atom_wchars(size_t len, const pl_wchar_t *s)
- Create atom from wide-character string as PL_new_atom_nchars()
does for ISO-Latin-1 strings. If s only contains ISO-Latin-1
characters a normal byte-array atom is created. If len is
(size_t)-1
, it is computed from s using wcslen(). - pl_wchar_t* PL_atom_wchars(atom_t atom, int *len)
- Extract characters from a wide-character atom. Succeeds on any atom marked as‘text'. If the underlying atom is a wide-character atom, the returned pointer is a pointer into the atom structure. If it is an ISO-Latin-1 character, the returned pointer comes from Prolog's‘buffer ring' (see PL_get_chars()).
- int PL_get_wchars(term_t t, size_t *len, pl_wchar_t **s, unsigned flags)
- Wide-character version of PL_get_chars(). The flags argument is the same as for PL_get_chars().
- int PL_unify_wchars(term_t t, int type, size_t len, const pl_wchar_t *s)
- Unify t with a textual representation of the C wide-character
array s. The type argument defines the Prolog
representation and is one of
PL_ATOM
,PL_STRING
,PL_CODE_LIST
orPL_CHAR_LIST
. - int PL_unify_wchars_diff(term_t +t, term_t -tail, int type, size_t len, const pl_wchar_t *s)
- Difference list version of PL_unify_wchars(),
only supporting the types
PL_CODE_LIST
andPL_CHAR_LIST
. It serves two purposes. It allows for returning very long lists from data read from a stream without the need for a resizing buffer in C. Also, the use of difference lists is often practical for further processing in Prolog. Examples can be found inpackages/clib/readutil.c
from the source distribution.
12.4.3.5 Reading a list
The functions from this section are intended to read a Prolog list from C. Suppose we expect a list of atoms; the following code will print the atoms, each on a line:
foreign_t pl_write_atoms(term_t l) { term_t head = PL_new_term_ref(); /* the elements */ term_t list = PL_copy_term_ref(l); /* copy (we modify list) */ while( PL_get_list(list, head, list) ) { char *s; if ( PL_get_atom_chars(head, &s) ) Sprintf("%s\n", s); else PL_fail; } return PL_get_nil(list); /* test end for [] */ }
Note that as of version 7, lists have a new representation unless the option --traditional is used. see section 5.1.
- int PL_get_list(term_t +l, term_t -h, term_t -t)
- If l is a list and not the empty list, assign a term reference to the head to h and to the tail to t.
- int PL_get_head(term_t +l, term_t -h)
- If l is a list and not the empty list, assign a term reference to the head to h.
- int PL_get_tail(term_t +l, term_t -t)
- If l is a list and not the empty list, assign a term reference to the tail to t.
- int PL_get_nil(term_t +l)
- Succeeds if l represents the list termination constant.
- int PL_skip_list(term_t +list, term_t -tail, size_t *len)
- This is a multi-purpose function to deal with lists. It allows for
finding the length of a list, checking whether something is a list, etc.
The reference tail is set to point to the end of the list,
len is filled with the number of list-cells skipped, and the
return value indicates the status of the list:
- PL_LIST
- The list is a‘proper' list: one that ends in the list terminator constant and tail is filled with the terminator constant.
- PL_PARTIAL_LIST
- The list is a‘partial' list: one that ends in a variable and tail is a reference to this variable.
- PL_CYCLIC_TERM
- The list is cyclic (e.g. X = [a|X]). tail points to an arbitrary cell of the list and len is at most twice the cycle length of the list.
- PL_NOT_A_LIST
- The term list is not a list at all. tail is bound to the non-list term and len is set to the number of list-cells skipped.
It is allowed to pass 0 for tail and
NULL
for len.
12.4.3.6 An example: defining write/1 in C
Figure 6 shows a simplified definition of write/1 to illustrate the described functions. This simplified version does not deal with operators. It is called display/1, because it mimics closely the behaviour of this Edinburgh predicate.
foreign_t pl_display(term_t t) { functor_t functor; int arity, len, n; char *s; switch( PL_term_type(t) ) { case PL_VARIABLE: case PL_ATOM: case PL_INTEGER: case PL_FLOAT: PL_get_chars(t, &s, CVT_ALL); Sprintf("%s", s); break; case PL_STRING: PL_get_string_chars(t, &s, &len); Sprintf("\"%s\"", s); break; case PL_TERM: { term_t a = PL_new_term_ref(); PL_get_name_arity(t, &name, &arity); Sprintf("%s(", PL_atom_chars(name)); for(n=1; n<=arity; n++) { PL_get_arg(n, t, a); if ( n > 1 ) Sprintf(", "); pl_display(a); } Sprintf(")"); break; default: PL_fail; /* should not happen */ } } PL_succeed; }