Niffler.Library behaviour (Niffler v0.3.0) View Source

Niffler.Library allows to wrap dynamically loaded libraries. And create foreign function interfaces (FFI) for them. This usually requires:

  • Shared headers such for type definitions
  • Some init code involving dlopen()
  • Static variables/state that is part of the library
  • Potential deinit code involving dlclose()

Niffler.Library allows to produce modules at runtime doing all these things and exposing access to the c-functions of a dynamic library.

defmodule Gmp do
  use Niffler.Library, thread_safe: false

  @impl true
  def header() do
    """
    // library handle
    void *gmp;

    // types
    typedef struct
    {
      int _mp_alloc;
      int _mp_size;
      void *_mp_d;
    } __mpz_struct;

    typedef __mpz_struct mpz_t[1];
    void (*mpz_init)(mpz_t);
    // ...
    """
  end

  @impl true
  def on_load() do
    """
    // loading the library with dlopen()
    gmp = dlopen("libgmp.#{library_suffix()}", RTLD_LAZY);
    if (!gmp) {
      return "could not load libgmp";
    }
    // loading symbols:
    dlerror();
    if (!(mpz_init = dlsym(gmp, "__gmpz_init"))) {
      return dlerror();
    }
    // other symbols ...
    """
  end

  @impl true
  def on_destroy() do
    """
    if (!gmp) {
      return;
    }
    // unloading
    dlclose(gmp);
    """
  end

  # definining one or more operations here...
  defnif :mul, [a: :int, b: :int], ret: :int do
    """
    mpz_set_si(ma, $a);
    mpz_set_si(mb, $b);
    mpz_mul(mc, ma, mb);
    $ret = mpz_get_si(mc);
    """
  end
end

Once defined the module functions can be used via:

  {ok, [result]} = Gmp.mul(4, 5)

Link to this section Summary

Functions

Defines a new nif function in the current module.

Returns the current platforms default library suffix

Callbacks

Return a c-fragement of a common header for type definitions, static variables and other neccesary c code.

Return a c-fragement that is called when the module is unloaded. Typicallly this fragment would contain a call to dlclose() for a dynamic library.

Return a c-fragement that is called on the first call to the module. Typicall this fragment would contain a call to dlopen() when loading a dynamic library.

Link to this section Functions

Link to this macro

defnif(name, inputs, outputs, list)

View Source (macro)

Specs

defnif(atom(), keyword(), keyword(), [{:do, binary()}]) ::
  {:__block__, [], [{any(), any(), any()}, ...]}

Defines a new nif function in the current module.

Same as Niffler.defnif/4 but with access to the current module context.

Returns the current platforms default library suffix:

  • dll on windows
  • dylib on mac
  • so on linux

Useful for dlopen() code to load the correct library:

  @impl true
  def on_load() do
    """
    // loading the library with dlopen()
    gmp = dlopen("libgmp.#{library_suffix()}", RTLD_LAZY);
    if (!gmp) {
      return "could not load libgmp";
    }
    """
  end

Link to this section Callbacks

Specs

header() :: binary()

Return a c-fragement of a common header for type definitions, static variables and other neccesary c code.

Example:

  @impl true
  def header() do
    """
    // library handle
    void *gmp;
    """
  end

Specs

on_destroy() :: binary()

Return a c-fragement that is called when the module is unloaded. Typicallly this fragment would contain a call to dlclose() for a dynamic library.

Example:

  @impl true
  def on_destroy() do
    """
    if (!gmp) {
      return;
    }
    // unloading
    dlclose(gmp);
    """
  end

Specs

on_load() :: binary()

Return a c-fragement that is called on the first call to the module. Typicall this fragment would contain a call to dlopen() when loading a dynamic library.

This c-fragment should return a char* (a common string in c) when any error has occured.

Example:

  @impl true
  def on_load() do
    """
    // loading the library with dlopen()
    gmp = dlopen("libgmp.#{library_suffix()}", RTLD_LAZY);
    if (!gmp) {
      return "could not load libgmp";
    }
    """
  end