34
loading...
This website collects cookies to deliver better user experience
lib_calc.h
int somar(int a, int b);
int subtrair(int a, int b);
int multiplicar(int a, int b);
int dividir(int a, int b);
lib_calc.c
#include "lib_calc.h"
int somar(int a, int b) {
return a + b;
}
int subtrair(int a, int b) {
return a - b;
}
int multiplicar(int a, int b) {
return a * b;
}
int dividir(int a, int b) {
return a / b;
}
somar -> sum
subtrair -> subtract
multiplicar -> multiply
dividir -> divide
$ gcc -o lib_calc.so -c lib_calc.c
calc.c
#include<stdio.h>
#include "lib_calc.h"
#include<stdlib.h>
int main(int argc, char ** argv) {
int a, b;
char option;
a = strtol(argv[1], (char **)NULL, 10);
b = strtol(argv[3], (char **)NULL, 10);
switch (argv[2][0]) {
case '+': printf("%d + %d = %d\n", a, b, somar(a, b)); break;
case '-': printf("%d - %d = %d\n", a, b, subtrair(a, b)); break;
case '*': printf("%d * %d = %d\n", a, b, multiplicar(a, b)); break;
case '/': printf("%d / %d = %d\n", a, b, dividir(a, b)); break;
default: printf("Invalid option\n");
}
return 0;
}
$ gcc -o calc calc.c lib_calc.so
$ ./calc 1 + 1
1 + 1 = 2
$ ./calc 1
[1] 388 segmentation fault ./calc 1
lib_calc_nif.c
#include <erl_nif.h>
#include "lib_calc.h"
static ERL_NIF_TERM somar_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
int a, b, result;
enif_get_int(env, argv[0], &a);
enif_get_int(env, argv[1], &b);
result = somar(a, b);
return enif_make_int(env, result);
}
static ERL_NIF_TERM subtrair_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
int a, b, result;
enif_get_int(env, argv[0], &a);
enif_get_int(env, argv[1], &b);
result = subtrair(a, b);
return enif_make_int(env, result);
}
static ERL_NIF_TERM multiplicar_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
int a, b, result;
enif_get_int(env, argv[0], &a);
enif_get_int(env, argv[1], &b);
result = multiplicar(a, b);
return enif_make_int(env, result);
}
static ERL_NIF_TERM dividir_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
int a, b, result;
enif_get_int(env, argv[0], &a);
enif_get_int(env, argv[1], &b);
result = dividir(a, b);
return enif_make_int(env, result);
}
static ErlNifFunc nif_funcs[] = {
{"somar", 2, somar_nif},
{"subtrair", 2, subtrair_nif},
{"multiplicar", 2, multiplicar_nif},
{"dividir", 2, dividir_nif},
};
ERL_NIF_INIT(Elixir.Calc, nif_funcs, NULL, NULL, NULL, NULL)
#include <erl_nif.h>
erlang-dev
package.static ERL_NIF_TERM somar_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
int a, b, result;
enif_get_int(env, argv[0], &a);
enif_get_int(env, argv[1], &b);
result = somar(a, b);
return enif_make_int(env, result);
}
static
function called somar_nif
that returns an ERL_NIF_TERM
(a type that represent any Erlang term), and expects three arguments:ErlNifEnv* env
is a pointer that represents an environment that can host Erlang terms. Let's consider it as the environment that is running our NIFint argc
contains the number of arguments that was passed to the functionconst ERL_NIF_TERM argv[]
are the arguments passed to the functionmain
function in any C programint main(int argc, char ** argv)
argv
to get the values passed to your function, based on the number or arguments contained in argc
.// Our C variables
int a, b, result;
// Reads the first value and stores it in a
enif_get_int(env, argv[0], &a);
// Reads the second value and stores it in b
enif_get_int(env, argv[1], &b);
// Our lib_calc function been called!
result = somar(a, b);
// Transforms the result into an ERL_NIF_TERM and returns it
return enif_make_int(env, result);
argc
is being totally ignored, as we already known that exactly 2 values are being passed as arguments.static ErlNifFunc nif_funcs[] = {
{"somar", 2, somar_nif},
{"subtrair", 2, subtrair_nif},
{"multiplicar", 2, multiplicar_nif},
{"dividir", 2, dividir_nif},
};
ERL_NIF_INIT(Elixir.Calc, nif_funcs, NULL, NULL, NULL, NULL)
static ErlNifFunc nif_funcs[]
is an array of ErlNifFunc
struct. This struct is defined as having the following variablesErlNifFunc
struct that is the flags
, but for our example it can be ommited.ERL_NIF_INIT
macro call, passing the module name, the functions that will be exposed in our NIF, and pointer to functions dedicated to treat load, reload, upgrade and unload events (ignored here in our example).Elixir.Calc
, and not just Calc
. That's necessary because our goal is to use this NIF in an Elixir module, and all Elixir modules from the Erlang perspective starts with Elixir.
.$ gcc -shared -o lib_calc_nif.so -fPIC lib_calc_nif.c lib_calc.so
-fPIC
flag passed to gcc
. This is to inform gcc
to create a Position Independent Code, which will generate an assembly code with relative addresses references.calc.ex
defmodule Calc do
@on_load :load_nifs
def load_nifs do
:erlang.load_nif('./lib_calc_nif', 0)
end
def somar(_a, _b) do
raise "NIF somar not implemented"
end
def subtrair(_a, _b) do
raise "NIF subtrair not implemented"
end
def multiplicar(_a, _b) do
raise "NIF multiplicar not implemented"
end
def dividir(_a, _b) do
raise "NIF dividir not implemented"
end
end
@on_load :load_nifs
def load_nifs do
:erlang.load_nif('./lib_calc_nif', 0)
end
@on_load :load_nifs
), and the callback will load our NIF (:erlang.load_nif('./lib_calc_nif', 0)
). Let's see it in action!$ iex
Erlang/OTP 22 [erts-10.6.4] [source] [64-bit] [smp:12:12] [ds:12:12:10] [async-threads:1]
Interactive Elixir (1.12.0) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> c("Calc.ex")
[Calc]
iex(2)> Calc.somar(1, 2)
3
iex(3)>
lib_calc.h
int somar(int a, int b);
lib_calc_nif.c
enif_get_int(env, argv[0], &a);
iex(3)> Calc.somar("test", 1)
32753
iex(7)> Calc.somar(1.0,1)
32753
iex(8)> Calc.somar(1,1.0)
1251267553
iex(9)>
iex(8)> Calc.somar(1,1.0)
1251267553
iex(9)> Calc.somar(1,1.0)
1251267553
iex(10)> Calc.somar(1,1.0)
1251531297
iex(11)> Calc.somar(1,1.0)
1251531297
iex(12)> Calc.somar(1,1.0)
1251288249
iex(13)>
iex(13)> Calc.dividir(1,0)
[1] 501 floating point exception iex