Did you know ... | Search Documentation: |
Unifying data |
The functions of this section unify terms with other terms
or translated C data structures. Except for PL_unify(),
these functions are specific to SWI-Prolog. They have been introduced
because they shorten the code for returning data to Prolog and at the
same time make this more efficient by avoiding the need to allocate
temporary term references and reduce the number of calls to the Prolog
API. Consider the case where we want a foreign function to return the
host name of the machine Prolog is running on. Using the PL_get_*()
and PL_put_*()
functions, the code becomes:
foreign_t pl_hostname(term_t name) { char buf[100]; if ( gethostname(buf, sizeof buf) ) { term_t tmp = PL_new_term_ref(); PL_put_atom_chars(tmp, buf); return PL_unify(name, tmp); } PL_fail; }
Using PL_unify_atom_chars(), this becomes:
foreign_t pl_hostname(term_t name) { char buf[100]; if ( gethostname(buf, sizeof buf) ) return PL_unify_atom_chars(name, buf); PL_fail; }
Note that unification functions that perform multiple bindings may leave part of the bindings in case of failure. See PL_unify() for details.
TRUE
on success. PL_unify()
does not evaluate attributed variables (see section
8.1), it merely schedules the goals associated with the attributes
to be executed after the foreign predicate succeeds.221Goal
associated with attributes may be non-deterministic, which we cannot
handle from a callback. A callback could also result in deeply nested
mutual recursion between C and Prolog and eventually trigger a C stack
overflow.
Care is needed if PL_unify()
returns FALSE
and the foreign function does not immediately
return to Prolog with
FALSE
. Unification may perform multiple changes to either
t1 or t2. A failing unification may have created
bindings before failure is detected. Already created bindings are
not undone. For example, calling PL_unify()
on a(X, a)
and
a(c,b)
binds X to c
and fails when
trying to unify
a
to b
. If control remains in C or if we want
to return success to Prolog, we must undo such bindings. In
addition, PL_unify()
may have failed on an exception, typically a resource (stack)
overflow. This can be tested using PL_exception(),
passing 0 (zero) for the query-id argument. Foreign functions that
encounter an exception must return FALSE
to Prolog as soon
as possible or call PL_clear_exception()
if they wish to ignore the exception. Note that there can only be an
exception if PL_unify()
returned FALSE
.
In some scenarios we need to undo partial unifications.
Suppose we have a database that contains Prolog terms and we run a query
over this database. We must succeed on the first successful unification.
If a unification is not successful, we must stop if there is an
exception or undo the partial unification and try again. Suppose our
database contains f(a,1)
and f(b,2)
and our
query is f(A,2)
. This should succeed with A =
b
, but the first unification binds A to a
before failing to unify 1 with 2.
static foreign_t find_in_db(term_t target) { fid_t fid = PL_open_foreign_frame(); term_t candidate = PL_new_term_ref(); while(get_from_my_database(candidate)) { if ( PL_unify(candidate, target) ) /* found */ { PL_close_foreign_frame(fid); return TRUE; } else if ( PL_exception(0) ) /* error */ { PL_close_foreign_frame(fid); return FALSE; } PL_rewind_foreign_frame(fid); /* try next */ } PL_close_foreign_frame(fid); /* not found */ return FALSE; }
This code is only needed if the foreign predicate does not return
immediately to Prolog when PL_unify()
fails - there is an implicit frame around the entire predicate, and
returning FALSE
undoes all bindings when that frame is
closed.
false
or true
,
according to whether a is zero or non-zero. If t
is instantiated, off
and on
are also accepted.char*
with various
encodings to a Prolog representation. The flags argument is a
bitwise or specifying the Prolog target type and the encoding
of
chars. A Prolog type is one of PL_ATOM
, PL_STRING
,
PL_CODE_LIST
or PL_CHAR_LIST
. A representation
is one of
REP_ISO_LATIN_1
, REP_UTF8
or REP_MB
.
See
PL_get_chars()
for a definition of the representation types. If
len is -1
chars must be
zero-terminated and the length is computed from chars using strlen().
If flags includes PL_DIFF_LIST
and type is
one of
PL_CODE_LIST
or PL_CHAR_LIST
, the text is
converted to a difference list. The tail of the difference list
is
t+1.
int64_t
. If unbounded integers are not supported a
representation_error
is raised../2
). If successful,
write a reference to the head of the list into h and a
reference to the tail of the list into t. This reference to h
may be used for subsequent calls to this function. Suppose we want to
return a list of atoms from a char **
. We could use the
example described by
PL_cons_list(),
followed by a call to PL_unify(),
or we can use the code below. If the predicate argument is unbound, the
difference is minimal (the code based on PL_cons_list()
is probably slightly faster). If the argument is bound, the code below
may fail before reaching the end of the word list, but even if the
unification succeeds, this code avoids a duplicate (garbage) list and a
deep unification.
Note that PL_unify_list()
is not used with env but with
tail
, which is a copy of env. PL_copy_term_ref()
creates a copy term_t
holding the same Prolog term, i.e., not
a copy of the Prolog term. The only thing that is allowed to be done
with an argument to a foreign predicate (such as env) is
unification; for anything that might over-write the term, you must use a
copy created by PL_copy_term_ref().
The name PL_unify_list()
is slightly misleading - it unifies the first argumment (l
but
overwrites the second (h) and third (t)
arguments.
foreign_t pl_get_environ(term_t env) { term_t tail = PL_copy_term_ref(env); term_t item = PL_new_term_ref(); extern char **environ; for(const char **e = environ; *e; e++) { if ( !PL_unify_list(tail, item, tail) || !PL_unify_atom_chars(item, *e) ) PL_fail; } return PL_unify_nil(tail); }
In this example, item
is initialized outside the loop.
This allocates a single new reference to a term, which is used as a
temporary inside the loop - there is no need to allocate a new reference
each time around the loop because the item
term reference
can be reused and the call to PL_unify_list()
copies a reference to the new list cell's head into the the term
referenced by
item
.
[]
.
Special attention is required when passing numbers. C‘promotes’any
integral smaller than int
to int
. That is, the
types
char
, short
and int
are all
passed as int
. In addition, on most 32-bit platforms int
and long
are the same. Up to version 4.0.5, only PL_INTEGER
could be specified, which was taken from the stack as long
.
Such code fails when passing small integral types on machines where int
is smaller than long
. It is advised to use PL_SHORT
, PL_INT
or PL_LONG
as appropriate. Similarly, C compilers promote
float
to double
and therefore PL_FLOAT
and
PL_DOUBLE
are synonyms.
The type identifiers are:
PL_VARIABLE
nonePL_FUNCTOR
.PL_BOOL
inttrue
or false
.PL_ATOM
atom_tPL_CHARS
const char *char *
,
as in PL_unify_atom_chars().PL_NCHARS
size_t, const char *char*
as in PL_unify_atom_nchars().PL_UTF8_CHARS
const char *PL_UTF8_STRING
const char *PL_MBCHARS
const char *PL_MBCODES
const char *PL_MBSTRING
const char *PL_NWCHARS
size_t, const wchar_t *PL_NWCODES
size_t, const wchar_t *PL_NWSTRING
size_t, const wchar_t *PL_SHORT
shortshort
is promoted to int
, PL_SHORT
is a synonym for PL_INT
.PL_INTEGER
longPL_INT
intPL_LONG
longPL_INT64
int64_tPL_INTPTR
intptr_tPL_LONG
. but on 64-bit
MS-Windows pointers are 64 bits while longs are only 32 bits.PL_DOUBLE
doublePL_FLOAT
doublePL_POINTER
void *PL_STRING
const char *PL_TERM
term_tPL_FUNCTOR
functor_t, ...PL_FUNCTOR_CHARS
const char *name, int arity,
...PL_FUNCTOR
.PL_LIST
int length, ...
For example, to unify an argument with the term language(dutch)
,
the following skeleton may be used:
static functor_t FUNCTOR_language1; static void init_constants() { FUNCTOR_language1 = PL_new_functor(PL_new_atom("language"),1); } foreign_t pl_get_lang(term_t r) { return PL_unify_term(r, PL_FUNCTOR, FUNCTOR_language1, PL_CHARS, "dutch"); } install_t install() { PL_register_foreign("get_lang", 1, pl_get_lang, 0); init_constants(); }
FALSE
if a syntax error was encountered and TRUE
after successful
completion. In addition to returning FALSE
, the
exception-term is returned in t on a syntax error. See also term_to_atom/2.
The following example builds a goal term from a string and calls it.
int call_chars(const char *goal) { fid_t fid = PL_open_foreign_frame(); term_t g = PL_new_term_ref(); BOOL rval; if ( PL_chars_to_term(goal, g) ) rval = PL_call(goal, NULL); else rval = FALSE; PL_discard_foreign_frame(fid); return rval; } ... call_chars("consult(load)"); ...
PL_chars_to_term() is defined using PL_put_term_from_chars() which can deal with not null-terminated strings as well as strings using different encodings:
int PL_chars_to_term(const char *s, term_t t) { return PL_put_term_from_chars(t, REP_ISO_LATIN_1, (size_t)-1, s); }
'\''
, the result is a quoted atom. If chr is
'"'
, the result is a string. The result string is stored in
the same ring of buffers as described with the BUF_STACK
argument of PL_get_chars();
In the current implementation, the string is surrounded by chr and any occurrence of chr is doubled. In the future the behaviour will depend on the character_escapes Prolog flag.
0
value, the iteration
stops and PL_for_dict()
returns that value; otherwise, all pairs are processed and PL_for_dict()
returns 0
. If
flags contains PL_FOR_DICT_SORTED
, the key-value
pairs are processed in the standard order of terms; otherwise the
processing order is unspecified.