C Foreign Function Interface
Introduction
This tutorial demonstrates how to call C code from a neko application. We will create a library with two simple C functions. One returns the sum of two numbers. The other appends two string
s together. Then we will call each function from Haxe code.
The C Code
#include <neko.h> #include <stdlib.h> #include <string.h> value sum(value a, value b) { if( !val_is_int(a) || !val_is_int(b) ) return val_null; return alloc_int(val_int(a) + val_int(b)); } value append(value a, value b) { value ret; int lena, lenb; char* str; if( !val_is_string(a) || !val_is_string(b) ) return val_null; lena = val_strlen(a); lenb = val_strlen(b); str = (char*)malloc(lena + lenb + 1); memcpy(str, val_string(a), lena); memcpy(str + lena, val_string(b), lenb); str[lena + lenb] = 0; ret = alloc_string(str); free(str); return ret; } DEFINE_PRIM(sum,2); // function sum with 2 arguments DEFINE_PRIM(append,2); // function append with 2 arguments
All functions that will be called from neko must include neko.h. Notice that the parameters and return values are of type value
. The value
type is used to wrap all data passed in and out of C functions.
In sum
, we expect both input parameters to be int
's. If they are not, we return null
. We use the neko function val_is_int
to check that they are indeed int
s. append
does a similar check but for string
s using val_is_string
.
The val_int
function returns the int
stored in a value type. The alloc_int
function creates an int
to be returned. val_string
and alloc_string
are analogous functions for string
s. val_strlen
obviously returns the length of a string
value.
append
could have been written more easily using a neko buffer, but I wanted the example to contain some regular C code.
The DEFINE_PRIM
macro declares the function to be importable by neko. The first parameter is the function; the second parameter is the number of parameters it takes.
Functions that take more than five arguments are handled differently. More info on writing C functions for use with neko can be found with the neko documentation.
Compile the Library
Compile the code into a shared library. Be sure to add [haxe install]/neko/include to the include path and link with the neko library (neko.lib for windows or libneko.so for linux), which is in [haxe install]/neko. Move the binary into the current directory and rename it ffilib.ndll.
The Haxe Code
import neko.Lib; class FfiTest { public static function main() { trace("sum of 1 and 2 is " + sumFunc(1,2)); trace("\"pet\" appended to \"car\" is \"" + Lib.nekoToHaxe(appendFunc(Lib.haxeToNeko("car"),Lib.haxeToNeko("pet"))) + "\""); } private static var sumFunc = neko.Lib.load("ffilib","sum",2); private static var appendFunc = neko.Lib.load("ffilib","append",2); }
We use neko.Lib.load
to load the c functions. The first parameter is the name of the .ndll file. The second parameter is the name of the function. The third parameter is the number of input parameters it takes. The function, once loaded, can be called like any other haxe function if it only uses simple types, like sum
.
append
uses strings, which are represented differently in haxe and neko, so we call neko.Lib.haxeToNeko
to convert parameters being passed in and neko.Lib.nekoToHaxe
to convert the return value.
Compile and Run
Compile and run with:
haxe -neko ffi.n -main FfiTest neko ffi