Go to the first, previous, next, last section, table of contents.


Foreign Language Interface Guidelines and Usage

Author(s): Jose Morales, Manuel Carro.

Version: 1.7#119 (2001/8/28, 15:39:1 CEST)

Version of last change: 1.7#76 (2001/3/26, 18:8:57 CEST)

Ciao Prolog provides a high-level, flexible way to interface C and Prolog, based on the use of assertions to declare what are the expected types and modes of the arguments of a Prolog predicate, and which C files contain the corresponding code. I.e., the user must provide:

The Ciao Prolog compiler analyzes the Prolog code written by the user and gathers this information to generate automatically C code implementing the data translation between Prolog and C, and to compile the C code into dynamically loadable C object files, which are loaded automatically when needed.

To this end, each predicate implemented as a foreign C function must appear in the Ciao Prolog module as:

     :- true pred prolog_predicate(m1(Arg1), ... mN(ArgN)) :: type1 *
          ... * typeN + (foreign(foreign_function_name), returns(ArgR)).

     :- impl_defined([..., prolog_predicate/N, ...]).

where m1, ..., mN and type1, ..., typeN are respectively the modes and types of the arguments. foreign_function_name is the name of the C function implementing prolog_predicate/N, and the result of this function is unified with ArgR.

This notation can be simplified in several ways. If the name of the foreign function is the same as the name of the Ciao Prolog predicate, foreign(foreign_function_name) can be replaced by foreign/0. returns(ArgR) specifies that the result of the function corresponds to the ArgR argument of the Ciao Prolog predicate. If the foreign function does not return anything (or if its value is ignored), then returns(ArgR) must be removed. Note that returns cannot be used without foreign. A simplified form is thus:

     :- true pred prolog_predicate(m1(Arg1), ... mN(ArgN)) :: type1 *
          ... * typeN + foreign.

Equivalence between Ciao Prolog and C types

The translation between Ciao Prolog and C types is defined at the moment only for some simple, but very useful, types. The translation to be performed is solely defined by types of the arguments in the Ciao Prolog file (i.e., no inspection of the corresponding C file is done), obeying the table below. The Ciao Prolog types understood by the interface are contained in the package section Foreign Language Interface Properties, and are the following (Prolog types are on the left, and the corresponding C types on the right):

Strings, atoms, and lists of bytes are passed to (and from) C as dynamically (malloc) created arrays of characters (bytes). Those arrays are freed by Ciao Prolog upon return of the foreign function unless do_not_free/2 is specified (see examples below). This caters for the case in which the C files save in a private state (either by themselves, or by library being called by them) the values passed on from Prolog.

Empty lists of bytes are converted into NULL, and strings [] or atoms " are converted into "" (a zero ended C string). NULL is converted into an empty list or the " atom.

NULL strings and empty buffers are converted into the empty list or the null atom ("). Empty Ciao Prolog strings (" or []) and null atoms are converted into zero-terminated strings of zero length. Empty Ciao Prolog lists are converted into NULL.

The type byte_list/1 requires an additional property, size_of/3, to indicate which argument represents its size.

Most of the work is performed by the predicates in the Foreign Language Interface Builder, which can be called explicitly by the user. Doing that is not usually needed, since the Ciao Prolog Compiler takes care of building glue code files an of compiling and linking whatever is necessary.

Equivalence between Ciao Prolog and C modes

The in/1 or (prefix) +/1 mode states that the corresponding argument is given a value in Prolog, and therefore it is an input argument in the C part. The go/1 or (prefix) -/1 mode states that the Prolog side expects the C side to generate a value for that argument. The return value of a function can always be used as an output argument. Other output arguments should appear as pointers to the corresponding base type in the C files. E.g., an argument which is an integer generated by the C file, declared as

:- true pred get_int(go(ThisInt)) :: int + foreign

or as

:- true pred get_int(+ThisInt) :: int + foreign

should appear in the C code as

void get_int(int *thisint)
{
        ....
}

The examples below illustrate this point, and the use of several assertions to guide the compilation.

Examples

Mathematical functions

The mathematical library is accessed to provide the sin, cos, and fabs functions. Note that that library is specified simply as

:- use_foreign_library([m]).

The foreign interface adds the -lm at compile time. Note also how some additional options are added to optimize the compiled code (only glue code, in this case) and mathematics (only in the case of Linux in an Intel processor).

The functions imported from the C file, and exported as predicates by the Prolog module are stated as defined elsewhere by the directive

:- impl_defined([sin/2,cos/2,fabs/2]).

so that the Prolog compiler does not complain when examining the Prolog file.

math.pl

:- module(math,
	[sin/2,
	 cos/2,
	 fabs/2
	],
	[assertions,
	 basicmodes,
	 regtypes,
	 foreign_interface
	]).

:- true pred sin(in(X),go(Y)) :: num * num + (foreign,returns(Y)).
:- true pred cos(in(X),go(Y)) :: num * num + (foreign,returns(Y)).
:- true pred fabs(in(X),go(Y)) :: num * num + (foreign,returns(Y)).

:- extra_compiler_opts(['-O2']).
:- extra_compiler_opts('LINUXi86',['-ffast-math']).
:- use_foreign_library([m]).

:- impl_defined([sin/2,cos/2,fabs/2]).

Addresses and C pointers

The address type designates any pointer, and provides a means to deal with C pointers in Prolog without interpreting them whatsoever. The C source file which implements the operations accessed from Prolog is declared with the

:- use_foreign_source(objects_c).

directive.

objects.pl

:- module(objects,
	[object/2,
	 show_object/1
	],
	[assertions,
	 basicmodes,
	 regtypes,
	 foreign_interface
	]).
	 
:- true pred object(in(N),go(Object)) :: int * address +
	(foreign,returns(Object)).

:- true pred show_object(in(Object)) :: address + foreign.

:- use_foreign_source(objects_c).
:- extra_compiler_opts('-O2').

:- impl_defined([object/2,show_object/1]).

objects_c.c

#include <stdio.h>

struct object {
  char *name;
  char *colour;
};

#define OBJECTS 3

struct object objects[OBJECTS] =
{ {"ring","golden"},
  {"table","brown"},
  {"bottle","green"} };

struct object *object(int n) {
  return &objects[n % OBJECTS];
}

void show_object(struct object *o) {
  printf("I show you a %s %s\n", o->colour, o->name);
}

Lists of bytes and buffers

A list of bytes (c.f., a list of ints) corresponds to a buffer in C. The length of the buffer always goes associated to the list using the property size_of/2. The returned buffer is freed by Ciao Prolog upon its recepction, unless the do_not_free/1 property is specified (see later). Conversely, a list of natural numbers not exceeding 255 can be passed to C as a buffer.

byte_lists.pl

:- module(byte_lists,
	[obtain_list/3,
	 show_list/2
	],
	[assertions,
	 basicmodes,
	 regtypes,
	 foreign_interface
	]).
	 
:- true pred obtain_list(in(N),go(Length),go(List)) :: int * int * byte_list
	+ (foreign,size_of(List,Length)).
:- true pred show_list(in(Length),in(List)) :: int * byte_list
	+ (foreign,size_of(List,Length)).

:- use_foreign_source(bytes_op).

:- impl_defined([obtain_list/3,show_list/2]).

bytes_op.c

#include <malloc.h>
#include <stdio.h>

void obtain_list(int n, int *l, char **s) {
  int i;
  int c;
  if (n < 0) n = 0;
  *l = n;
  *s = (char *)malloc(*l);
  for (i = 0; i < *l; i++) {
    (*s)[i] = i;
  }
}

void show_list(int l, char *s) {
  if (s) {
    int n;
    printf("From C: [");
    for (n = 0; n < l; n++) {
      printf(" %d", s[n]);
    }
    printf("]\n");
  } else {
    printf("From C: []\n");
  }
}

Lists of integers

int_lists.pl

:- module(int_lists,
	[obtain_list/3,
	 show_list/2
	],
	[assertions,
	 basicmodes,
	 regtypes,
	 foreign_interface
	]).
	 
:- true pred obtain_list(in(N),go(Length),go(List)) :: int * int * int_list
	+ (foreign,size_of(List,Length)).
:- true pred show_list(in(Length),in(List)) :: int * int_list
	+ (foreign,size_of(List,Length)).

:- use_foreign_source(ints_op).

:- impl_defined([obtain_list/3,show_list/2]).

ints_op.c

#include <malloc.h>
#include <stdio.h>

void obtain_list(int n, int *l, int **s) {
  int i;
  int c;
  if (n < 0) n = 0;
  *l = n;
  *s = (int *)malloc((*l) * sizeof(int));
  for (i = 0; i < *l; i++) {
    (*s)[i] = i;
  }
}

void show_list(int l, int *s) {
  if (s) {
    int n;
    printf("From C:");
    for (n = 0; n < l; n++) {
      printf(" %d", s[n]);
    }
    printf(".\n");
  } else {
    printf("From C: []\n");
  }
}

Strings and atoms

A C string can be seen as a buffer whose end is denoted by the trailing zero, and therefore stating its length is not needed. Two translations are possible into Ciao Prolog: as a Prolog string (list of bytes, with no trailing zero) and as an atom. These are selected automatically just by choosing the corresponding type (look at the examples below).

Note how the do_not_free/1 property is specified in the a_string/1 predicate: the string returned by C is static, and therefore it should not be freed by Prolog. strings_and_atoms.pl

:- module(strings_and_atoms,
	[lookup_string/2,
	 lookup_atom/2,
	 a_string/1,
	 show_string/1,
	 show_atom/1
	],
	[assertions,
	 basicmodes,
	 regtypes,
	 foreign_interface
	]).

:- true pred a_string(go(S)) ::
	string + (foreign(get_static_str),returns(S),do_not_free(S)).
	 
:- true pred lookup_string(in(N),go(S)) ::
	int * string + (foreign(get_str),returns(S)).
:- true pred lookup_atom(in(N),go(S)) ::
	int * atm + (foreign(get_str),returns(S)).

:- true pred show_string(in(S)) :: string + foreign(put_str).
:- true pred show_atom(in(S)) :: atm + foreign(put_str).

:- use_foreign_source(str_op).

:- impl_defined([lookup_string/2,lookup_atom/2,show_string/1,show_atom/1,
                 a_string/1]).

str_op.c

#include <malloc.h>
#include <stdio.h>

char *get_static_str() {
  return "this is a string Ciao Prolog should not free";
}

char *get_str(int n) {
  char *s;
  int size;
  int i;
  int c;
  if (n < 0) n = -n;
  size = (n%4) + 5;
  s = (char *)malloc(size+1);
  for (i = 0, c = ((i + n) % ('z' - 'a' + 1)) + 'a'; i < size; i++,c++) {
    if (c > 'z') c = 'a'; 
    s[i] = c;
  }
  s[i] = 0;
  return s;
}

void put_str(char *s) {
  if (s) {
    printf("From C: \"%s\"\n", s);
  } else {
    printf("From C: null\n");
  }
}

Usage and interface (foreign_interface)

Known bugs and planned improvements (foreign_interface)


Go to the first, previous, next, last section, table of contents.