Erl_Interface

## 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-2006 Ericsson AB