-
Notifications
You must be signed in to change notification settings - Fork 414
API changes from v0.x to v1.x
Most of the API's in node-ffi
changed in v1.x
to be more concise.
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 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:
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
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 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 ])
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.
var func = new ffi.ForeignFunction(funcPtr, 'int', [ 'int' ], true)
func(-5).on('success', function (res) {
console.log('result:', res)
})
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)
})
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.