3

免責事項: 質問の作成者は、Erlang の平均的な知識と、C の基本的な (ただし増加中の) 知識を持っています。

この関数を使用して、プログラムがInteroperability Tutorial User Guideで提供されている Erlang ポートの例から取得しread()たバイトを読み取ります(「Erlang Programming」本の第 12 章にも説明されています)。port.c

しかし、私が得た間違った値 (例えば、999 ではなく 231) は C 側から来ているため、この質問は Erlang にまったく関係がないと考える傾向があります。

問題は、このプロトコルが 255 を超えるパラメータでは機能しないことです (それ以外の場合はうまく機能します)。byte型と実装に関係があると思いますが、それを修正する方法や、値read_exact()を渡すことができるようにする方法がわかりません。float

このコードを理解するために K&R の本を半分読みましたが、まだ行き詰まっています。

コードは次のとおりです。

実際の C 関数:

/* complex.c */

int foo(int x) {
  return x+1;
}

int bar(int y) {
  return y*2;
}

C ポート:

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

バッファ管理:

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

アーラン ポート:

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

私は変わろうとする愚かな試みをしました

typedef unsigned char byte;

typedef int byte;

しかし、うまくいきませんでした。

実際には、次の 2 つの問題があります。

  • 255 より大きいパラメータでポートを呼び出すと (たとえばfoo(256)、Erlang ポートから)、実行は read_cmd() 内の i = 0 の read() で終了します。
  • 255 未満のパラメーターでポートを呼び出したが、関数の結果が 255 を超えた場合 (たとえばint foo(int x) { return x+1000; }、プログラムは終了しませんが、Erlang ポートに予期しない値が返されます。

問題は、プロトコルをより大きな数や浮動小数点数で動作させるにはどうすればよいかということです。

4

3 に答える 3

2

s をより大きな構造にマーシャリングする例byteは、既にに示されていread_cmdます。

...
  int len;

  if (read_exact(buf, 2) != 2)
    return(-1);
  len = (buf[0] << 8) | buf[1];
...

これは から最初の 2 つbyteの を取り出し、それらを(4つの構造)bufとして扱います。thisによると、 aは 8 バイトなので、理論的には同じアプローチを適用できるはずです。次のようなことをしたいでしょう(テストされていない、動作しないことがほぼ保証されている、説明のみを目的としているなど):intbytedouble

double marshall_eight(byte *buff) {
  int i;
  double tmp, res;
  for(i=0; i<8;i++) {
    tmp = buff[i] << 8*i;
    res += tmp;
  }
  return res;
}
于 2012-05-13T23:05:06.787 に答える
2

遭遇する可能性のあるマシンのバイトは、-128 から 127 (符号付き) または 0 から 255 (符号なし) の値しか保持できません。それ以外は、2 つの間でデータをマーシャリングする必要があります。 http://www.erlang.org/documentation/doc-5.6/pdf/tutorial.pdfは、Erlang と他の言語の間でデータをマーシャリングするための公式の Erlang チュートリアルで、C の例が含まれています。

于 2012-05-13T18:17:05.973 に答える
1

Erlang からの変換が文字配列ではなくバイト配列であると仮定します。

コードは少し緩い、最も簡単な答えです...

while (read_cmd(buf) > 0) { 
 fn = buf[0];
    arg = buf[1];

になるはずだった:

while (arg=read_cmd(buf) > 0) {
 fn = buf[0];

あなたのコードは、bytes と int のみを処理し、float と double は処理できません。

書き込みコマンドに追加のエラーがあります。

------------編集----------------- cで達成しようとしているものの例を次に示します。

 #include <stdio.h>
 #include <math.h>

 /*
  * With certain compilers  __attribute__((transparent_union)) 
  * can be used to ease handling of an unknown type.
  */
 union  u_unionalue
 {
     char                char_union;
     unsigned char       u_char_union;
     short               s_int_union;
     unsigned short      us_int_union;
     int                 int_union;
     unsigned int        u_int_union;
     long                l_int_union;
     unsigned long       ul_int_union;
     long long           ll_int_union;
     unsigned long long  ull_int_union;
     float               float_union;
     double              double_union;
     long double         l_double_union;
 };

 enum e_type
 {
     char_enum    ,
     u_char_enum  ,
     s_int_enum   ,
     us_int_enum  ,
     int_enum     ,
     u_int_enum   ,
     l_int_enum   ,
     ul_int_enum  ,
     ll_int_enum  ,
     ull_int_enum ,
     float_enum   ,
     double_enum  ,
     l_double_enum,
     last_type
 };

 struct s_type
 {
     int  type;
     char *name;
     union u_unionalue value;
     int   size;
     char *stringFormat;

 } as_typeList[]=
 /**
  * This is a quick example of how convoluted type handling can be in C. The 
  * non portable  __attribute__((transparent_union)) can be useful if the 
  * complier supports it. This helps to 
  * reduce the amount of casting, but these are the convoluted tricks that 
  * occur behind the scenes. C++ has to handle this in the compiler as well
  * as a result .. sometimes what you get is not what you expect.
  */
 {
     { char_enum    ,  "char"              ,  {.char_union=(1 << (-1 + sizeof( char ) * 8  ))}, sizeof( char ),"%+d" },
     { u_char_enum  ,  "unsigned char"     ,  {.u_char_union=-1} , sizeof( unsigned char ) ,"%+d" },
     { s_int_enum   ,  "short"             ,  {.s_int_union=((short)1 << (-1 + sizeof( short ) * 8))}  , sizeof( short ),"%+d" },
     { us_int_enum  ,  "unsigned short"    ,  {.us_int_union=-1}, sizeof( unsigned short ),"%+u"  },
     { int_enum     ,  "int"               ,  {.int_union = ((int)1<< (-1 + sizeof( int) * 8  ))}, sizeof( int), "%+i"  },
     { u_int_enum   ,  "unsigned int"      ,  {.u_int_union=-1}, sizeof( unsigned int ), "%+u"  },
     { l_int_enum   ,  "long"              ,  {.l_int_union=((long)1<< (-1 + sizeof( long) * 8  ))}, sizeof( long ), "%+li" },
     { ul_int_enum  ,  "unsigned long"     ,  {.ul_int_union=(long)-1}, sizeof( unsigned long ), "%+lu" },
     { ll_int_enum  ,  "long long"         ,  {.ll_int_union=(long long)-1 }, sizeof( long long ), "%+lli"},
     { ull_int_enum ,  "unsigned long long",  {.ull_int_union=((unsigned long long)1<< (-1 + sizeof( unsigned long long) * 8  ))}, sizeof( unsigned long long  ), "%+llu"},
     { float_enum   ,  "float"             ,  {.float_union=1e+37L}, sizeof( float ), "%+f"  },
     { double_enum  ,  "double"            ,  {.double_union=1e+37L}, sizeof( double ), "%+lf"  },
     { l_double_enum,  "long double"       ,  {.l_double_union=1e+37L}, sizeof( long double), "%+lle"}
 };


 /**
  * This is how your foo and bar functions should be organized this would 
  * allow handling for all your types. but the type is needed.
  */
 void sprintVal(struct s_type *typeVal, char*buf)
 {
     switch(typeVal->type)
     {
     case char_enum     :
         sprintf(buf, typeVal->stringFormat, typeVal->value.char_union);
         break;
     case u_char_enum   :
         sprintf(buf, typeVal->stringFormat, typeVal->value.u_char_union);
         break;
     case s_int_enum    :
         sprintf(buf, typeVal->stringFormat, typeVal->value.s_int_union);
         break;
     case us_int_enum   :
         sprintf(buf, typeVal->stringFormat, typeVal->value.us_int_union);
         break;
     case int_enum      :
         sprintf(buf, typeVal->stringFormat, typeVal->value.int_union);
         break;
     case u_int_enum    :
         sprintf(buf, typeVal->stringFormat, typeVal->value.u_int_union);
         break;
     case l_int_enum    :
         sprintf(buf, typeVal->stringFormat, typeVal->value.l_int_union);
         break;
     case ul_int_enum   :
         sprintf(buf, typeVal->stringFormat, typeVal->value.ul_int_union);
         break;
     case ll_int_enum   :
         sprintf(buf, typeVal->stringFormat, typeVal->value.ll_int_union);
         break;
     case ull_int_enum  :
         sprintf(buf, typeVal->stringFormat, typeVal->value.ull_int_union);
         break;
     case float_enum    :
         sprintf(buf, typeVal->stringFormat, typeVal->value.float_union);
         break;
     case double_enum   :
         sprintf(buf, typeVal->stringFormat, typeVal->value.double_union);
         break;
     case l_double_enum :
         sprintf(buf, typeVal->stringFormat, typeVal->value.l_double_union);
         break;
     }
 }


 void print_types()
 {
     int i=0;
     char buf[100];

     while(i < last_type )
     {
         sprintVal( &as_typeList[i], buf);
         printf( "Type: %-18s value=%-30s size=  %-dBytes \n", as_typeList[i].name, buf, as_typeList[i].size );
         i++;
     };
 }

 int main(int argc, char** argv)
 {

     print_types();
     return(0);
 }

あなたの問題の最大の問題は、erlang と c の間のメッセージです。現在の形式は /CMD/VALUE/ です。これは最低限 /CMD/TYPE/VALUE/ である必要がありますが、メッセージ ヘッダーの長さとチェックサム フッターは一般的です。より大きな結果値を返すことができるように、型をプロモートする必要があります。そのため、何を渡しているかを知る必要があります。

ああ、データを文字列として渡す場合、型のサイズをある程度適切に設定できます。また、パイプの両端に違いがある場合のエンディアンの問題も防ぎます。

于 2012-05-14T18:38:39.177 に答える