C Foreign Language interface

Author(s): Jose F. Morales, Manuel Carro.

Ciao includes a high-level, flexible way to interface C and Prolog, based on the use of assertions to declare the expected types and modes of the arguments of a Prolog predicate, and which C files contain the corresponding code. To this end, the user provides:

  • A set of C files, or a precompiled shared library,
  • A Ciao module defining which predicates are implemented in the C files and the types and modes of their arguments, and
  • an (optional) set of flags required for the compilation of the files.

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

Declaration of Foreign Sources

The compilation and dynamic linking of foreign sources is controled by the following declarations:

More details about these options can be found in Foreign Language Interface Properties.

The compilation of the foreign sources together with the glue code is decomposed into two phases:

  • The Compilation phase: Each C (or C++) file <SrcFile> is compiled into an object file <ObjFile> by a command of the form:
    $ <Compiler> <CompilerOpts> -c <ExtraCompilerOptions> -o <ObjectFile> <SrcFile>
    where <Compiler> is the compiler command, <CompilerOpts> are some Ciao-specific compiler options, and <ExtraCompilerOptions> are the additional compiler options specified by the declaration(s) extra_compiler_opts/1.

  • The Linking phase: All the object files <ObjectFiles> obtained by the compilation phase are linked together with the external libray(ies) into a dynamic library <SOLib> by a command of the form:
     
    $ <Linker> <CompilerOpts> -o <SOLib> <ObjectFiles> <ExternalLibs> <ExtraLinkerOptions>
    where <Linker> is the linker command, <LinkerOpts> are some Ciao-specific linker options, <ExternalLibs> are the foreign libraries in order they are declared, and <ExtraLinkerOptions> are the additional linker options specified by the declaration(s) extra_linker_opts/1.

Note that for the linking phase the order of libraries may be important. See https://gcc.gnu.org/onlinedocs/gcc/Link-Options.html or https://sourceware.org/binutils/docs-2.24/ld/Options.html#Options for more details.

Once the foreign sources are compiled and linked together, the resulting library is dynamically loaded using the semantics of the RTLD_LOCAL flag of the POSIX dlopen() function (see the dlopen man page for additional details). In other words, the symbols defined in this dynamic library are not made available to resolve references in subsequently foreign loaded libraries. In particular the foreign sources cannot depend on previously loaded foreign sources.

Declaring Types and Modes

Each predicate implemented as a foreign C function must have accompanying declarations in the Ciao associated file stating the types and modes of the C function. A sample declaration for prolog_predicate which is implemented as foreign_function_name is:

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

where

This notation can be simplified in several ways:

  • If the name of the foreign function is the same as the name of the Ciao predicate, foreign(foreign_function_name) can be replaced by foreign.

  • 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, minimal form is thus:

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

Equivalence between Ciao and C types

The automatic translation between Ciao and C types is defined for some simple but useful Prolog types (like atoms and strings) and for a collection of reserved Prolog types (ctypes) that reflect C types.

The names (and meaning) of the types known for performing that translation are to be found in Foreign Language Interface Properties; they are also summarized below (Prolog types are listed first and the corresponding C types after them):

  • c_short, c_ushort, c_int, c_uint, c_long, c_ulong, c_uintptr, c_size

    correspond to the C types:

    short, unsigned short, int, unsigned int, long, unsigned long, uintptr_t, size_t

    (be aware that the ranges of all types depend on the C data model, which differs in, e.g., 32 and 64-bit architectures).

  • c_int8, c_uint8, c_int16, c_uint16, c_int32, c_uint32, c_int64, c_uint64

    correspond to the C types:

    int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t.

  • atm corresponds to C's 'char *' (with trailing zero).
  • string corresponds to C's 'char *' (with trailing zero).

  • X_list corresponds to C's 'T *' (an array of some ctype X with C type T, with associated length).

  • address corresponds to C's 'void *'.

Strings, atoms, and lists of bytes are passed to (and from) C as dynamically (ciao_malloc) created arrays of characters (bytes). Those arrays are freed by Ciao upon return of the foreign function unless the property 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 a library function being called by them) the values passed on from Prolog. The type X_list/1 requires an additional property, size_of/2, to indicate which argument represents its size.

Empty lists of bytes and integers are converted into C NULL pointers, and vice-versa. Empty strings ([]) and null atoms ('') are converted into zero-length, zero-ended C strings (""). C NULL strings and empty arrays (i.e., arrays with zero length) are transformed into the empty list or the null atom ('').

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 Compiler takes care of building glue code files an of compiling and linking whatever is necessary.

The translation to be performed is solely defined by the types of the arguments in the Ciao module (i.e., no inspection of the corresponding C file is done).

An incorrect selection of types may generate wrong C prototypes. Those problems are silently ignored during compilation.

Equivalence between Ciao and C modes

The (prefix) +/1 ISO mode (or, equivalently, the in/1 mode) states that the corresponding Prolog argument is ground at the time of the call, and therefore it is an input argument in the C part; this groundness is automatically checked upon entry. The (prefix) -/1 ISO mode (or, equivalently, the go/1 mode) states that Prolog expects the C side to generate a (ground) value for that argument. Arguments with output mode should appear in C functions as pointers to the corresponding base type (as it is usual with C), i.e., an argument which is an integer generated by the C file, declared as:

:- trust pred get_int(go(ThisInt)) :: c_int + foreign.

or as:

:- trust pred get_int(-ThisInt) :: c_int + foreign.

should appear in the C code as

void get_int(int *thisint) {
    ....
}
Note the type of the (single) argument of the function. Besides, the return value of a function can always be used as an output argument, just by specifying to which Prolog arguments it corresponds, using the returns/1 property. The examples below illustrate this point, and the use of several assertions to guide the compilation process.

Custom access to Prolog from C

Automatic type conversion does not cover all the possible cases. When the automatic type conversion is not enough (or if the user, for any reason, does not want to go through the automatic conversion), it is possible to instruct Ciao not to make implicit type conversion. The strategy in that case is to pass the relevant argument(s) with a special type (a ciao_term) which can represent any term which can be built in Prolog. Operations to construct, traverse, and test this data abstraction from C are provided. The prototypes of these operations are placed on the "ciao_prolog.h" file, under the include subdirectory of the installation directory (the Ciao compiler knowns where it has been installed, and gives the C compiler the appropriate flags). This non direct correspondence mode is activated whenever a Ciao Prolog type unknown to the foreign interface (i.e., none of those in Foreign Language Interface Properties) or the type any_term (which is explicitly recognized by the foreign language interface) is found. The latter is preferred, as it is much more informative, and external tools, such as the the CiaoPP preprocessor, can take advantage of them.

Term construction

All term construction primitives return an argument of type ciao_term, which is the result of constructing a term. All Ciao Prolog terms can be built using the interface operations ciao_var(), ciao_structure(), ciao_X(), etc. There are, however, variants and specialized versions of these operations which can be freely intermixed. Using one version or another is a matter of taste and convenience. We list below the prototypes of the primitives in order of complexity.

Throughout this section, true, when referred to a boolean value, corresponds to the integer value 1, and false corresponds to the integer value 0, as is customary in C boolean expressions. These values are also available as the (predefined) constants ciao_true and ciao_false, both of type ciao_bool.

  • ciao_term ciao_var();

    Returns a fresh, unbound variable.

  • ciao_term ciao_mk_X(T i);

    Creates a term of ctype X from the value of i, of the corresponding C type T.

  • ciao_term ciao_put_number_chars(char *number_string);

    It converts number_string (which must a string representing a syntactically valid number) into a ciao_term.

  • ciao_term ciao_atom(char *name);

    Creates an atom whose printable name is given as a C string.

  • ciao_term ciao_structure_a(char *name, int arity, ciao_term *args);

    Creates a structure with name name (i.e., the functor name ), arity arity and the components of the array args as arguments: args[0] will be the first argument, args[1] the second, and so on. The args array itself is not needed after the term is created, and can thus be a variable local to a procedure. An atom can be represented as a 0-arity structure (with ciao_structure(name, 0)), and a list cell can be constructed using the '.'/2 structure name. The _a suffix stands for array.

  • ciao_term ciao_structure(char *name, int arity, ...);

    Similar to ciao_structure_a, but the C arguments after the arity are used to fill in the arguments of the structure.

  • ciao_term ciao_list(ciao_term head, ciao_term tail);

    Creates a list from a head and a tail. It is equivalent to ciao_structure(".", 2, head, tail).

  • ciao_term ciao_empty_list();

    Creates an empty list. It is equivalent to ciao_atom("[]").

  • ciao_term ciao_listn_a(int len, ciao_term *args);

    Creates a list with 'len' elements from the array args. The nth element of the list (starting at 1) is args[n-1] (starting at zero).

  • ciao_term ciao_listn(size_t length, ...);

    Like ciao_listn_a(), but the list elements appear explicitly as arguments in the call.

  • ciao_term ciao_dlist_a(size_t len, ciao_term *args, ciao_term base);

    Like ciao_listn_a, but a difference list is created. base will be used as the tail of the list, instead of the empty list.

  • ciao_term ciao_dlist(size_t length, ...);

    Similar to ciao_dlist_a() with a variable number of arguments. The last one is the tail of the list.

  • ciao_term ciao_copy_term(ciao_term src_term);

    Returns a new copy of the term, with fresh variables (as copy_term/2 does).

Testing the Type of a Term

A ciao_term can contain any Prolog term, and its implementation is opaque to the C code. Therefore the only way to know reliably what data is passed on is using explicit functions to test term types. Below, ciao_bool is a type defined in "ciao_prolog.h" which can take the values 1 (for true) and 0 (for false).

  • ciao_bool ciao_is_variable(ciao_term term);

    Returns true if term is currently an uninstantiated variable.

  • ciao_bool ciao_is_number(ciao_term term);

    Returns true if term is an integer (of any length) or a floating point number.

  • ciao_bool ciao_is_integer(ciao_term term);

    Returns true if term is instantiated to an integer.

  • ciao_bool ciao_fits_in_X(ciao_term term);

    Returns true if term is instantiated to a value representable by ctype X (e.g., 10000 is not representable by c_int8 but it is for c_int32).

  • ciao_bool ciao_is_atom(ciao_term atom);

    Returns true if term is an atom.

  • ciao_bool ciao_is_list(ciao_term term);

    Returns true if term is a list (actually, a cons cell).

  • ciao_bool ciao_is_empty_list(ciao_term term);

    Returns true if term is the atom which represents the empty list (i.e., []).

  • ciao_bool ciao_is_structure(ciao_term term);

    Returns true if term is a structure of any arity. This includes atoms (i.e., structures of arity zero) and lists, but excludes variables and numbers.

Testing for Equality and Performing Unification

Variables of type ciao_term cannot be tested directly for equality: they are (currently) implemented as a sort of pointers which may be aliased (two different pointers may refer to the same object). The interface provides helper functions for testing term equality and to perform unification of terms.

  • ciao_bool ciao_unify(ciao_term x, ciao_term y);

    Performs the unification of the terms x and y, and returns true if the unification was successful. This is equivalent to calling the (infix) Prolog predicate =/2. The bindings are trailed and undone on backtracking.

  • ciao_bool ciao_equal(ciao_term x, ciao_term y);

    Performs equality testing of terms, and returns true if the test was successful. This is equivalent to calling the (infix) Prolog predicate ==/2. Equality testing does not modify the terms compared.

Raising Exceptions

The following functions offer a way of throwing exceptions from C that can be caught in Prolog with catch/3. The term that reaches Prolog is exactly the same which was thrown by C. The execution flow is broken at the point where ciao_raise_exception() is executed, and it returns to Prolog.

  • void ciao_raise_exception(ciao_term ball);

    Raises an exception an throws the term ball.

Creating and disposing of memory chunks

Memory to be used solely by the user C code can be reserved/disposed of using, e.g., the well-known malloc()/free() functions (or whatever other functions the user may have available). However, memory explicitly allocated by Ciao and passed to C code, or allocated by C code and passed on to Ciao (and subject to garbage collection by it) should be allotted and freed (when necessary) by using the functions:

  • void *ciao_malloc(int size);

  • void ciao_free(void *pointer);

whose behavior is similar to malloc()/free(), but which will cooordinate properly with Ciao's internal memory management.

Calling Prolog from C

It is also possible to make arbitrary calls to Prolog predicates from C. There are two basic ways of making a query, depending on whether only one solution is needed (or if the predicate to be called is known to generate only one solution), or if several solutions are required.

When only one solution is needed ciao_commit_call obtains it (the solution obtained will obviously be the first one) and discards the resources used for finding it:

  • ciao_bool ciao_commit_call(char *name, int arity, ...);

    Makes a call to a predicate and returns true or false depending on whether the query has succedeed or not. In case of success, the (possibly) instantiated variables are reachable from C.

  • ciao_bool ciao_commit_call_term(ciao_term goal);

    Like ciao_commit_call() but uses the previously built term goal as goal.

If more than one solution is needed, it is necessary to use the ciao_query operations. A consult begins with a ciao_query_begin which returns a ciao_query object. Whenever an additional solution is required, the ciao_query_next function can be called. The query ends by calling ciao_query_end and all pending search branches are pruned.

  • ciao_query *ciao_query_begin(char *name, int arity, ...);

    The predicate with the given name, arity and arguments (similar to the ciao_structure() operation) is transformed into a ciao_query object which can be used to make the actual query.

  • ciao_query *ciao_query_begin_term(ciao_term goal);

    Like ciao_query_begin but using the term goal instead.

  • ciao_bool ciao_query_ok(ciao_query *query);

    Determines whether the query may have pending solutions. A false return value means that there are no more solutions; a true return value means that there are more possible solutions.

  • void ciao_query_next(ciao_query *query);

    Ask for a new solution.

  • void ciao_query_end(ciao_query *query);

    Ends the query and frees the used resources.

Examples

Mathematical functions

In this example, the standard mathematical library is accessed to provide the sin, cos, and fabs functions. Note that the 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).

File math.pl:

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

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

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

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.

File objects.pl:

:- module(objects, [object/2, show_object/1], [foreign_interface]).

:- trust pred object(in(N),go(Object)) ::
    c_int * address + (foreign,returns(Object)).

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

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

File 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 arrays

A list of bytes (c.f., a list of ints) corresponds to a byte array in C. The length of the array is associated to that of the list using the property size_of/2. The returned array 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 in the range 0 to 255 can be passed to C as an array.

File byte_lists.pl:

:- module(byte_lists, [obtain_list/3, show_list/2], [foreign_interface]).
     
:- trust pred obtain_list(in(N),go(Length),go(List)) :: c_int * c_size * c_uint8_list
    + (foreign,size_of(List,Length)).
:- trust pred show_list(in(Length),in(List)) :: c_size * c_uint8_list
    + (foreign,size_of(List,Length)).

:- use_foreign_source(bytes_op).

File bytes_op.c:

#include <stdlib.h>
#include <stdio.h>

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

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

Lists of integers

File int_lists.pl:

:- module(int_lists, [obtain_list/3, show_list/2], [foreign_interface]).
     
:- trust pred obtain_list(in(N),go(Length),go(List)) :: c_size * c_size * c_int_list
    + (foreign,size_of(List,Length)).
:- trust pred show_list(in(Length),in(List)) :: c_size * c_int_list
    + (foreign,size_of(List,Length)).

:- use_foreign_source(ints_op).

File ints_op.c:

#include <stdlib.h>
#include <stdio.h>

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

void show_list(size_t 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 an array whose end is denoted by the trailing zero, and therefore stating its length is not needed. Two translations are possible into Ciao: 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.

File strings_and_atoms.pl:

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

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

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

:- use_foreign_source(str_op).

File str_op.c:

#include <stdlib.h>
#include <stdio.h>

char *get_static_str(void) {
  return "this is a string 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");
  }
}

Arbitrary Terms

This example shows how data Prolog can be passed untouched to C code, and how it can be manipulated there.

File any_term.pl:

:- module(any_term,
    [custom_display_term/1,
     custom_create_term/2
    ],
    [foreign_interface]).

:- trust pred custom_display_term(in(X)) :: any_term + foreign.
:- trust pred custom_create_term(in(L), go(X)) :: c_int * any_term + (foreign,returns(X)).

:- use_foreign_source(any_term_c).
:- extra_compiler_opts(['-O2']).

File any_term_c.c:

#include <stdio.h>
#include <ciao_prolog.h>

ciao_term custom_create_term(int n) {
  ciao_term t;
  t = ciao_empty_list();
  while (n > 0) {
    t = ciao_list(ciao_mk_c_int(n), t);
    n--;
  }
  return t;
}

void custom_display_term(ciao_term term) {
  if (ciao_is_atom(term)) {
    printf("<atom name=\"%s\"/>", ciao_atom_name(term));
  } else if (ciao_is_structure(term)) {
    int i;
    int a;
    a = ciao_structure_arity(term);
    printf("<structure name=\"%s\" arity=\"%d\">", ciao_structure_name(term), a);
    for (i = 1; i <= a; i++) {
      printf("<argument number=\"%d\">", i);
      custom_display_term(ciao_structure_arg(term, i));
      printf("</argument>");
    }
    printf("</structure>");
  } else if (ciao_is_list(term)) {
    printf("<list>");
    printf("<head>");
    custom_display_term(ciao_list_head(term));
    printf("</head>");
    printf("<tail>");
    custom_display_term(ciao_list_tail(term));
    printf("</tail>");
    printf("</list>");
  } else if (ciao_is_empty_list(term)) {
    printf("<empty_list/>");
  } else if (ciao_is_integer(term)) {
    printf("<integer value=\"%d\"/>", ciao_get_c_int(term));
  } else if (ciao_is_number(term)) {
    printf("<float value=\"%f\"/>", ciao_get_c_float(term));
  } else {
    printf("<unknown/>");
  }
}

Exceptions

The following example defines a predicate in C that converts a list of codes into a number using strtol(). If this conversion fails, then a exception is raised.

File exceptions_example.pl:

:- module(exceptions_example,
    [codes_to_number_c/2,
     safe_codes_to_number/2
    ],
    [foreign_interface]).

:- use_module(library(format)).

% If the string is not a number raises an exception.
:- trust pred codes_to_number_c(in(X), go(Y)) :: string * c_int + (foreign, returns(Y)).

safe_codes_to_number(X, Y) :-
    catch(codes_to_number_c(X, Y), Error, handle_exception(Error)).

handle_exception(Error) :- format("Exception caught ~w~n", [Error]).

:- use_foreign_source(exceptions_c).
:- extra_compiler_opts(['-O2']).

File exceptions_c.c:

#include <string.h>
#include <stdlib.h>
#include <ciao_prolog.h>

int codes_to_number_c(char *s) {
  char *endptr;
  int n;
  n = strtol(s, &endptr, 10);
  if (endptr == NULL || *endptr != '\0') {
    ciao_raise_exception(ciao_structure("codes_to_number_exception", 
                                        1,
                                        ciao_atom(s)));
  }
  return n;
}


Testing number types and using unbounded length integers

Unbounded length integers (and, in general, any number) can be converted to/from ciao_terms by using strings. The following examples show two possibilities: one which tries to be as smart as possible (checking whether numbers fit into a machine int or not), and being lazy and simpler --and probably slower.

File bigints.pl:

:- module(bigints,
    [ 
      make_smart_conversion/3, % Checks and uses convenient format
      force_string_conversion/2  % Passes around using strings
    ],
    [foreign_interface]).

:- trust pred make_smart_conversion_c(in(X), go(Y), go(How))
   :: any_term * any_term * any_term + foreign
   # "Given a number @var{X}, it is unified with @var{Y} by using the
   most specific internal representation (short integer, float, or
   long integer).  @var{How} returns how the conversion was done.  It
   behaves unpredictably if @var{X} is not a number.".

:- trust pred force_string_conversion_c(in(X), go(Y))
   :: any_term * any_term + foreign
   # "Given a number @var{X}, it is unified with @var{Y} by using the
   most general internal representation (a string of characters). It
   behaves unpredictably if @var{X} is not a number.".

:- use_foreign_source(bigints_c).

make_smart_conversion(A, B, C):-
    number(A),                              % Safety test
    make_smart_conversion_c(A, B, C).

force_string_conversion(A, B):-
    number(A),                              % Safety test
    force_string_conversion_c(A, B).

File bigints_c.c:

#include <ciao_prolog.h>

void make_smart_conversion_c(ciao_term  number_in,
                             ciao_term *number_out,
                             ciao_term *how_converted) {
  int    inter_int;
  double inter_float;
  char * inter_str;

  if (ciao_fits_in_c_int(number_in)) {/* Includes the case of being a float */
    inter_int = ciao_get_c_int(number_in);
    *number_out = ciao_mk_c_int(inter_int);
    *how_converted = ciao_atom("machine_integer");
  } else
    if (ciao_is_integer(number_in)) { /* Big number */
      inter_str   = ciao_get_number_chars(number_in);
      *number_out = ciao_put_number_chars(inter_str);
      ciao_free(inter_str);
      *how_converted = ciao_atom("string");
    } else { /* Must be a float */
      inter_float = ciao_get_c_double(number_in);
      *number_out = ciao_mk_c_double(inter_float);
      *how_converted = ciao_atom("float");
    }
}

void force_string_conversion_c(ciao_term  number_in, 
                               ciao_term *number_out) {
  char *inter_str;
  inter_str  = ciao_get_number_chars(number_in);
  *number_out = ciao_put_number_chars(inter_str);
  ciao_free(inter_str);
}

Interfacing with C++

Ciao code can be interfaced easily with C++ using this interface. The basic idea is to write C functions (functions prefixed by 'extern "C"') within the C++ code to make the bridge between calls from Ciao to C++. Then, C++ objects can be cast as addresses. Because the foreign interface assumes that the foreign source is classical C, C++ source files should be declared with their extension.

File cc_stack.pl:

:- module(cc_stack, [cc_stack_new/1, cc_stack_size/2, 
                   cc_stack_push/2, cc_stack_pop/1, cc_stack_top/2], 
                  [foreign_interface, assertions]).

:- use_module(library(odd), [undo/1]).
    
:- trust pred ciao_stack_new(go(Stack)) :: address 
    + (foreign, returns(Stack)).
:- trust pred ciao_stack_delete(in(_Stack)) :: address 
    + foreign.
:- trust pred ciao_stack_size(in(_Stack), go(Size)) ::  (address * c_int)  
    + (foreign, returns(Size)).
:- trust pred ciao_stack_push(in(_Stack), in(_Value)) :: (address * c_int) 
    + foreign.
:- trust pred ciao_stack_pop(in(_Stack)) ::  address
    + foreign.
:- trust pred ciao_stack_top(in(_Stack), go(Value)) ::  (address * c_int)  
    + (foreign, returns(Value)).

cc_stack_new(cc_stack(X)) :-
    ciao_stack_new(X), 
    % stack are deallocated on backtrack.
    undo(ciao_stack_delete(X)).

cc_stack_size(cc_stack(X), Size):-
    ciao_stack_size(X, Size).

cc_stack_push(cc_stack(X), I):-
    ciao_stack_push(X, I).

cc_stack_pop(cc_stack(X)):-
    (
        ciao_stack_size(X, Size), Size > 0  ->
        ciao_stack_pop(X)
    ;
        throw(error(empty_cc_stack, cc_stack_pop/1-1))
    ).

cc_stack_top(cc_stack(X), Int):-
    (
        ciao_stack_size(X, Size), Size > 0  ->
        ciao_stack_top(X, Int)
    ;
        throw(error(empty_cc_stack, cc_stack_top/1-1))
    ).

    
:- use_foreign_library('stdc++').
:- use_foreign_source('cc_stack.cc').


File cc_stack.cc:

#include <stack>
using namespace std;

typedef stack<int> ciao_stack;

extern "C" void *
ciao_stack_new(void) 
{
  return (void*) new ciao_stack;
}

extern "C" void
ciao_stack_delete(void * S) 
{
  delete ((ciao_stack *) S);
}

extern "C" int
ciao_stack_size(void * S) 
{
  return  (((ciao_stack *) S)->size());
}

extern "C" void
ciao_stack_push(void * S, int v) 
{
  ((ciao_stack *) S)->push(v);
}

extern "C" void 
ciao_stack_pop(void * S) 
{
  ((ciao_stack *) S)->pop();
}

extern "C" int
ciao_stack_top(void * S) 
{
  return ((ciao_stack *) S)->top();
}

Embedding a Ciao engine into a C/C++ application

It is possible to include a Ciao engine (compiled as a static or dynamic library) into an existing C or C++ application. To do that it is necessary to call the ciao_opts() and ciao_init() functions to initialize the engine and some ciao_load_qfile() to load the necessary bytecode files. The structure of the C program is similar to:

#include <ciao_prolog.h>

int main(void) {
  ciao_opts("program_name", 0, NULL, 0, NULL, NULL);
  ciao_init(NULL);
  ciao_load_qfile("...");
  ...
  return 0;
}

See the files at foreign_interface/embedding_example/ for a complete detailed example including a sample build script.


Usage and interface