Skip to content

Instantly share code, notes, and snippets.

@mattsan
Created November 29, 2020 03:43
Show Gist options
  • Save mattsan/5c7b568c603740c60a6406138a27eaac to your computer and use it in GitHub Desktop.
Save mattsan/5c7b568c603740c60a6406138a27eaac to your computer and use it in GitHub Desktop.
C++のクラスをNIFでElixirにバインドしてみた
defmodule Counter do
@on_load {:load_nif, 0}
@compile {:autoload, false}
def load_nif do
Application.app_dir(:counter, "priv/counter_nif")
|> to_charlist()
|> :erlang.load_nif(0)
end
def create(_init_count), do: :erlang.nif_error(:nif_not_loaded)
def up(_ref), do: :erlang.nif_error(:nif_not_loaded)
def down(_ref), do: :erlang.nif_error(:nif_not_loaded)
def set(_ref, _count), do: :erlang.nif_error(:nif_not_loaded)
def get(_ref), do: :erlang.nif_error(:nif_not_loaded)
end
// c_src/counter.h
#ifndef COUNTER_H_
#define COUNTER_H_
class Counter {
public:
Counter(int count = 0) : count_(count) {}
~Counter() {}
int up() { return ++count_; }
int down() { return --count_; }
void set(int count) { count_ = count; }
int get() const { return count_; }
private:
int count_;
};
#endif//COUNTER_H_
// c_src/counter_nif.cpp
#include <new>
#include <erl_nif.h>
#include "counter.h"
ErlNifResourceType* CounterType;
void destruct_counter(ErlNifEnv* caller_env, void* obj) {
Counter* counter = static_cast<Counter*>(obj);
counter->~Counter();
}
int load(ErlNifEnv* caller_env, void** priv_data, ERL_NIF_TERM load_info) {
CounterType = enif_open_resource_type(caller_env, "Counter", "Counter", destruct_counter, ERL_NIF_RT_CREATE, nullptr);
return 0;
}
ERL_NIF_TERM create(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
int count;
enif_get_int(env, argv[0], &count);
void* resource = enif_alloc_resource(CounterType, sizeof(Counter));
new(resource) Counter(count);
ERL_NIF_TERM handle = enif_make_resource(env, resource);
enif_release_resource(resource);
return enif_make_tuple2(
env,
enif_make_atom(env, "ok"),
handle
);
}
ERL_NIF_TERM up(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
Counter* counter;
enif_get_resource(env, argv[0], CounterType, reinterpret_cast<void**>(&counter));
int result = counter->up();
return enif_make_tuple2(
env,
enif_make_atom(env, "ok"),
enif_make_int(env, result)
);
}
ERL_NIF_TERM down(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
void* resource;
enif_get_resource(env, argv[0], CounterType, &resource);
Counter* counter = static_cast<Counter*>(resource);
int result = counter->down();
return enif_make_tuple2(
env,
enif_make_atom(env, "ok"),
enif_make_int(env, result)
);
}
ERL_NIF_TERM set(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
void* resource;
enif_get_resource(env, argv[0], CounterType, &resource);
Counter* counter = static_cast<Counter*>(resource);
int count;
enif_get_int(env, argv[1], &count);
counter->set(count);
return enif_make_atom(env, "ok");
}
ERL_NIF_TERM get(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
void* resource;
enif_get_resource(env, argv[0], CounterType, &resource);
Counter* counter = static_cast<Counter*>(resource);
int result = counter->get();
return enif_make_tuple2(
env,
enif_make_atom(env, "ok"),
enif_make_int(env, result)
);
}
static ErlNifFunc nif_funcs[] = {
{"create", 1, create},
{"up", 1, up},
{"down", 1, down},
{"set", 2, set},
{"get", 1, get}
};
ERL_NIF_INIT(Elixir.Counter, nif_funcs, load, nullptr, nullptr, nullptr);
PREFIX = $(MIX_APP_PATH)/priv
BUILD = $(MIX_APP_PATH)/obj
NIF = $(PREFIX)/counter_nif.so
CFLAGS = -std=c++11 -fpic
LDFLAGS = -lpthread -dynamiclib -undefined dynamic_lookup
ERL_CFLAGS = -I$(ERL_EI_INCLUDE_DIR)
ERL_LDFLAGS = -L$(ERL_EI_LIBDIR) -lei
SRC = $(wildcard c_src/*.cpp)
HEADERS = $(wildcard c_src/*.h)
OBJ = $(SRC:c_src/%.cpp=$(BUILD)/%.o)
all: install
install: $(PREFIX) $(BUILD) $(NIF)
$(OBJ): $(HEADERS) Makefile
$(BUILD)/%.o: c_src/%.cpp
$(CC) -c $(CFLAGS) $(ERL_CFLAGS) -o $@ $<
$(NIF): $(OBJ)
$(CC) $(LDFLAGS) $(ERL_LDFLAGS) -o $@ $^
$(PREFIX) $(BUILD):
mkdir -p $@
clean:
$(RM) $(NIF) $(OBJ)
.PHONY: all clean install
defmodule Counter.MixProject do
use Mix.Project
def project do
[
app: :counter,
version: "0.1.0",
elixir: "~> 1.11",
start_permanent: Mix.env() == :prod,
deps: deps(),
compilers: [:elixir_make | Mix.compilers()]
]
end
def application do
[
extra_applications: [:logger]
]
end
defp deps do
[
{:elixir_make, "~> 0.6", runtime: false}
]
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment