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
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.
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:
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.
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`.
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