5 Erl_Interface
This is an example of how to solve the example problem by using a port and
erl_interface
. It is necessary to read the port example before reading this chapter.5.1 Erlang Program
Compared to the Erlang module complex1.erl used for the plain port, there are two differences when using
erl_interface
on the C side: Sinceerl_interface
operates on the Erlang external term format the port must be set to use binaries and, instead of inventing an encoding/decoding scheme, the BIFsterm_to_binary/1
andbinary_to_term/1
should be used. That is:open_port({spawn, ExtPrg}, [{packet, 2}])is replaced with:
open_port({spawn, ExtPrg}, [{packet, 2}, binary])And:
Port ! {self(), {command, encode(Msg)}}, receive {Port, {data, Data}} -> Caller ! {complex, decode(Data)} endis replaced with:
Port ! {self(), {command, term_to_binary(Msg)}}, receive {Port, {data, Data}} -> Caller ! {complex, binary_to_term(Data)} endThe resulting Erlang program can be found in complex2.erl . Note that calling
complex2:foo/1
andcomplex2:bar/1
will result in the tuple{foo,X}
or{bar,Y}
being sent to thecomplex
process, which will code them as binaries and send them to the port. This means that the C program must be able to handle these two tuples.5.2 C Program
Compared to the C program port.c used for the plain port the
while
-loop must be rewritten. Messages coming from the port will be on the Erlang external term format. They should be converted into anETERM
struct, a C struct similar to an Erlang term. The result of callingfoo()
orbar()
must be converted to the Erlang external term format before being sent back to the port. But before calling any othererl_interface
function, the memory handling must be initiated.erl_init(NULL, 0);For reading from and writing to the port the functions
read_cmd()
andwrite_cmd()
from erl_comm.c can still be used. The functionerl_decode()
fromerl_marshal
will convert the binary into anETERM
struct.int main() { ETERM *tuplep; while (read_cmd(buf) > 0) { tuplep = erl_decode(buf);In this case
tuplep
now points to anETERM
struct representing a tuple with two elements; the function name (atom) and the argument (integer). By using the functionerl_element()
fromerl_eterm
it is possible to extract these elements, which also must be declared as pointers to anETERM
struct.fnp = erl_element(1, tuplep); argp = erl_element(2, tuplep);The macros
ERL_ATOM_PTR
andERL_INT_VALUE
fromerl_eterm
can be used to obtain the actual values of the atom and the integer. The atom value is represented as a string. By comparing this value with the strings "foo" and "bar" it can be decided which function to call.if (strncmp(ERL_ATOM_PTR(fnp), "foo", 3) == 0) { res = foo(ERL_INT_VALUE(argp)); } else if (strncmp(ERL_ATOM_PTR(fnp), "bar", 3) == 0) { res = bar(ERL_INT_VALUE(argp)); }Now an
ETERM
struct representing the integer result can be constructed using the functionerl_mk_int()
fromerl_eterm
. It is also possible to use the functionerl_format()
from the moduleerl_format
.intp = erl_mk_int(res);The resulting
ETERM
struct is converted into the Erlang external term format using the functionerl_encode()
fromerl_marshal
and sent to Erlang usingwrite_cmd()
.erl_encode(intp, buf); write_cmd(buf, erl_eterm_len(intp));Last, the memory allocated by the
ETERM
creating functions must be freed.erl_free_compound(tuplep); erl_free_term(fnp); erl_free_term(argp); erl_free_term(intp);The resulting C program can be found in ei.c .
5.3 Running the Example
1. Compile the C code, providing the paths to the include files
erl_interface.h
andei.h
, and to the librarieserl_interface
andei
.unix> gcc -o extprg -I/usr/local/otp/lib/erl_interface-3.2.1/include \ -L/usr/local/otp/lib/erl_interface-3.2.1/lib \ complex.c erl_comm.c ei.c -lerl_interface -leiIn R5B and later versions of OTP, the
include
andlib
directories are situated underOTPROOT/lib/erl_interface-VSN
, where OTPROOT is the root directory of the OTP installation (/usr/local/otp
in the example above) and VSN is the version of theerl_interface
application (3.2.1 in the example above).
In R4B and earlier versions of OTP,include
andlib
are situated underOTPROOT/usr
.2. Start Erlang and compile the Erlang code.
unix> erl Erlang (BEAM) emulator version 4.9.1.2 Eshell V4.9.1.2 (abort with ^G) 1> c(complex2). {ok,complex2}3. Run the example.
2> complex2:start("extprg"). <0.34.0> 3> complex2:foo(3). 4 4> complex2:bar(5). 10 5> complex2:bar(352). 704 6> complex2:stop(). stop