17

Foreign Function Interface Reference

The Foreign Function Interface (FFI) is a mechanism for interfacing Slogan with C libraries. Slogan has two interfaces for communicating with C libraries. First we will discuss the higher-level interface made available through the declare ffi statement. The syntax of this statement is,

declare ffi library_name list_of_prototypes

Library_name should be a string or an identifier. If it is a string, it must be the full or relative path of the C library to be loaded. If the path is relative, the library is first looked up in the local folder and then under the paths specified in the `LD_LIBRARY_PATH` environment variable.

If library_name is an identifier it must point to a library object loaded by the ffi_open function.

List_of_prototypes contain prototype declarations of functions and structures in the C library. The Slogan compiler uses these declarations to define functions that act as proxies to access C objects.

A function prototype has the following syntax:

return_type c_function_name list_of_argument_types as proxy_function_name

Proxy_function_name is optional. If provided, the Slogan function that acts as a proxy will be bound to this name. Otherwise it will be bound to c_function_name.

The following table list type names that can be used in a prototype and their equivalent C-types:

Type Equivalent C-type
uint8 uint8_t
int8 int8_t
uint16 uint16_t
int16 int16_t
uint32 uint32_t
int32 int32_t
uchar unsigned char
char signed char
ushort unsigned short
short signed short
uint unsigned int
int signed int
ulong unsigned long
long signed long
uint64 uint64_t
int64 int64_t
float float
double double
longdouble long double
charstring char *
pointer void *
void void

Note that the void type can only be used as a return type. It cannot be used as the type of a parameter.

As an example, two C functions and their corresponding prototypes are given below:


// sample_c_lib.c

#include <math.h>

double square_root(double f) { return sqrt(f); }
long add_li(int i, long l) { return ((long)i) + l; }
      

This is how this library would be loaded in slogan:


declare ffi "sample_c_lib.so"
[double square_root [double] as sqrt,
 long add_li [int, long]]
      

This prototype declaration means that the Slogan program can call the C square_root function as sqrt(param) and the add_li function with the same name.

A C structure proxy has the following syntax:

struct c_struct_name list_of_members

List_of_members is an association list of the types and field names. As an example consider the definition of a C struct and its corresponding prototype:


// sample_c_lib.c
struct point {
  int x;
  int y;
};

struct point make_point(int x, int y)
{
  struct point p = {x, y};
  return p;
}
      

To import this structure into a Slogan program,


declare ffi "sample_c_lib.so"
[struct point [int:x, int:y],
 point make_point [int, int]]
      

Note that a struct prototype is required only if Slogan has to call C functions that deal with struct instances directly, i.e not through pointers. If all C functions that you need to call deal with pointers to structures, you can get by with function declarations with the pointer type. Struct prototypes are also required if you want to inspect the contents of a pointer to a structure or you want to construct an instance of the structure from Slogan.

The complete example is reproduced below:


// sample_c_lib.c
#include <math.h>

double square_root(double f) { return sqrt(f); }
long add_li(int i, long l) { return ((long)i) + l; }

struct point {
  int x;
  int y;
};

struct point make_point(int x, int y)
{
  struct point p = {x, y};
  return p;
}

      

Compile to a shared library (Linux):

$ gcc -Wall -shared -fPIC -o sample_c_lib.so sample_c_lib.c

Compile to a shared library (OS X):

$ gcc -dynamiclib -Wl,-undefined -Wl,dynamic_lookup -o sample_c_lib.so sample_c_lib.c

// Load and use the C library from Slogan:


declare ffi "sample_c_lib.so"
[double square_root [double] as sqrt,
 long add_li [int, long],
 struct point [int:x, int:y],
 point make_point [int, int]]

sqrt(10.3)
// 3.2093613071762426
add_li(10, 20)
// 30
let p = make_point(100, 200)
c_struct_name(p)
// point
c_struct_get(p, 'x)
// 100
c_struct_get(p, 'y)
// 200
      

Also see:

c_struct_instance
pointer_to_c_struct

Configurable limits

The header file src/include/slogan.h define certain constants that control the number of arguments that can be passed to a C function and the number of C-struct proxies that can be defined in Slogan. These definitions are:

#define SLOGAN_LIBFFI_ARGC 32
#define SLOGAN_LIBFFI_STRUCT_DEFS 32
#define SLOGAN_LIBFFI_STRUCT_SIZE 1024

By default the number of arguments and struct proxies are limited to 32. A struct can be up to 1024 bytes in size. These limits can be increased or decreased as per the application's requirement. Slogan must be rebuilt for these changes to take effect.

Low-level FFI

The lower-level FFI gives more control over making C function calls. It also allows C functions to call-back into Slogan code.

A C function to be callable from low-level FFI must have one of the following signatures:

  1. return_type f(___slogan_obj *args)
  2. return_type f(void *argp, ___slogan_obj *args)

Here args is an array of ___slogan_obj objects. Argp is a pointer to an arbitrary C object and the return_type is one of: void, void*, ___slogan_obj, char, char*, int, unsigned int, long, unsigned long, long long, float or double.

The type ___slogan_obj represent all Slogan objects like strings, lists, integers and characters. The header file slogan.h declare functions and macros required for converting a ___slogan_obj to a C object and vice versa.

Conversion Macros and Functions

This section describes the C macros and functions defined in slogan.h for converting between ___slogan_obj and C types.

C macro: ___slogan_obj_to_char(s, t)
C macro: ___slogan_obj_to_uchar(s, t)
C macro: ___slogan_obj_to_int(s, t)
C macro: ___slogan_obj_to_uint(s, t)
C macro: ___slogan_obj_to_long(s, t)
C macro: ___slogan_obj_to_ulong(s, t)
C macro: ___slogan_obj_to_longlong(s, t)
C macro: ___slogan_obj_to_float(s, t)
C macro: ___slogan_obj_to_double(s, t)
C macro: ___slogan_obj_to_charstring(s, t)
C macro: ___slogan_obj_to_nonnull_charstring(s, t)
C macro: ___slogan_obj_to_void_pointer(s, t)
      

The ___slogan_obj object s is converted to a C int, unsigned int, float, double or char* and stored in t which should be the address of a C variable of the appropriate type.

C macro: ___int(s)
returns: a C int that contain the integer value of the Slogan character or integer
(___slogan_obj) `s`.
C function: ___slogan_obj ___fix(int i)
returns: a ___slogan_obj containing the value of the C integer `i`.
C macro: ___char_to_slogan_obj(c)
returns: a ___slogan_obj containing the value of the character `c`.
C macro: ___uchar_to_slogan_obj(c)
returns: a ___slogan_obj containing the value of the unsigned character `c`.
C macro: ___uint_to_slogan_obj(i)
returns: a ___slogan_obj containing the value of the unsigned integer `i`.
C macro: ___ulong_to_slogan_obj(i)
returns: a ___slogan_obj containing the value of the unsigned long `i`.
C macro: ___long_to_slogan_obj(i)
returns: a ___slogan_obj representing the long value `i`.
C macro: ___longlong_to_slogan_obj(i)
returns: a ___slogan_obj representing the long long value `i`.
C macro: ___float_to_slogan_obj(f)
returns: a ___slogan_obj representing the float value `f`.
C macro: ___double_to_slogan_obj(d)
returns: a ___slogan_obj representing the double value `d`.
      
C macro: ___void_pointer_to_slogan_obj(p)
returns: a ___slogan_obj representing the generic pointer `p`.
C macro: ___charstring_to_slogan_obj(cs, s)
C macro: ___nonnullcharstring_to_slogan_obj(cs, s)
      

___charstring_to_slogan_obj initializes the ___slogan_obj s as a Slogan string with the characters from the C string c. ___charstring_to_slogan_obj creates a null-terminated string and ___nonnullcharstring_to_slogan_obj creates a string object that can keep track of its length. The C string cs should always be null terminated.

The following C program shows the correct way to use these functions:


// test.c
#include <stdio.h>
#include "slogan.h"

___slogan_obj hi(___slogan_obj *args)
{
  ___slogan_obj message;
  char *s = "hi from C!";
  char *p;
  ___slogan_obj_to_charstring(args[0], &p);
  printf("Slogan says \"%s\"\n", p);

  ___charstring_to_slogan_obj(s, &message);
  return message;
}
      

Note: To compile this program, you will have to include $SLOGAN_SOURCE/src/include.h and $SLOGAN_SOURCE/platform/gsc/include in your C compiler's include path.

Once this program is compiled to s shared library, the function hi can be invoked from Slogan as shown below:


let clib = ffi_open("./test.so")
let f = ffi_fn(clib, "hi")
ffi_call_obj(f, #["hello from Slogan"])
//> Slogan says "hello from Slogan"
// hi from C!
ffi_close(clib) 
// true
C function: ___slogan_obj ___pair(___slogan_obj obj1, ___slogan_obj obj2)
 returns: a ___slogan_obj that represents a Slogan pair with head and tail pointing to
 `obj1` and `obj2`.

 Both `obj1` and `obj2` must be of types ___slogan_obj.
C macro: ___head(p)
C macro: ___tail(p)
returns: the head or tail objects of the ___slogan_obj `p` that represents a Slogan pair.
C constant: ___TRU
C constant: ___FAL
returns: the ___slogan_obj that represents the Slogan values true or false.
C constant: ___NUL
 returns: the ___slogan_obj that represents the empty list [].
C macro: ___is_empty(pair)
returns: ___TRU if pair is the empty list [], ___FAL otherwise.
      

The following C function demonstrates how to receive a list of integers from Slogan and print it:


void print_i_list(___slogan_obj *args)
{
  ___slogan_obj a = args[0];
  while (!___is_empty(a)) {
    ___slogan_obj h = ___head(a);
    ___slogan_obj t = ___tail(a);
    int i;
    ___slogan_obj_to_int(h, &i);
    printf("%d\n", i);
    a = t;
  }
}
      

Print_i_list can be invoked from Slogan as shown below:


let f = ffi_fn(clib, "print_i_list")
ffi_call_void(f, #[[1, 10, -3, 99]])
//> 1
  10  
  -3
  99
      
C macro: ___body(array)
returns: a C array of ___slogan_obj objects that are the elements of the Slogan array array.
      

The following C function prints the first two elements of a Slogan array passed to it. The function expects the first element to be an integer and the second element to be a string.


void f(___slogan_obj *args)
{
  ___slogan_obj *b = ___body(args[0]);
  int i;
  char *s;
  ___slogan_obj_to_int(b[0], &i);
  ___slogan_obj_to_charstring(b[1], &s);
  printf("%d\n%s\n", i, s);
}
      

This function can be invoked from Slogan as,


f = ffi_fn(clib, "f")
ffi_call_void(f, #[#[100, "hello"]])
//> 100
  hello
      

A bunch of macros and functions are provided for working with arrays. (Some of the macros require the local variable `___slogan_obj ___temp;` to be declared explicitly.)

C function: ___slogan_obj ___alloc_u8array(int size)
returns: a byte array of length `size`.
C function: ___slogan_obj ___alloc_array(int size)
returns: an array of length `size`.
C macro: ___array_length(array)
returns: the number of elements in `array` as a C integer.
C macro: ___array_at(array, i)
returns: the element in `array` at index `i` as a ___slogan_obj.
C macro: ___array_set(array, i, obj)
returns: sets the element in `array` at index `i` to the ___slogan_obj `obj`.

The index argument must be passed in as a Slogan integer (i.e ___fix(i)).

Equivalent macros are available for arrays of signed/unsigned integers and floats. Similar macros are there to access and manipulate Slogan strings as well:

C macro: ___s8array_length(array)
returns: the number of elements in `array` as a C integer.
C macro: ___s8array_at(array, i)
returns: the element in `array` at index `i` as a ___slogan_obj.
C macro: ___s8array_set(array, i, obj)
returns: sets the element in `array` at index `i` to the ___slogan_obj `obj`.
C macro: ___u8array_length(array)
returns: the number of elements in `array` as a C integer.
C macro: ___u8array_at(array, i)
returns: the element in `array` at index `i` as a ___slogan_obj.
C macro: ___u8array_set(array, i, obj)
returns: sets the element in `array` at index `i` to the ___slogan_obj `obj`.
C macro: ___s16array_length(array)
returns: the number of elements in `array` as a C integer.
C macro: ___s16array_at(array, i)
returns: the element in `array` at index `i` as a ___slogan_obj.
C macro: ___s16array_set(array, i, obj)
returns: sets the element in `array` at index `i` to the ___slogan_obj `obj`.
C macro: ___u16array_length(array)
returns: the number of elements in `array` as a C integer.
C macro: ___u16array_at(array, i)
returns: the element in `array` at index `i` as a ___slogan_obj.
C macro: ___u16array_set(array, i, obj)
returns: sets the element in `array` at index `i` to the ___slogan_obj `obj`.
C macro: ___s32array_length(array)
returns: the number of elements in `array` as a C integer.
C macro: ___s32array_at(array, i)
returns: the element in `array` at index `i` as a ___slogan_obj.
C macro: ___s32array_set(array, i, obj)
returns: sets the element in `array` at index `i` to the ___slogan_obj `obj`.
C macro: ___u32array_length(array)
returns: the number of elements in `array` as a C integer.
C macro: ___u32array_at(array, i)
returns: the element in `array` at index `i` as a ___slogan_obj.
C macro: ___u32array_set(array, i, obj)
returns: sets the element in `array` at index `i` to the ___slogan_obj `obj`.
C macro: ___s64array_length(array)
returns: the number of elements in `array` as a C integer.
C macro: ___s64array_at(array, i)
returns: the element in `array` at index `i` as a ___slogan_obj.
C macro: ___s64array_set(array, i, obj)
returns: sets the element in `array` at index `i` to the ___slogan_obj `obj`.
C macro: ___u64array_length(array)
returns: the number of elements in `array` as a C integer.
C macro: ___u64array_at(array, i)
returns: the element in `array` at index `i` as a ___slogan_obj.
C macro: ___u64array_set(array, i, obj)
returns: sets the element in `array` at index `i` to the ___slogan_obj `obj`.
C macro: ___f32array_length(array)
returns: the number of elements in `array` as a C integer.
C macro: ___f32array_at(array, i)
returns: the element in `array` at index `i` as a ___slogan_obj.
C macro: ___f32array_set(array, i, obj)
returns: sets the element in `array` at index `i` to the ___slogan_obj `obj`.
C macro: ___f64array_length(array)
returns: the number of elements in `array` as a C integer.
C macro: ___f64array_at(array, i)
returns: the element in `array` at index `i` as a ___slogan_obj.
C macro: ___f64array_set(array, i, obj)
returns: sets the element in `array` at index `i` to the ___slogan_obj `obj`.
C macro: ___string_length(s)
returns: the number of characters in the string `s` as a C integer.
C macro: ___string_at(s, i)
returns: the character in `s` at index `i` as a ___slogan_obj.
C macro: ___string_set(s, i, obj)
returns: sets the character in `s` at index `i` to the ___slogan_obj `obj`.

Calling Slogan functions from C

The ___call_fn function enables C code to call any function implemented in Slogan. ___call_fn takes two arguments - the function object to call and its arguments. The arguments are passed as a single list. ___call_fn will return a ___slogan_obj which is the return value of the Slogan function. The following example show how to write a shared library that makes a call back into Slogan:


// call_fn_test.c 
#include <stdio.h>
#include "slogan.h"

___slogan_obj call(___slogan_obj *args)
{
  int error = 0;
  ___slogan_obj result;
  ___slogan_obj f = args[0];
  ___ON_THROW(result = ___call_fn(f, ___pair(___fix(100), ___pair(___fix(200), ___NUL))), error = 1);
  if (error == 1)
  {
    printf("Slogan function raised error\n");
    return ___FAL;
  }
  return result;
}
      

The ___ON_THROW macro will set the integer variable error to 1 if the Slogan function raised an exception.

Once the C file is compiled into a shared library, call can be invoked as shown below:


let clib = ffi_open("./call_fn_test.so")
let f = ffi_fn(clib, "call")
ffi_call_obj(f, #[add])
// 300
      

Next | Previous | Contents