Skip to content

API changes from v0.x to v1.x

TooTallNate edited this page Sep 9, 2012 · 18 revisions

Most of the API's in node-ffi changed in v1.x to be more concise.

Package on npm has been renamed

On npm, node-ffi is now published as simply ffi, to comply with npm's package naming conventions. This works out well because it becomes part of "opting in" to the node-ffi v1.0 API.

The "Pointer" class is GONE!

The main change is that the old Pointer class has been completely removed, in favor of using official Node Buffer objects everywhere. The Pointer class had some additional functionality over vanilla Buffers, so ref was written to iron out those differences.

So anywhere where you have ffi.Pointer in your code, that will need to be changed to use vanilla Buffers. Read up on these subjects if you are not familiar with them:

The "type" system

ref introduces a "type" system, which allows you to classify the data inside a Buffer as an int, or char *, etc. So now node-ffi uses these "type" objects when defining FFI functions:

var ref = require('ref')
var ffi = require('ffi')

// the "int" type
var int = ref.types.int

// create an FFI'd "abs(3)" function
var absPtr = ffi.DynamicLibrary().get('abs')
var abs = ffi.ForeignFunction(absPtr, int, [ int ])

// and invoke!
abs(-1)   // ← 1
abs(-123) // ← 123

String-name specifiers

ref supports String shorthands for these types. So "int" gets coerced into ref.types.int. This is mostly for convenience and backwards compatibility with old node-ffi apps/modules.

The "pointer" type

The old "pointer" type should not be used anymore! This is because "pointer" is ambiguous, and node-ffi has no idea what the pointer represents. Instead, pass in an explicit "reference" type.

So if you had a function that accepts an int * type, then you can create an appropriate "type" for that by invoking the ref.refType() function:

var intPtr = ref.refType(ref.types.int)

// and now you can pass in the "int *" type to ForeignFunction, Library, or Callback
var func = ffi.ForeignFunction(funcPtr, 'void', [ intPtr ])

Async FFI'd Functions

The API for invoking asynchronous ForeignFunction instances has changed. Previously, you'd have to specify whether a ForeignFunction instance was "async" during instantiation, but now you can decide at call-time. The "regular" function invokation (i.e. func()) is the "sync" version, and calling func.async() with a callback function is the "async" version.

Additionally, before an async invokation would return an EventEmitter instance that would emit "success" when the FFI'd function completed. This was unnecessary, and now you simply pass the .async() function a callback function as the final argument. The callback follows Node's the traditional err, res argument signature.

Before:

var func = new ffi.ForeignFunction(funcPtr, 'int', [ 'int' ], true)
func(-5).on('success', function (res) {
  console.log('result:', res)
})

Now:

var func = ffi.ForeignFunction(funcPtr, 'int', [ 'int' ]) // no need to specify "async" here...
func(-5)  // "sync" version
func.async(-5, function (err, res) {  // "async" version
  if (err) throw err
  console.log('result:', res)
})

FFI'd vararg functions (like printf)

There's a new function called VariadicForeignFunction which doesn't return a ForeignFunction function, but rather a "function generator" that returns ForeignFunction instances when invoked. The API for it looks like:

var printfPointer = ffi.DynamicLibrary().get('printf')
var printfGen = ffi.VariadicForeignFunction(printfPointer, 'void', [ 'string' ])

// invoke!
printfGen()('Hello World!\n')
printfGen('int')('This is an int: %d\n', 10)
printfGen('string')('This is a string: %s\n', 'hello')

So you invoke VariadicForeignFunction with the same arguments as you usually would, leaving out the ... arguments (i.e. you only define the "fixed" arguments). What's returned is a "function generator", that you invoke with the "types" that are about to be passed for the varargs section. If there's no additional args being passed for the varargs section, then you invoke the generator with no arguments. After that you invoke the returned ForeignFunction instance with the actual arguments.

The "function generator" keeps track of caching the ForeignFunction instances in between subsequent calls with the same vararg types, so there's a good convenience there.