> ## 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: Since Erl_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 BIFs `term_to_binary/1` and `binary_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)}
>     end

> is replaced with:

>     Port ! {self(), {command, term_to_binary(Msg)}},
>     receive
>       {Port, {data, Data}} ->
>         Caller ! {complex, binary_to_term(Data)}
>     end

> The resulting Erlang program can be found in complex2.erl . Note that calling `complex2:foo/1` and `complex2:bar/1` will result in the tuple `{foo,X}` or `{bar,Y}` being sent to the `complex` 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 an `ETERM` struct, a C struct similar to an Erlang term. The result of calling `foo()` or `bar()` must be converted to the Erlang external term format before being sent back to the port. But before calling any other `erl_interface` function, the memory handling must be initiated.

>     erl_init(NULL, 0);

> For reading from and writing to the port the functions `read_cmd()` and `write_cmd()` from erl_comm.c can still be used. The function `erl_decode()` from `erl_marshal` will convert the binary into an `ETERM` struct.

>     int main() {
>       ETERM *tuplep;

>       while (read_cmd(buf) > 0) {
>         tuplep = erl_decode(buf);

> In this case `tuplep` now points to an `ETERM` struct representing a tuple with two elements; the function name (atom) and the argument (integer). By using the function `erl_element()` from `erl_eterm` it is possible to extract these elements, which also must be declared as pointers to an `ETERM` struct.

>         fnp = erl_element(1, tuplep);
>         argp = erl_element(2, tuplep);

> The macros `ERL_ATOM_PTR` and `ERL_INT_VALUE` from `erl_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 function `erl_mk_int()` from `erl_eterm`. It is also possible to use the function `erl_format()` from the module `erl_format`.

>         intp = erl_mk_int(res);

> The resulting `ETERM` struct is converted into the Erlang external term format using the function `erl_encode()` from `erl_marshal` and sent to Erlang using `write_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` and `ei.h`, and to the libraries `erl_interface` and `ei`.

>     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 -lei

> In R5B and later versions of OTP, the `include` and `lib` directories are situated under `OTPROOT/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 the `erl_interface` application (3.2.1 in the example above).
> In R4B and earlier versions of OTP, `include` and `lib` are situated under `OTPROOT/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

Copyright © 1991-2003 Ericsson Utvecklings AB