3

免责声明:该问题的作者对 Erlang 有一般的了解,并且对 C 有基本的(但不断增加的)知识。

我正在使用该read()函数来读取我的程序从互操作性教程用户指南port.c中提供的 Erlang 端口示例中获取的字节(它也在“Erlang 编程”一书的第 12 章中进行了描述)。

但我倾向于认为这个问题根本与 Erlang 无关,因为我得到的错误值(例如 231 而不是 999)来自 C 端。

问题是该协议不适用于超过 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 端口:

/* 端口.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;

但它没有用。

其实有两个问题:

  • 如果我们使用大于 255 的参数调用端口(例如foo(256)来自 Erlang 端口),则执行将在 read_cmd() 中的 read() 处终止,其中 i = 0;
  • 如果我们使用小于 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];
...

这将前两个bytes 取出buf并将它们视为int(一个四byte结构)。据此 adouble是 8 个字节,因此理论上,您应该能够应用相同的方法。你想做一些类似的事情(未经测试,几乎可以保证不起作用,仅用于说明目的等):

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(无符号)的值;其他任何事情都需要在两者之间编组数据。 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];

您的代码将只处理字节和整数,它不能处理浮点数和双精度数。

您的写入 cmd 中还有其他错误。

---------编辑----------------- 这是您尝试在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 回答