<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html xmlns:fn="http://www.w3.org/2005/02/xpath-functions"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <link rel="stylesheet" href="../otp_doc.css" type="text/css"> <title>Erlang -- Erl_Interface</title> </head> <body bgcolor="white" text="#000000" link="#0000ff" vlink="#ff00ff" alink="#ff0000"><div id="container"> <script id="js" type="text/javascript" language="JavaScript" src="../js/flipmenu/flipmenu.js"></script><script id="js2" type="text/javascript" src="../js/erlresolvelinks.js"></script><script language="JavaScript" type="text/javascript"> <!-- function getWinHeight() { var myHeight = 0; if( typeof( window.innerHeight ) == 'number' ) { //Non-IE myHeight = window.innerHeight; } else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) { //IE 6+ in 'standards compliant mode' myHeight = document.documentElement.clientHeight; } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) { //IE 4 compatible myHeight = document.body.clientHeight; } return myHeight; } function setscrollpos() { var objf=document.getElementById('loadscrollpos'); document.getElementById("leftnav").scrollTop = objf.offsetTop - getWinHeight()/2; } function addEvent(obj, evType, fn){ if (obj.addEventListener){ obj.addEventListener(evType, fn, true); return true; } else if (obj.attachEvent){ var r = obj.attachEvent("on"+evType, fn); return r; } else { return false; } } addEvent(window, 'load', setscrollpos); //--></script><div id="leftnav"><div class="innertube"> <img alt="Erlang logo" src="../erlang-logo.png"><br><small><a href="users_guide.html">User's Guide</a><br><a href="../pdf/otp-system-documentation-5.7.5.pdf">PDF</a><br><a href="../index.html">Top</a></small><p><strong>Interoperability Tutorial</strong><br><strong>User's Guide</strong><br><small>Version 5.7.5</small></p> <br><a href="javascript:openAllFlips()">Expand All</a><br><a href="javascript:closeAllFlips()">Contract All</a><p><small><strong>Chapters</strong></small></p> <ul class="flipMenu" imagepath="../js/flipmenu"> <li id="no" title="Introduction" expanded="false">Introduction<ul> <li><a href="introduction.html"> Top of chapter </a></li> <li title="Purpose"><a href="introduction.html#id2259115">Purpose</a></li> <li title="Prerequisites"><a href="introduction.html#id2259373">Prerequisites</a></li> </ul> </li> <li id="no" title="Overview" expanded="false">Overview<ul> <li><a href="overview.html"> Top of chapter </a></li> <li title="Built-In Mechanisms"><a href="overview.html#id2261381">Built-In Mechanisms</a></li> <li title="C and Java Libraries"><a href="overview.html#id2259965">C and Java Libraries</a></li> <li title="Standard Protocols"><a href="overview.html#id2258954">Standard Protocols</a></li> <li title="IC"><a href="overview.html#id2257816">IC</a></li> <li title="Old Applications"><a href="overview.html#id2257830">Old Applications</a></li> </ul> </li> <li id="no" title="Problem Example" expanded="false">Problem Example<ul> <li><a href="example.html"> Top of chapter </a></li> <li title="Description"><a href="example.html#id2259285">Description</a></li> </ul> </li> <li id="no" title="Ports" expanded="false">Ports<ul> <li><a href="c_port.html"> Top of chapter </a></li> <li title="Erlang Program"><a href="c_port.html#id2260423">Erlang Program</a></li> <li title="C Program"><a href="c_port.html#id2259164">C Program</a></li> <li title="Running the Example"><a href="c_port.html#id2252162">Running the Example</a></li> </ul> </li> <li id="loadscrollpos" title="Erl_Interface" expanded="true">Erl_Interface<ul> <li><a href="erl_interface.html"> Top of chapter </a></li> <li title="Erlang Program"><a href="erl_interface.html#id2259426">Erlang Program</a></li> <li title="C Program"><a href="erl_interface.html#id2253521">C Program</a></li> <li title="Running the Example"><a href="erl_interface.html#id2255204">Running the Example</a></li> </ul> </li> <li id="no" title="Port drivers" expanded="false">Port drivers<ul> <li><a href="c_portdriver.html"> Top of chapter </a></li> <li title="Port Drivers"><a href="c_portdriver.html#id2260907">Port Drivers</a></li> <li title="Erlang Program"><a href="c_portdriver.html#id2260932">Erlang Program</a></li> <li title="C Driver"><a href="c_portdriver.html#id2261082">C Driver</a></li> <li title="Running the Example"><a href="c_portdriver.html#id2261188">Running the Example</a></li> </ul> </li> <li id="no" title="C Nodes" expanded="false">C Nodes<ul> <li><a href="cnode.html"> Top of chapter </a></li> <li title="Erlang Program"><a href="cnode.html#id2261320">Erlang Program</a></li> <li title="C Program"><a href="cnode.html#id2256889">C Program</a></li> <li title="Running the Example"><a href="cnode.html#id2257312">Running the Example</a></li> </ul> </li> </ul> </div></div> <div id="content"> <div class="innertube"> <h1>5 Erl_Interface</h1> <p>This is an example of how to solve the <span class="bold_code"><a href="example.html">example problem</a></span> by using a port and <span class="code">erl_interface</span>. It is necessary to read the <span class="bold_code"><a href="c_port.html">port example</a></span> before reading this chapter.</p> <h3><a name="id2259426">5.1 Erlang Program</a></h3> <p>The example below shows an Erlang program communicating with a C program over a plain port with home made encoding.</p> <div class="example"><pre> -module(complex1). -export([start/1, stop/0, init/1]). -export([foo/1, bar/1]). start(ExtPrg) -> spawn(?MODULE, init, [ExtPrg]). stop() -> complex ! stop. foo(X) -> call_port({foo, X}). bar(Y) -> call_port({bar, Y}). call_port(Msg) -> complex ! {call, self(), Msg}, receive {complex, Result} -> Result end. init(ExtPrg) -> register(complex, self()), process_flag(trap_exit, true), Port = open_port({spawn, ExtPrg}, [{packet, 2}]), loop(Port). loop(Port) -> receive {call, Caller, Msg} -> Port ! {self(), {command, encode(Msg)}}, receive {Port, {data, Data}} -> Caller ! {complex, decode(Data)} end, loop(Port); stop -> Port ! {self(), close}, receive {Port, closed} -> exit(normal) end; {'EXIT', Port, Reason} -> exit(port_terminated) end. encode({foo, X}) -> [1, X]; encode({bar, Y}) -> [2, Y]. decode([Int]) -> Int. </pre></div> <p>Compared to the Erlang module above 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 <span class="code">term_to_binary/1</span> and <span class="code">binary_to_term/1</span> should be used. That is:</p> <div class="example"><pre> open_port({spawn, ExtPrg}, [{packet, 2}])</pre></div> <p>is replaced with:</p> <div class="example"><pre> open_port({spawn, ExtPrg}, [{packet, 2}, binary])</pre></div> <p>And:</p> <div class="example"><pre> Port ! {self(), {command, encode(Msg)}}, receive {Port, {data, Data}} -> Caller ! {complex, decode(Data)} end</pre></div> <p>is replaced with:</p> <div class="example"><pre> Port ! {self(), {command, term_to_binary(Msg)}}, receive {Port, {data, Data}} -> Caller ! {complex, binary_to_term(Data)} end</pre></div> <p>The resulting Erlang program is shown below.</p> <div class="example"><pre> -module(complex2). -export([start/1, stop/0, init/1]). -export([foo/1, bar/1]). start(ExtPrg) -> spawn(?MODULE, init, [ExtPrg]). stop() -> complex ! stop. foo(X) -> call_port({foo, X}). bar(Y) -> call_port({bar, Y}). call_port(Msg) -> complex ! {call, self(), Msg}, receive {complex, Result} -> Result end. init(ExtPrg) -> register(complex, self()), process_flag(trap_exit, true), Port = open_port({spawn, ExtPrg}, [{packet, 2}, binary]), loop(Port). loop(Port) -> receive {call, Caller, Msg} -> Port ! {self(), {command, term_to_binary(Msg)}}, receive {Port, {data, Data}} -> Caller ! {complex, binary_to_term(Data)} end, loop(Port); stop -> Port ! {self(), close}, receive {Port, closed} -> exit(normal) end; {'EXIT', Port, Reason} -> exit(port_terminated) end. </pre></div> <p>Note that calling <span class="code">complex2:foo/1</span> and <span class="code">complex2:bar/1</span> will result in the tuple <span class="code">{foo,X}</span> or <span class="code">{bar,Y}</span> being sent to the <span class="code">complex</span> 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.</p> <h3><a name="id2253521">5.2 C Program</a></h3> <p>The example below shows a C program communicating with an Erlang program over a plain port with home made encoding.</p> <div class="example"><pre> /* port.c */ typedef unsigned char byte; int main() { int fn, arg, res; byte buf[100]; while (read_cmd(buf) > 0) { fn = buf[0]; arg = buf[1]; if (fn == 1) { res = foo(arg); } else if (fn == 2) { res = bar(arg); } buf[0] = res; write_cmd(buf, 1); } } </pre></div> <p>Compared to the C program above used for the plain port the <span class="code">while</span>-loop must be rewritten. Messages coming from the port will be on the Erlang external term format. They should be converted into an <span class="code">ETERM</span> struct, a C struct similar to an Erlang term. The result of calling <span class="code">foo()</span> or <span class="code">bar()</span> must be converted to the Erlang external term format before being sent back to the port. But before calling any other <span class="code">erl_interface</span> function, the memory handling must be initiated.</p> <div class="example"><pre> erl_init(NULL, 0);</pre></div> <p>For reading from and writing to the port the functions <span class="code">read_cmd()</span> and <span class="code">write_cmd()</span> from the erl_comm.c example below can still be used. </p> <div class="example"><pre> /* erl_comm.c */ typedef unsigned char byte; read_cmd(byte *buf) { int len; if (read_exact(buf, 2) != 2) return(-1); len = (buf[0] << 8) | buf[1]; return read_exact(buf, len); } write_cmd(byte *buf, int len) { byte li; li = (len >> 8) & 0xff; write_exact(&li, 1); li = len & 0xff; write_exact(&li, 1); return write_exact(buf, len); } read_exact(byte *buf, int len) { int i, got=0; do { if ((i = read(0, buf+got, len-got)) <= 0) return(i); got += i; } while (got<len); return(len); } write_exact(byte *buf, int len) { int i, wrote = 0; do { if ((i = write(1, buf+wrote, len-wrote)) <= 0) return (i); wrote += i; } while (wrote<len); return (len); } </pre></div> <p>The function <span class="code">erl_decode()</span> from <span class="code">erl_marshal</span> will convert the binary into an <span class="code">ETERM</span> struct.</p> <div class="example"><pre> int main() { ETERM *tuplep; while (read_cmd(buf) > 0) { tuplep = erl_decode(buf);</pre></div> <p>In this case <span class="code">tuplep</span> now points to an <span class="code">ETERM</span> struct representing a tuple with two elements; the function name (atom) and the argument (integer). By using the function <span class="code">erl_element()</span> from <span class="code">erl_eterm</span> it is possible to extract these elements, which also must be declared as pointers to an <span class="code">ETERM</span> struct.</p> <div class="example"><pre> fnp = erl_element(1, tuplep); argp = erl_element(2, tuplep);</pre></div> <p>The macros <span class="code">ERL_ATOM_PTR</span> and <span class="code">ERL_INT_VALUE</span> from <span class="code">erl_eterm</span> 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.</p> <div class="example"><pre> 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)); }</pre></div> <p>Now an <span class="code">ETERM</span> struct representing the integer result can be constructed using the function <span class="code">erl_mk_int()</span> from <span class="code">erl_eterm</span>. It is also possible to use the function <span class="code">erl_format()</span> from the module <span class="code">erl_format</span>.</p> <div class="example"><pre> intp = erl_mk_int(res);</pre></div> <p>The resulting <span class="code">ETERM</span> struct is converted into the Erlang external term format using the function <span class="code">erl_encode()</span> from <span class="code">erl_marshal</span> and sent to Erlang using <span class="code">write_cmd()</span>.</p> <div class="example"><pre> erl_encode(intp, buf); write_cmd(buf, erl_eterm_len(intp));</pre></div> <p>Last, the memory allocated by the <span class="code">ETERM</span> creating functions must be freed.</p> <div class="example"><pre> erl_free_compound(tuplep); erl_free_term(fnp); erl_free_term(argp); erl_free_term(intp);</pre></div> <p>The resulting C program is shown below:</p> <div class="example"><pre> /* ei.c */ #include "erl_interface.h" #include "ei.h" typedef unsigned char byte; int main() { ETERM *tuplep, *intp; ETERM *fnp, *argp; int res; byte buf[100]; long allocated, freed; erl_init(NULL, 0); while (read_cmd(buf) > 0) { tuplep = erl_decode(buf); fnp = erl_element(1, tuplep); argp = erl_element(2, tuplep); if (strncmp(ERL_ATOM_PTR(fnp), "foo", 3) == 0) { res = foo(ERL_INT_VALUE(argp)); } else if (strncmp(ERL_ATOM_PTR(fnp), "bar", 17) == 0) { res = bar(ERL_INT_VALUE(argp)); } intp = erl_mk_int(res); erl_encode(intp, buf); write_cmd(buf, erl_term_len(intp)); erl_free_compound(tuplep); erl_free_term(fnp); erl_free_term(argp); erl_free_term(intp); } } </pre></div> <h3><a name="id2255204">5.3 Running the Example</a></h3> <p>1. Compile the C code, providing the paths to the include files <span class="code">erl_interface.h</span> and <span class="code">ei.h</span>, and to the libraries <span class="code">erl_interface</span> and <span class="code">ei</span>.</p> <div class="example"><pre> unix> <span class="bold_code">gcc -o extprg -I/usr/local/otp/lib/erl_interface-3.2.1/include \\ </span> <span class="bold_code"> -L/usr/local/otp/lib/erl_interface-3.2.1/lib \\ </span> <span class="bold_code"> complex.c erl_comm.c ei.c -lerl_interface -lei</span></pre></div> <p>In R5B and later versions of OTP, the <span class="code">include</span> and <span class="code">lib</span> directories are situated under <span class="code">OTPROOT/lib/erl_interface-VSN</span>, where <span class="code">OTPROOT</span> is the root directory of the OTP installation (<span class="code">/usr/local/otp</span> in the example above) and <span class="code">VSN</span> is the version of the <span class="code">erl_interface</span> application (3.2.1 in the example above). <br> In R4B and earlier versions of OTP, <span class="code">include</span> and <span class="code">lib</span> are situated under <span class="code">OTPROOT/usr</span>.</p> <p>2. Start Erlang and compile the Erlang code.</p> <div class="example"><pre> unix> <span class="bold_code">erl</span> Erlang (BEAM) emulator version 4.9.1.2 Eshell V4.9.1.2 (abort with ^G) 1> <span class="bold_code">c(complex2).</span> {ok,complex2}</pre></div> <p>3. Run the example.</p> <div class="example"><pre> 2> <span class="bold_code">complex2:start("extprg").</span> <0.34.0> 3> <span class="bold_code">complex2:foo(3).</span> 4 4> <span class="bold_code">complex2:bar(5).</span> 10 5> <span class="bold_code">complex2:bar(352).</span> 704 6> <span class="bold_code">complex2:stop().</span> stop</pre></div> </div> <div class="footer"> <hr> <p>Copyright © 2000-2010 Ericsson AB. All Rights Reserved.</p> </div> </div> </div></body> </html>