Slogan, by design, is a high-level language, allowing you to build systems without being concerned too much about how the underlying hardware works. For instance, the programmer need not worry about how memory is allocated for objects involved in a computation and how that memory is returned to the system once the computation is over. Memory management is fully automated by the Slogan runtime system. Despite being a high-level, dynamic language, Slogan programs are compiled to efficient machine code that can sometimes rival the performance of statically typed languages.
While it's possible to write high-performance applications entirely in Slogan, situations may arise where a program needs direct access to platform specific libraries. Examples of such libraries include hardware drivers and database clients. Sometimes the programmer may like to have more control on how the application makes use of resources like memory and optimize a critical part of the program for performance. Requirements like this makes it necessary to enable Slogan programs to integrate libraries written in system programming languages like C and C++. The Foreign Function Interface (FFI) is a framework designed for this purpose.
Let us see how to create Slogan bindings to functions and structures defined in a small C library. The C library itself does not do anything fancy. It export two functions – one for creating a structure that represents a Cartesian point and another function for scaling a Cartesian point by a given factor. The code for the library is shown below:
// sample_c_lib.c
// 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
#include <stdlib.h>
struct point {
int x;
int y;
};
struct point make_point(int x, int y)
{
struct point p = {x, y};
return p;
}
struct point scale_point(struct point p, int factor)
{
struct point fp = {p.x * factor, p.y * factor};
return fp;
}
We should introduce the signatures of the definitions in the shared library to the Slogan program that wants to use it:
declare ffi "sample_c_lib.so"
[struct point [int:x, int:y],
point make_point [int, int],
point scale_point [point, int]]
After these declarations, the Slogan program can call the C functions, just like it would call normal Slogan functions.
let p = make_point(10, 20)
p
// [0, x:10, y:20]
let p2 = scale_point(p, 5)
p2
[0, x:50, y:100]
The declare ffi
statement has the following syntax:
declare ffi
<library_name> <list_of_prototypes>
Library_name
must 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 by 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 use these declarations to create functions that act as proxies to access C objects.
The syntax of a function prototype is shown below:
<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.
The syntax of the specification for a C struct is as follows:
struct <struct_name> <list_of_struct_members>
List_of_struct_members
is an association list (a list of pairs) that map types and names of a struct member.
Slogan allow even finer grained control over how a shared library is loaded into a program's address space and how C functions are mapped to
into the program. It is also possible to call Slogan functions from a C program! Please see the reference documentation on the
ffi_open
and ___call_fn
functions for more details.