Erlang 21 - 5. Erl_Interface

5 एर्ल_इंटरफेस




erlang

5 एर्ल_इंटरफेस

यह खंड पोर्ट और Erl_Interface का उपयोग करके Problem Example में Problem Example का समाधान करने के तरीके का एक Problem Example बताता है। इस खंड को पढ़ने से पहले Ports में पोर्ट उदाहरण को पढ़ना आवश्यक है।

5.1 एर्लांग कार्यक्रम

निम्न उदाहरण एक एरलैंग प्रोग्राम को दर्शाता है कि सी प्रोग्राम के साथ एक सादे बंदरगाह पर घर के बने एन्कोडिंग के साथ संवाद कर रहा है:

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

Ports में उदाहरण की तुलना में सी तरफ Erl_Interface का उपयोग करते समय दो अंतर हैं, केवल सादे पोर्ट का उपयोग करके:

  • जैसा कि Erl_Interface Erlang बाहरी शब्द प्रारूप पर चल रहा है, पोर्ट को बायनेरिज़ का उपयोग करने के लिए सेट किया जाना चाहिए।
  • एन्कोडिंग / डिकोडिंग योजना का आविष्कार करने के बजाय, term_to_binary/1 और binary_to_term/1 BIF का उपयोग किया जाना है।

अर्थात्:

open_port({spawn, ExtPrg}, [{packet, 2}])

इसके साथ प्रतिस्थापित किया गया है:

open_port({spawn, ExtPrg}, [{packet, 2}, binary])

तथा:

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

इसके साथ प्रतिस्थापित किया गया है:

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

परिणामस्वरूप Erlang कार्यक्रम इस प्रकार है:

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

ध्यान दें कि कॉलिंग complex2:foo/1 और complex2:bar/1 परिणामस्वरूप टपल {foo,X} या {bar,Y} को complex प्रक्रिया में भेजा जा रहा है, जो उन्हें बायनेरिज़ के रूप में कोड करता है और पोर्ट पर भेजता है। इसका मतलब यह है कि सी प्रोग्राम को इन दो ट्यूपलों को संभालने में सक्षम होना चाहिए।

5.2 सी कार्यक्रम

निम्न उदाहरण एक सी प्रोग्राम दिखाता है जिसमें घर के बने एन्कोडिंग के साथ एक सादे बंदरगाह पर एक एरलांग कार्यक्रम के साथ संवाद होता है:

/* 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);
  }
}

Ports में सी कार्यक्रम की तुलना में, केवल सादे बंदरगाह का उपयोग while , while -लूप को फिर से लिखा जाना चाहिए। पोर्ट से आने वाले संदेश एर्लांग बाहरी शब्द प्रारूप पर हैं। उन्हें एक ETERM संरचना में परिवर्तित किया जाना चाहिए, जो एक Erlang शब्द के समान C संरचना है। पोर्ट में वापस भेजे जाने से पहले कॉलिंग foo() या bar() को एर्लांग बाहरी शब्द प्रारूप में परिवर्तित किया जाना चाहिए। लेकिन किसी भी अन्य Erl_Interface फ़ंक्शन को कॉल करने से पहले, मेमोरी हैंडलिंग को आरंभ करना होगा:

erl_init(NULL, 0);

Ports में erl_comm.c उदाहरण से read_cmd() और write_cmd() निम्न कार्य अभी भी Ports से पढ़ने और लिखने के लिए उपयोग किया जा सकता है:

/* 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);
}

erl_decode() फ़ंक्शन erl_decode() बाइनरी को एक ETERM संरचना में परिवर्तित करता है:

int main() {
  ETERM *tuplep;

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

यहाँ, दो तत्वों के साथ एक tuple का प्रतिनिधित्व करने वाले ETERM संरचना की tuplep इंगित करता है; फ़ंक्शन नाम (परमाणु) और तर्क (पूर्णांक)। erl_element() से फ़ंक्शन erl_element() का उपयोग करते हुए, इन तत्वों को निकाला जा सकता है, लेकिन उन्हें ETERM संरचना के संकेत के रूप में भी घोषित किया जाना चाहिए:

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

ERL_INT_VALUE से मैक्रोज़ ERL_ATOM_PTR और ERL_INT_VALUE उपयोग परमाणु और पूर्णांक के वास्तविक मूल्यों को प्राप्त करने के लिए किया जा सकता है। परमाणु मूल्य को एक स्ट्रिंग के रूप में दर्शाया जाता है। इस मूल्य की तुलना "फू" और "बार" से करते हुए, यह तय किया जा सकता है कि किस फ़ंक्शन को कॉल करना है:

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));
}

अब पूर्णांक परिणाम का प्रतिनिधित्व करने वाली एक ETERM संरचना का निर्माण erl_mk_int() erl_eterm से उपयोग करके किया जा सकता है। मॉड्यूल erl_format से फ़ंक्शन erl_format() भी उपयोग किया जा सकता है:

intp = erl_mk_int(res);

परिणामी ETERM संरचना को erl_encode() से erl_marshal erl_encode() फ़ंक्शन का उपयोग करके Erlang बाहरी शब्द प्रारूप में परिवर्तित किया जाता है और write_cmd() का उपयोग करके Erlang को भेजा जाता है:

erl_encode(intp, buf);
write_cmd(buf, erl_eterm_len(intp));

अंत में, ETERM द्वारा ETERM कार्यों को आवंटित की गई मेमोरी को मुक्त किया जाना चाहिए:

erl_free_compound(tuplep);
erl_free_term(fnp);
erl_free_term(argp);
erl_free_term(intp);

परिणामस्वरूप सी कार्यक्रम इस प्रकार है:

/* 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);
  }
}

5.3 उदाहरण चलाना

चरण 1. सी कोड संकलित करें। यह erl_interface.h और ei.h फ़ाइलों को शामिल करने के लिए पथ प्रदान करता है, और पुस्तकालयों erl_interface और ei :

unix> gcc -o extprg -I/usr/local/otp/lib/erl_interface-3.9.2/include \\
-L/usr/local/otp/lib/erl_interface-3.9.2/lib \\
complex.c erl_comm.c ei.c -lerl_interface -lei -lpthread

Erlang / OTP R5B और OTP के बाद के संस्करणों में, OTPROOT/lib/erl_interface-VSN अंतर्गत include और OTPROOT/lib/erl_interface-VSN निर्देशिकाएँ स्थित हैं, जहाँ OTPROOT हाल के उदाहरण में OTP स्थापना ( /usr/local/otp ) की मूल निर्देशिका है और VSN Erl_interface एप्लिकेशन (हाल के उदाहरण में 3.2.1) का संस्करण है।

R4B और OTP के पुराने संस्करणों में, include और lib OTPROOT/usr अंतर्गत स्थित हैं।

चरण 2. Erlang प्रारंभ करें और Erlang कोड संकलित करें:

unix> erl
Erlang (BEAM) emulator version 4.9.1.2

Eshell V4.9.1.2 (abort with ^G)
1> c(complex2).
{ok,complex2}

चरण 3. उदाहरण चलाएँ:

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