<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html xmlns:erl="http://erlang.org" 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> <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"> <div class="erlang-logo-wrapper"><a href="../index.html"><img alt="Erlang Logo" src="../erlang-logo.png" class="erlang-logo"></a></div> <p class="section-title">Interoperability Tutorial</p> <p class="section-subtitle">User's Guide</p> <p class="section-version">Version 10.2.3</p> <ul class="panel-sections"> <li><a href="users_guide.html">User's Guide</a></li> <li><a href="../pdf/otp-system-documentation-10.2.3.pdf">PDF</a></li> <li><a href="../index.html">Top</a></li> </ul> <ul class="expand-collapse-items"> <li><a href="javascript:openAllFlips()">Expand All</a></li> <li><a href="javascript:closeAllFlips()">Contract All</a></li> </ul> <h3>Chapters</h3> <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#purpose">Purpose</a></li> <li title="Prerequisites"><a href="introduction.html#prerequisites">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#built-in-mechanisms">Built-In Mechanisms</a></li> <li title="C and Java Libraries"><a href="overview.html#c-and-java-libraries">C and Java Libraries</a></li> <li title="Standard Protocols"><a href="overview.html#standard-protocols">Standard Protocols</a></li> <li title="IC"><a href="overview.html#ic">IC</a></li> <li title="Old Applications"><a href="overview.html#old-applications">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#description">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#erlang-program">Erlang Program</a></li> <li title="C Program"><a href="c_port.html#c-program">C Program</a></li> <li title="Running the Example"><a href="c_port.html#running-the-example">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#erlang-program">Erlang Program</a></li> <li title="C Program"><a href="erl_interface.html#c-program">C Program</a></li> <li title="Running the Example"><a href="erl_interface.html#running-the-example">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="Erlang Program"><a href="c_portdriver.html#erlang-program">Erlang Program</a></li> <li title="C Driver"><a href="c_portdriver.html#c-driver">C Driver</a></li> <li title="Running the Example"><a href="c_portdriver.html#running-the-example">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#erlang-program">Erlang Program</a></li> <li title="C Program"><a href="cnode.html#c-program">C Program</a></li> <li title="Running the Example"><a href="cnode.html#running-the-example">Running the Example</a></li> </ul> </li> <li id="no" title="NIFs" expanded="false">NIFs<ul> <li><a href="nif.html"> Top of chapter </a></li> <li title="Erlang Program"><a href="nif.html#erlang-program">Erlang Program</a></li> <li title="NIF Library Code"><a href="nif.html#nif-library-code">NIF Library Code</a></li> <li title="Running the Example"><a href="nif.html#running-the-example">Running the Example</a></li> </ul> </li> </ul> </div></div> <div id="content"> <div class="innertube"> <h1>5 Erl_Interface</h1> <p>This section outlines an example of how to solve the example problem in <span class="bold_code bc-19"><a href="example.html">Problem Example</a></span> by using a port and Erl_Interface. It is necessary to read the port example in <span class="bold_code bc-19"><a href="c_port.html">Ports</a></span> before reading this section.</p> <h3><span onMouseOver="document.getElementById('ghlink-erlang-program-idm281472710422040').style.visibility = 'visible';" onMouseOut="document.getElementById('ghlink-erlang-program-idm281472710422040').style.visibility = 'hidden';"><span id="ghlink-erlang-program-idm281472710422040" class="ghlink"><a href="https://github.com/erlang/otp/edit/maint/system/doc/tutorial/erl_interface.xmlsrc#L39" title="Found an issue with the documentation? Fix it by clicking here!"><span class="pencil"></span></a></span><a class="title_link" name="erlang-program" href="#erlang-program">5.1 Erlang Program</a></span></h3> <p>The following example 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>There are two differences when using Erl_Interface on the C side compared to the example in <span class="bold_code bc-19"><a href="c_port.html">Ports</a></span>, using only the plain port:</p> <ul> <li>As Erl_Interface operates on the Erlang external term format, the port must be set to use binaries.</li> <li>Instead of inventing an encoding/decoding scheme, the <span class="code">term_to_binary/1</span> and <span class="code">binary_to_term/1</span> BIFs are to be used.</li> </ul> <p>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 as follows:</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>Notice that calling <span class="code">complex2:foo/1</span> and <span class="code">complex2:bar/1</span> results 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 codes them as binaries and sends them to the port. This means that the C program must be able to handle these two tuples.</p> <h3><span onMouseOver="document.getElementById('ghlink-c-program-idm281472710406472').style.visibility = 'visible';" onMouseOut="document.getElementById('ghlink-c-program-idm281472710406472').style.visibility = 'hidden';"><span id="ghlink-c-program-idm281472710406472" class="ghlink"><a href="https://github.com/erlang/otp/edit/maint/system/doc/tutorial/erl_interface.xmlsrc#L83" title="Found an issue with the documentation? Fix it by clicking here!"><span class="pencil"></span></a></span><a class="title_link" name="c-program" href="#c-program">5.2 C Program</a></span></h3> <p>The following example 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 in <span class="bold_code bc-19"><a href="c_port.html">Ports</a></span>, using only the plain port, the <span class="code">while</span>-loop must be rewritten. Messages coming from the port is on the Erlang external term format. They must be converted into an <span class="code">ETERM</span> struct, which is 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 Erl_Interface function, the memory handling must be initiated:</p> <div class="example"><pre> erl_init(NULL, 0);</pre></div> <p>The following functions, <span class="code">read_cmd()</span> and <span class="code">write_cmd()</span>, from the <span class="code">erl_comm.c</span> example in <span class="bold_code bc-19"><a href="c_port.html">Ports</a></span> can still be used for reading from and writing to the port: </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> converts 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>Here, <span class="code">tuplep</span> points to an <span class="code">ETERM</span> struct representing a tuple with two elements; the function name (atom) and the argument (integer). Using the function <span class="code">erl_element()</span> from <span class="code">erl_eterm</span>, these elements can be extracted, but they must also 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 that represents the integer result can be constructed using the function <span class="code">erl_mk_int()</span> from <span class="code">erl_eterm</span>. The function <span class="code">erl_format()</span> from the module <span class="code">erl_format</span> can also be used:</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>Finally, 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 as follows:</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", 3) == 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><span onMouseOver="document.getElementById('ghlink-running-the-example-idm281472710270936').style.visibility = 'visible';" onMouseOut="document.getElementById('ghlink-running-the-example-idm281472710270936').style.visibility = 'hidden';"><span id="ghlink-running-the-example-idm281472710270936" class="ghlink"><a href="https://github.com/erlang/otp/edit/maint/system/doc/tutorial/erl_interface.xmlsrc#L159" title="Found an issue with the documentation? Fix it by clicking here!"><span class="pencil"></span></a></span><a class="title_link" name="running-the-example" href="#running-the-example">5.3 Running the Example</a></span></h3> <p><strong>Step 1.</strong> Compile the C code. This provides the paths to the include files <span class="code">erl_interface.h</span> and <span class="code">ei.h</span>, and also 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 bc-12">gcc -o extprg -I/usr/local/otp/lib/erl_interface-3.9.2/include \\ </span> <span class="bold_code bc-12"> -L/usr/local/otp/lib/erl_interface-3.9.2/lib \\ </span> <span class="bold_code bc-12"> complex.c erl_comm.c ei.c -lerl_interface -lei -lpthread</span></pre></div> <p>In Erlang/OTP 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 recent example) and <span class="code">VSN</span> is the version of the Erl_interface application (3.2.1 in the recent example).</p> <p>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><strong>Step 2.</strong> Start Erlang and compile the Erlang code:</p> <div class="example"><pre> unix> <span class="bold_code bc-12">erl</span> Erlang (BEAM) emulator version 4.9.1.2 Eshell V4.9.1.2 (abort with ^G) 1> <span class="bold_code bc-12">c(complex2).</span> {ok,complex2}</pre></div> <p><strong>Step 3.</strong> Run the example:</p> <div class="example"><pre> 2> <span class="bold_code bc-12">complex2:start("./extprg").</span> <0.34.0> 3> <span class="bold_code bc-12">complex2:foo(3).</span> 4 4> <span class="bold_code bc-12">complex2:bar(5).</span> 10 5> <span class="bold_code bc-12">complex2:bar(352).</span> 704 6> <span class="bold_code bc-12">complex2:stop().</span> stop</pre></div> </div> <div class="footer"> <hr> <p>Copyright © 2000-2019 Ericsson AB. All Rights Reserved.</p> </div> </div> </div> <script type="text/javascript">window.__otpTopDocDir = '../js/';</script><script type="text/javascript" src="../js/highlight.js"></script> </body> </html>