2012-05-13 16 views
3
で簡単なバイトプロトコルで大きな整数を書いたり、浮い

免責事項:質問の作者のErlangの平均知識を持っているとC.の基本的な(しかし増加)知識読む/ C

私が使用していますread()は、私のport.cプログラムがInteroperability Tutorial User Guideで提供されているErlangポートの例から取得したバイトを読み込む機能を持っています(「Erlang Programming」の第12章にも記載されています)。

しかし、C面からは間違った値(999の代わりに231など)が出てくるので、質問は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 int byte; 

typedef unsigned char byte; 

を変更するには、愚かな試みを行っているが、それはうまくいきませんでした。

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

  • 我々は255よりも大きいパラメータ、(アーランポートから例えばfoo(256))とポートを呼び出した場合、実行は(読み取り時に終了します)read_cmd内部が()をi = 0とする。
  • 255より小さいパラメータでポートを呼び出すが、関数の結果が255より大きい場合(たとえば、int foo(int x) { return x+1000; }とすると、プログラムは終了しないが、Erlangポートに予期しない値が得られる)

だから、質問は:?私は大きな数字とプロトコルを動作させるために、あるいはフロートで何をすべき

答えて

2

バイトあなたが遭遇する可能性が高いです任意のマシン上でのみから値を保持することができます-128〜127(符号付き)または0〜255(符号なし)です。他のものは両方の間でデータをマーシャリングする必要があります。http://www.erlang.org/documentation/doc-5.6/pdf/tutorial.pdfは、より大きな構築物にbyte Sをマーシャリングの例アーランと他の言語の間でデータをマーシャリングするための公式のアーランチュートリアルであり、そしてC.

+0

はい、私は符号なしバイトが0から255であることを知っています。あなたが提供したリンクは私の質問で言及したチュートリアルのPDF版のようです。 – skanatek

+0

それを読んで、バイトの範囲を理解しているなら、なぜバイトの範囲外の値が使えると期待していたのですか? – geekosaur

+0

これらの値が1バイトの範囲外で動作するとは思わなかった。私はこの例がうまくいくことを知っていますが、私はそれを微調整したいと思います。問題は「プロトコルをより大きい数で、あるいは浮動小数点でも動作させるためにはどうすればよいでしょうか」でした。申し訳ありませんが私は十分に明確にしていない場合。 – skanatek

2

の例を含んで既にread_cmdで存在します。 bufのうちの最初の二つbyte Sを取り、int(四byte構造)として扱い

... 
    int len; 

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

thisによれば、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; 
} 
1

アーランからの変換は、バイト配列ではなく文字配列にあると仮定すると。

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

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

はされている必要があります:あなたのコードはバイトのみとint型を処理する

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 /にする必要がありますが、メッセージヘッダーとチェックサムフッターの凡例は共通です。より大きな結果値が返されるように型を昇格させる必要があります。あなたが何を渡しているかを知ることが必要です。

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