FFI vs C/C++ API – Advantages and Disadvantages

language-designlanguage-discussionlanguage-features

I am trying to understand the advantages and disadvantages of a Foreign Function Interface (FFI) (in which the high-level languagd can call most C functions directly and can manipulate C data structures) vs. a C/C++/etc. API, in which the native function is passed runtime-specific data structures and must use runtime-provided APIs to manipulate them.

To me, the first of only two advantages of the latter approach seems to be in languages that do not support proper encapsulation, in which case a native extension can encapsulate unsafe code in a way that would otherwise be difficult. But Python has ctypes and CFFI, Ruby has FFI, and LuaJIT has its FFI (on which CFFI is based). This only really seems valid for VMs that must securely execute untrusted code (Javascript, Lua, Dart) or that are purely interpreted.

Java only has JNI built-in, but that is changing. .NET family languages can take advantage of a superb FFI (P/Invoke).

In particular, interfacing C with OCaml and HHVM requires writing C and C++ code respectively, even though both are compiled. Why?

Edit: My question is "What advantages and disadvantages does an FFI have compared to a C API?" One or the other is necessary; my question is about their respective advantages and disadvantages.

Best Answer

Take a complex C library which was written ages ago, not written specifically to be used by Lua or Python or whatever, with an enormous number of functions.

You can:

  1. Try to write a wrapper dylib over it in C which calls the other C library and focuses on all the binding work necessary to make that a proper and idiomatic Python or Lua module, e.g.
  2. Just be able to use the library right away in Lua or Python or any other language through the FFI and test it, play with it, experiment, start wrapping it, etc.

The second option is pretty appealing to me. Also you don't have to use the FFI directly. You can still, for example, use LuaJIT's FFI to directly call C functions behind a nice Lua table which conforms to Lua idioms (1-based indexing, e.g.). I actually find that takes far less time wrapping FFI code to be idiomatic than exporting a proper Lua module which has to translate everything to tables and numbers and so on using Lua's C API to build a conforming Lua module.

The FFI lets you do more of your work directly in the language you're targeting. The alternative will inevitably require you to spend a whole lot more time in C.

Take OpenGL as an example and let's just pretend there were no OGL Lua modules already available. In that case, with LuaJIT's FFI, you could be drawing shapes to a viewport in no time by just directly calling OpenGL functions through the FFI. Soon you might start wrapping it into Lua tables with nice functions suitable for your application. Otherwise you might have to spend an entire weekend or longer binding and translating a boatload of OpenGL functions, constants, etc. in C to build a miniature Lua module with only a small subset of OGL functionality and then import that just to get a triangle on a screen, only to then have to bind more functions and so on.

It is far, far more time-consuming to get started trying to export a conforming module to your language as opposed to just being able to call C APIs directly from it right off the bat. That applies even if you use things to help with the binding like Boost.Python or LuaBind. It's an extremely time-consuming process to do properly, and FFI bypasses all of that.

I actually think FFI is the way to go and would prefer that over exporting modules specific to a language. As an example, some users wrapped my SDK to allow them to call its C functions from C#. I never had to write a C# or .NET module to let them do that. They just directly call my C functions from C#'s FFI which lets them import C functions from dylibs and call them right away.

Related Topic