How to use a NIF resource in multi modules?

I am studying Elixir and NIFs to implement elixir project which calls C libraries.
I have a question.
Is this possible to use a ErlNifResourceType in multiple modules?

#example.c
typedef struct {
  int num;
} Example;

ErlNifResourceType* res_type_ex;
ERL_NIF_TERM nif_create_ex(ErlNifEnv* env,int argc,const ERL_NIF_TERM argv[]){
  Example* res;
  ERL_NIF_TERM ret;
  enif_alloc_resource(res_type_ex, sizeof(Example));
  ret = enif_make_resource(env, res);
  enif_release_resource(res);
  res->num = 1;
  return ret;
}

static int load(ErlNifEnv* env, void** priv, ERL_NIF_TERM load_info){
  const char* mod = "Elixir.Num";
  const char* name = "Example";
  int flags = ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER;
  res_type_ex = enif_open_resource_type(env,mod,name,NULL,flags,NULL);
  return 0;
}

static ErlNifFunc nif_funcs[] = {
  {"create_ex",1,nif_create_ex}
};
ERL_NIF_INIT(Elixir.Num, nif_funcs,&load,NULL,NULL,NULL);

# compile
# gcc -fPIC -shared -o example.so example.c -I /usr/lib/erlang/erts-10.5.6/include
#num.ex
defmodule Num do
  @on_load :load_nifs
  def load_nifs do
    :erlang.load_nif('example',0)
  end
  def create_ex do
    raise "NIF create_ex/0 is not implemented"
  end
end

When resourcetype “res_type_ex” have to be used in other modules (for example nest module Num.Inner module) , are there any good way ?

Thanks.

Why do you want to have them in different modules? Why not just have the NIF interface in one Erlang/Elixir module which you can call from other modules?

3 Likes

Thank you so much for reply.
The reason is that the resources used in module Num are needed for the arguments of the function defined in another module. I thought it should be a single module, but I would like to maintain readbility. I write a function which uses res_type_ex

#use_example.c
#include "erl_nif.h"
ERL_NIF_TERM nif_use_ex(ErlNifEnv* env,int argc,const ERL_NIF_TERM argv[]){
  Example* res;
  enif_get_resource(env,argv[0],res_type_ex,(void**)&res);
  res->num ++;
  return enif_make_atom(env,"ok");
}

This function would like to be defined in as follows:

defmodule Num.Use do
@doc """
  arguments of use_ex/1 is assumed reference created by Num.create_ex/0
"""
  def use_ex(_a) do
    raise "NIF use_ex/1 is not implemented"
  end
end 

In this case, how should I write ? And

Why not just have the NIF interface in one Erlang/Elixir module which you can call from other modules?

I’m sorry… what does this mean ?

I thought you meant that you would have different Elixir modules calling different NIF modules which shared the same C data structure. This I would be careful doing. I don’t know if you can load a NIF module multiple times so you can have he functions defined in multiple Elixir modules, your Num and Num.Use. I would not expect that to work but I have never tried.

That is why I suggested having only one NIF module loaded by only one Elixir module which then provides the interface to all the NIF implemented functions. In your case that could be a Num.Nif.

You have to be careful with shared data stored by the NIF module as that C code can be called at the same by many Elixir processes on different BEAM schedulers each which runs in its own OS thread. The code needs to be thread safe, or at least thread aware.

1 Like

I’m sorry for the late reply. Thanks for answer.
I will follow the advice and use Only One Modules to call NIF functions.

I was able to resolve the problem.

Thanks

I guess it will be required to describe ERL_NIF_INIT for each module.