[英]Read/write big integers or floats with a simple byte protocol in C
免責聲明:該問題的作者具有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端口:
/ * 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;
但它不起作用。
實際上,有兩個問題:
foo(256)
調用端口,則執行將在read_cmd()內的read()處終止,其中i = 0; int foo(int x) { return x+1000; }
,那么程序不會終止,但是我們得到了我們的Erlang端口有一些意想不到的價值; 所以,問題是:為了使協議能夠使用更大的數字甚至浮點數,我該怎么做?
您可能遇到的任何計算機上的字節只能保存-128到127(有符號)或0到255(無符號)的值; 其他任何事情都需要在兩者之間編組數據。 http://www.erlang.org/documentation/doc-5.6/pdf/tutorial.pdf是用於在Erlang和其他語言之間編組數據的官方Erlang教程,並包含C語言中的示例。
read_cmd
已經存在將byte
編組為較大構造的read_cmd
。
...
int len;
if (read_exact(buf, 2) != 2)
return(-1);
len = (buf[0] << 8) | buf[1];
...
從buf
取出前兩個byte
,並將它們視為int
(四byte
結構)。 根據這個 , double
是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;
}
假設從Erlang轉換為字節數組而不是字符數組。
代碼有點寬松,最簡單的答案......
while (read_cmd(buf) > 0) {
fn = buf[0];
arg = buf[1];
本來應該:
while (arg=read_cmd(buf) > 0) {
fn = buf[0];
您的代碼只處理字節和整數,它不能處理浮點數和雙精度數。
您的寫入cmds中還有其他錯誤。
------------編輯-----------------這是你想要在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 /,盡管消息頭和校驗和頁腳的常用內容很常見。 應該提升類型,以便可以返回更大的結果值。 所以需要知道你要傳遞的是什么。
哦,如果您將數據作為字符串傳遞,您可以在一定程度上適當地調整您的類型。 如果管道末端之間存在差異,它還可以防止端點問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.