はじめに
NIFを実際に使ってみます
例として、C言語で整数の掛け算を行う関数を作成して、Elixirから使ってみます
C言語ファイルを作成する
multiply.c
#include "erl_nif.h"
static ERL_NIF_TERM multiply(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
int a, b;
if (!enif_get_int(env, argv[0], &a) || !enif_get_int(env, argv[1], &b)) {
return enif_make_badarg(env);
}
int result = a * b;
return enif_make_int(env, result);
}
static ErlNifFunc nif_funcs[] = {
{"multiply", 2, multiply}
};
ERL_NIF_INIT(Elixir.HelloNif, nif_funcs, NULL, NULL, NULL, NULL)
Elixirモジュールを作成する
hello_nif.ex
defmodule HelloNif do
@on_load :load_nif
def load_nif do
:ok = :erlang.load_nif(~c'./multiply', 0)
end
def multiply(_a, _b), do: :erlang.nif_error(:nif_not_loaded)
end
Makefile の作成
Makefile
NIF_NAME = multiply
ERLANG_INCLUDE_DIR = $(shell erl -eval 'io:format("~s", [code:lib_dir(erl_interface, include)])' -s init stop -noshell)
ERTS_INCLUDE_DIR = $(shell erl -eval 'io:format("~s", [code:root_dir() ++ "/erts-" ++ erlang:system_info(version) ++ "/include"])' -s init stop -noshell)
all: compile
compile:
gcc -o $(NIF_NAME).so -shared -fPIC -I$(ERTS_INCLUDE_DIR) $(NIF_NAME).c
clean:
rm -f $(NIF_NAME).so
mix に Makefile を組み込む
mix.exs
defmodule HelloNif.MixProject do
use Mix.Project
def project do
[
app: :hello_nif,
version: "0.1.0",
elixir: "~> 1.17",
compilers: [:elixir_make] ++ Mix.compilers(), # ElixirMake を追加
deps: deps()
]
end
# Run "mix help compile.app" to learn about applications.
def application do
[
extra_applications: [:logger]
]
end
# Run "mix help deps" to learn about dependencies.
defp deps do
[
{:elixir_make, "~> 0.6"}
]
end
end
実行してみる
$ iex -S mix
Erlang/OTP 27 [erts-15.0.1] [source] [64-bit] [smp:16:16] [ds:16:16:10] [async-threads:1] [jit:ns]
gcc -o multiply.so -shared -fPIC -I/home/masa/.asdf/installs/erlang/27.0.1/erts-15.0.1/include multiply.c
Compiling 1 file (.ex)
Generated hello_nif app
Interactive Elixir (1.17.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> HelloNif.multiply(2,3)
6
iex(2)>
コンパイルも実行され、正しい結果が得られました。
試したコード