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 strings 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 ints. append does a similar check but for strings 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 strings. 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

version #5755, modified 2009-04-14 02:39:22 by ianxm