Up Next

9.1  Calling C from Prolog

9.1.1  Introduction

This interface allows a Prolog predicate to call a C function. Here are some features of this facility: This interface can then be used to write both simple and complex C routines. A simple routine uses either input or output arguments which type is simple. In that case the user does not need any knowledge of Prolog data structures since all Prolog ↔ C data conversions are implicitly achieved. To manipulate complex terms (lists, structures) a set of functions is provided. Finally it is also possible to write non-deterministic C code.

9.1.2  foreign/2 directive

foreign/2 directive (section 6.1.14) declares a C function interface. The general form is foreign(Template, Options) which defines an interface predicate whose prototype is Template according to the options given by Options. Template is a callable term specifying the type/mode of each argument of the associated Prolog predicate.

Foreign options: Options is a list of foreign options. If this list contains contradictory options, the rightmost option is the one which applies. Possible options are: foreign(Template) is equivalent to foreign(Template, []).

Foreign modes and types: each argument of Template specifies the foreign mode and type of the corresponding argument. This information is used to check the type of effective arguments at run-time and to perform Prolog ↔ C data conversions. Each argument of Template is formed with a mode symbol followed by a type name. Possible foreign modes are: Possible foreign types are:
Foreign type Prolog type C type Description of the C type
integer integer long value of the integer
positive positive integer long value of the integer
float floating point number double value of the floating point number
number number double value of the number
atom atom int internal key of the atom
boolean boolean int value of the boolean (0=false, 1=true)
char character int value of (the code of) the character
code character code int value of the character-code
byte byte int value of the byte
in_char in-character int value of the character or -1 for end-of-file
in_code in-character code int value of the character-code or -1 for end-of-file
in_byte in-byte int value of the byte or -1 for the end-of-file
string atom char * C string containing the name of the atom
chars character list char * C string containing the characters of the list
codes character-code list char * C string containing the characters of the list
term Prolog term PlTerm generic Prolog term

Simple foreign type: a simple type is any foreign type listed in the above tabled except term. A simple foreign type is an atomic term (character and character-code lists are in fact lists of constants). Each simple foreign type is converted to/from a C type to simplify the writing of the C function.

Complex foreign type: type foreign type term refers to any Prolog term (e.g. lists, structures...). When such an type is specified the argument is passed to the C function as a PlTerm (GNU Prolog C type equivalent to a long). Several functions are provided to manipulate PlTerm variables (section 9.2). Since the original term is passed to the function it is possible to read its value or to unify it. So the meaning of the mode symbol is less significant. For this reason it is possible to omit the mode symbol. In that case term is equivalent to +term.

9.1.3  The C function

The C code is written in a C file which must first include the GNU Prolog header file called gprolog.h. This file contains all GNU Prolog C definitions (constants, types, prototypes,...).

The type returned by a C function depends on the value of the return foreign option (section 9.1.2). If it is boolean then the C function is of type Bool and shall return TRUE in case of success and FALSE otherwise. If the return option is none the C function is of type void. Finally if it is jump, the function shall return the address of a Prolog predicate and, at the exit of the function, the control is given to that predicate.

The type of the arguments of the C function depends on the mode and type declaration specified in Template for the corresponding argument as explained in the following sections.

9.1.4  Input arguments

An input argument is tested at run-time to check if its type conforms to the foreign type and then it is passed to the C function. The type of the associated C argument is given by the above table (section 9.1.2). For instance, the effective argument Arg associated with +positive foreign declaration is submitted to the following process: When +string is specified the string passed to the function is the internal string of the corresponding atom and should not be modified.

When +term is specified the term passed to the function is the original Prolog term. It can be read and/or unified. It is also the case when term is specified without any mode symbol.

9.1.5  Output arguments

An output argument is tested at run-time to check if its type conforms to the foreign type and it is unified with the value set by the C function. The type of the associated C argument is a pointer to the type given by the above table (section 9.1.2). For instance, the effective argument Arg associated with -positive foreign declaration is handled as follows: When -term is specified, the function must construct a term into the its corresponding argument (which is of type PlTerm *). At the exit of the function this term will be unified with the actual predicate argument.

9.1.6  Input/output arguments

Basically an input/output argument is treated as in input argument if it is not a variable, as an output argument otherwise. The type of the associated C argument is a pointer to a FIOArg (GNU Prolog C type) defined as follows:
typedef struct
    {
     Bool is_var;
     Bool unify;
     union
        {
         long   l;
         char  *s;
         double d;
        }value;
    }FIOArg;
The field is_var is set to TRUE if the argument is a variable and FALSE otherwise. This value can be tested by the C function to determine which treatment to perform. The field unify controls whether the effective argument must be unified at the exit of the C function. Initially unify is set to the same value as is_var (i.e. a variable argument will be unified while a non-variable argument will not) but it can be modified by the C function. The field value stores the value of the argument. It is declared as a C union since there are several kinds of value types. The field s is used for C strings, d for C doubles and l otherwise (int, long, PlTerm). if is_var is FALSE then value contains the input value of the argument with the same conventions as for input arguments (section 9.1.4). At the exit of the function, if unify is TRUE value must contain the value to unify with the same conventions as for output arguments (section 9.1.5).

For instance, the effective argument Arg associated with ?positive foreign declaration is handled as follows:

9.1.7  Writing non-deterministic C code

The interface allows the user to write non-deterministic C code. When a C function is non-deterministic, a choice-point is created for this function. When a failure occurs, if all more recent non-deterministic code are finished, the function is re-invoked. It is then important to inform Prolog when there is no more solution (i.e. no more choice) for a non-deterministic code. So, when no more choices remains the function must remove the choice-point. The interface increments a counter each time the function is re-invoked. At the first call this counter is equal to 0. This information allows the function to detect its first call. When writing non-deterministic code, it is often useful to record data between consecutive re-invocations of the function. The interface maintains a buffer to record such an information. The size of this buffer is given by choice_size(N) when using foreign/2 (section 9.1.2). This size is the number of (consecutive) longs needed by the C function. Inside the function it is possible to call the following functions/macros:
void Get_Choice_Counter(void)
TYPE Get_Choice_Buffer (TYPE)
void No_More_Choice    (void)
The function Get_Choice_Counter() returns the value of the invocation counter (0 at the first call).

The macro Get_Choice_Buffer(TYPE) returns a pointer to the buffer (casted to TYPE).

The function No_More_Choice() deletes the choice point associated with the function.

9.1.8  Example: input and output arguments

All examples presented here can be found in the ExamplesC sub-directory of the distribution, in the files examp.pl (Prolog part) and examp_c.c (C part).

Let us define a predicate first_occurrence(A, C, P) which unifies P with the position (from 0) of the first occurrence of the character C in the atom A. The predicate must fail if C does not appear in A.

In the prolog file examp.pl:
:- foreign(first_occurrence(+string, +char, -positive)).
In the C file examp_c.c:
#include <string.h>
#include "gprolog.h"

Bool
first_occurrence(char *str, long c, long *pos)
{
  char *p;

  p = strchr(str, c);
  if (p == NULL)                /* C does not appear in A */
    return FALSE;               /* fail */

  *pos = p - str;               /* set the output argument */
  return TRUE;                  /* succeed */
}
The compilation produces an executable called examp:
% gplc examp.pl examp_c.c
Examples of use:
| ?- first_occurrence(prolog, p, X).

X = 0

| ?- first_occurrence(prolog, k, X).

no

| ?- first_occurrence(prolog, A, X).
{exception: error(instantiation_error,first_occurrence/3)}

| ?- first_occurrence(prolog, 1 ,X).
{exception: error(type_error(character,1),first_occurrence/3)}

9.1.9  Example: non-deterministic code

We here define a predicate occurrence(A, C, P) which unifies P with the position (from 0) of one occurrence of the character C in the atom A. The predicate will fail if C does not appear in A. The predicate is re-executable on backtracking. The information that must be recorded between two invocations of the function is the next starting position in A to search for C.

In the prolog file examp.pl:
:- foreign(occurrence(+string, +char, -positive), [choice_size(1)]).
In the C file examp_c.c:
#include <string.h>
#include "gprolog.h"

Bool
occurrence(char *str, long c, long *pos)
{
  char **info_pos;
  char *p;

  info_pos = Get_Choice_Buffer(char **); /* recover the buffer */

  if (Get_Choice_Counter() == 0)        /* first invocation ? */
    *info_pos = str;

  p = strchr(*info_pos, c);
  if (p == NULL)                /* C does not appear */
    {
      No_More_Choice();         /* remove choice-point */
      return FALSE;             /* fail */
    }

  *pos = p - str;               /* set the output argument */
  *info_pos = p + 1;            /* update next starting pos */
  return TRUE;                  /* succeed */
}
The compilation produces an executable called examp:
% gplc examp.pl examp_c.c
Examples of use:
| ?- occurrence(prolog, o, X).
 
X = 2 ?    (here the user presses ; to compute another solution)
 
X = 4 ?    (here the user presses ; to compute another solution)
 
no    (no more solution)
 
| ?- occurrence(prolog, k, X).
 
no
In the first example when the second (the last) occurrence is found (X=4) the choice-point remains and the failure is detected only when another solution is requested (by pressing ;). It is possible to improve this behavior by deleting the choice-point when there is no more occurrence. To do this it is necessary to do one search ahead. The information stored is the position of the next occurrence. Let us define such a behavior for the predicate occurrence2/3.

In the prolog file examp.pl:
:- foreign(occurrence2(+string, +char, -positive), [choice_size(1)]).
In the C file examp_c.c:
#include <string.h>
#include "gprolog.h"

Bool
occurrence2(char *str, long c, long *pos)
{
  char **info_pos;
  char *p;

  info_pos = Get_Choice_Buffer(char **); /* recover the buffer */

  if (Get_Choice_Counter() == 0) /* first invocation ? */
    {
      p = strchr(str, c);
      if (p == NULL)            /* C does not appear at all */
        {
          No_More_Choice();     /* remove choice-point */
          return FALSE;         /* fail */
        }

      *info_pos = p;
    }
                                /* info_pos = an occurrence */
  *pos = *info_pos - str;       /* set the output argument */

  p = strchr(*info_pos + 1, c);
  if (p == NULL)                /* no more occurrence */
    No_More_Choice();           /* remove choice-point */
  else
    *info_pos = p;              /* else update next solution */

  return TRUE;                  /* succeed */
}
Examples of use:
| ?- occurrence2(prolog, l, X).
 
X = 3    (here the user is not prompted since there is no more alternative)
 
| ?- occurrence2(prolog, o, X).
 
X = 2 ?    (here the user presses ; to compute another solution)
 
X = 4    (here the user is not prompted since there is no more alternative)

9.1.10  Example: input/output arguments

We here define a predicate char_ascii(Char, Code) which converts in both directions the character Char and its character-code Code. This predicate is then similar to char_code/2 (section 7.19.4).

In the prolog file examp.pl:
:- foreign(char_ascii(?char, ?code), [fct_name('Char_Ascii')]).
In the C file examp_c.c:
#include "gprolog.h"

Bool
char_ascii(FIOArg *c, FIOArg *ascii)
{
  if (!c->is_var)               /* Char is not a variable */
    {
      ascii->unify = TRUE;      /* enforce unif. of Code */
      ascii->value.l = c->value.l; /* set Code */
      return TRUE;              /* succeed */
    }

  if (ascii->is_var)            /* Code is also a variable */
    Pl_Err_Instantiation();     /* emit instantiation_error */

  c->value.l = ascii->value.l;  /* set Char */
  return TRUE;                  /* succeed */
}
If Char is instantiated it is necessary to enforce the unification of Code since it could be instantiated. Recall that by default if an input/output argument is instantiated it will not be unified at the exit of the function (section 9.1.6). If both Char and Code are variables the function raises an instantiation_error. The way to raise Prolog errors is described later (section 9.3).

The compilation produces an executable called examp:
% gplc examp.pl examp_c.c
Examples of use:
| ?- char_ascii(a, X).

X = 97

| ?- char_ascii(X, 65).

X = 'A'

| ?- char_ascii(a, 12).

no

| ?- char_ascii(X, X).
{exception: error(instantiation_error,char_ascii/2)}

| ?- char_ascii(1, 12).
{exception: error(type_error(character,1),char_ascii/2)}

Copyright (C) 1999-2007 Daniel Diaz

Verbatim copying and distribution of this entire article is permitted in any medium, provided this notice is preserved.

More about the copyright
Up Next