Skip to content

API changes from v0.x to v1.x

TooTallNate edited this page May 31, 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 the convention. This works out well because it's 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('node-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:

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 ])

The "string" type

The old "string" type specifier still works, but is discouraged from being used. The "preferred" way to specify a string arg or return type is with a proper char * type, like so:

var charPtr = ref.refType(ref.types.char)

// create an FFI'd function that takes 0 arguments and returns a `char *` that is a C String
var func = ffi.ForeignFunction(funcPtr, charPtr, [])

// then you can read the C String from the returned Buffer when invoked
var rtn = func()
rtn.readCString()

// this is important for C functions that require you to free() the string manually
free(rtn)

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!')

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 connivence there.

More coming soon!

Clone this wiki locally