Foreign Function Interface and Perl
libffi is a Foreign Function Interface (FFI) used by a number of scripting languages and virtual machine platforms to call native code. It doesn't require a compiler, and as long as dynamic libraries can be found, development packages aren't necessary either.
For Perl, this means that FFI::Raw (Perl bindings for
libffi) provides a viable alternative to the traditional Foreign Function Interface, known as XS. There are lots of reasons why you might not want to implement something using XS. For me the motivation is not having to delve into perlapi or perlxs, both of which are a fine prescription for madness. There are even some good reasons why you might want to use
libffi instead of XS; it should work with any language that generates machine code, so go ahead and write your extensions in assembly or rust!
Calling a function
FFI::Raw has a constructor that takes the name of the library, the name of the function that you want to bind to, the return type for that function, and a list of the argument types. For example, I can call the C
puts function on my Linux system like this:
I usually put the return value type and argument types on separate lines, as above, to differentiate them.
Recent versions of FFI::Raw have a shortcut where if you pass
undef as the library argument it will search the currently running process for symbols. This is a good way to call functions in the standard C library, which usually has a different name on every platform.
Finding a library
For the rest of this article I am going to use
libmagic as an example of how to create useful bindings. This library is commonly available on Unix systems and it provides an interface for determining the type of a file by its contents.
The first thing that we need to do is find the path to the libmagic library. To do that we will use FFI::CheckLib.
Now we need to create the bindings for the
libmagic library like we did for
We also need some constants that are defined in
Unfortunately there is no getting around this. Since we are not using a compiler, there is no reliable way of parsing the C header files, except for by implementing a C parser. Also requiring the C header files may mean installing development packages (if you recall one of the advantages of
libffi is that we don't need those installed).
If the library that you are targeting changes its constants frequently then FFI::Raw may not be the tool that you want to use. On the other hand, if your library is frequently changing its interface like that then that library may not be the tool that you want to use either.
Using the bindings
Now we can write a useful program using the bindings that we have created.
In this example we use FFI::Raw::MemPtr to construct a memory pointer and copy the content of the file into that pointer. If you know that the library that you are using is not going to do any funny busines, like write into your buffer, then you can also skip the memory copy by using FFI::Util's
It returns a pointer to the data stored by the scalar and the size of the data stored.
FFI::Raw provides a less complicated, more portable method for calling machine code from Perl. Any FFI (calling one language from an other) will inherently involve extra overhead, when you cross the language barrier. Because FFI::Raw is implemented itself in XS, you are taking a hit from both XS and
libffi every time you call a function in your target library. Both XS and
libffi are relatively fast, but XS will frequently be somewhat faster assuming you tune your XS correctly. The question that you should ask yourself: is the time spent debugging code, tuning XS and reading perlapi really worth it? If more time is being spent in the library you are calling than in crossing the language barrier, then you should definitely consider FFI::Raw and save yourself some programmer time at the expense of a comparably small amout of CPU time.