Creating a foreign predicate using the lower-level foreign language interface is almost entirely a matter of writing C code. Consider the foreign module $XSBDIR/examples/XSB_calling_c/simple_foreign.[cH]. The .H file has the form:
:- export minus_one/2, my_sqrt/2, change_char/4.
:- ldoption('-lm'). % link together with the math library
When the lower level foreign language interface is used, C functions that implement foreign predicates must return values of type int. The return value is not used by a Prolog argument; rather if a non-zero is returned, the foreign predicate succeeds; a zero return value means failure.
At the C level, the function that implements the Prolog predicate must have the same name as the Prolog predicate (that is declared in the *.H file), and must have a special context parameter macro. The context parameter macro allows C functions to be used with both the single-threaded and multi-threaded engines, and are described in detail in Section 2.2.1. The Prolog level arguments are converted to C data structures through several predefined functions rather than through direct parameter passing 2.4. The C file simple_foreign.c corresponding to the above .H file is as follows.
/*----------------------------------------------------------------------*/
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <alloca.h>
/*----- Make sure your C compiler finds the following header file. -----
----- One way to do this is to include the directory XSB/emu on the -----
----- compiler's command line with the -I (/I in Windows) option -----*/
#include "cinterf.h"
/*-----------------------------------------*/
int minus_one(CTXTdecl)
{
int i = ptoc_int(CTXTc 1);
ctop_int(CTXTc 2, i-1);
return TRUE;
}
/*-----------------------------------------*/
int my_sqrt(CTXTdecl)
{
int i = ptoc_int(CTXTc 1);
ctop_float(CTXTc 2, (float) pow((double)i, 0.5));
return TRUE;
}
/*-----------------------------------------*/
int change_char(CTXTdecl)
{
char *str_in;
int pos;
int c;
char *str_out;
str_in = (char *) ptoc_string(CTXTc 1);
str_out = (char *) alloca(strlen(str_in)+1);
strcpy(str_out, str_in);
pos = ptoc_int(CTXTc (2);
c = ptoc_int(CTXTc (3);
if (c < 0 || c > 255) /* not a character */
return FALSE; /* this predicate will fail on the Prolog side */
str_out[pos-1] = c;
extern_ctop_string(CTXTc 4, str_out);
return TRUE;
}
/*----------------------------------------------------------------------*/
Before describing the C program used, here is a sample session illustrating the behavior of the predicates in simple_foreign.
XSB Version 2.0 (Gouden Carolus) of June 26, 1999
[i686-pc-linux-gnu; mode: optimal; engine: slg-wam; scheduling: batched]
| ?- [simple_foreign].
[Compiling C file ./simple_foreign.c using gcc]
[Compiling Foreign Module ./simple_foreign]
[simple_foreign compiled, cpu time used: 0.0099993 seconds]
[simple_foreign loaded]
yes
| ?- change_char('Kostis', 2, w, TempStr),
change_char(TempStr, 5, h, GrkName).
TempStr = Kwstis
GrkName = Kwsths;
no
| ?- minus_one(43, X).
X = 42;
no
| ?- minus_one(43, 42). % No output unification is allowed
Wrong arg in ctop_int 2a2 (Reg = 2)
yes
| ?- my_sqrt(4,X).
X = 2
yes
| ?- my_sqrt(23,X).
X = 4.7958;
no
Consider the function minus_one() above. As discussed, it
takes a context parameter (explained below), and returns an integer,
and as can be seen the return values can be specified by the macros
TRUE and FALSE. From the Prolog perspective the first
argument to minus_one/2 is an (integer) input argument, while
the second is an (integer) output argument. Input arguments for basic
C types are translated from their Prolog representation to a C
representation by functions of the form ptoc\_<type>() - here
ctop_int(). The single parameter of such a function is the
number of the Prolog argument that is to be transformed and the
function returns the C representation. Output arguments are converted
from C to Prolog by corresponding functions of the form
ctop_<type>() - here ctop_int(). For converting C back
to Prolog, the first parameter of ctop_int() is the number of
the Prolog argument to be transformed and the second is the C value to
be transformed. In the session output above, if an improper argument
is given to minus_one/2 it will emit a warning, and succeed.
Also note that the call my_sqrt(23,X) succeeds once, but fails
on backtracking since it is deterministic, as are all other foreign
language functions.
The above example illustrates the exchange of basic types through the lower-level interface - e.g. atoms, integers, and floating-point numbers. The lower-level interface also allows a user to pass lists and terms between XSB and C as will be discussed in Section 2.2.3.