1

编辑:我简化了代码以更好地显示情况。

任务:我有一个用 C 编写的工作套接字服务器/客户端程序。我想通过使用 Ada-C 接口来改进它。

当用户输入数学运算为 2+5、5*9、10/2 或 10-9 时获取用户输入的 C 函数

int read_message(void *buffer, int size, int timeout)
{
    //Recevie
    int n = recv(newfd, buffer, size, 0);

    if(n == -1)
    {
            perror("Can not read message");
            return -1;
    }

    return 1;
}

客户端向服务器发送一个结构体,如下所述:

typedef struct
{
     int number1;
     int number2;
     char operator;
}Operation;

ada主要程序:

with Ada.Text_IO, communication_pkg, Ada.Exceptions, Interfaces.C;
use Ada.Text_IO, communication_pkg;


procedure Main is
package C_pkg renames communication_pkg;
begin

Put_Line(Integer'Image(C_pkg.open_communication));
Put_Line("Server is Open");

C_pkg.read_message;



exception

when Event: Open_Error =>
    Put_Line("Can not open connection");
    New_Line;
    Put_Line(Ada.Exceptions.Exception_Name(Event));
    New_Line;
    Put_Line(Ada.Exceptions.Exception_Message(Event));
    New_Line;
    Put_Line(Ada.Exceptions.Exception_Information(Event));

when Event: Close_Error =>
    Put_Line("Can not close connection");
    New_Line;
    Put_Line(Ada.Exceptions.Exception_Name(Event));
    New_Line;
    Put_Line(Ada.Exceptions.Exception_Message(Event));
    New_Line;
    Put_Line(Ada.Exceptions.Exception_Information(Event));

when Event: Can_not_read_error =>
    Put_Line("Can not read");
    New_Line;
    Put_Line(Ada.Exceptions.Exception_Name(Event));
    New_Line;
    Put_Line(Ada.Exceptions.Exception_Message(Event));
    New_Line;
    Put_Line(Ada.Exceptions.Exception_Information(Event));      

when Event: Read_timeout_error =>
    Put_Line("Read timeout");
    New_Line;
    Put_Line(Ada.Exceptions.Exception_Name(Event));
    New_Line;
    Put_Line(Ada.Exceptions.Exception_Message(Event));
    New_Line;
    Put_Line(Ada.Exceptions.Exception_Information(Event));  

when Event: others =>
    Put_Line("Something else went wrong");
    New_Line;
    Put_Line(Ada.Exceptions.Exception_Name(Event));
    New_Line;
    Put_Line(Ada.Exceptions.Exception_Message(Event));
    New_Line;
    Put_Line(Ada.Exceptions.Exception_Information(Event));
 end Main;

名为“communication_pkg”的包具有打开、关闭和读取客户端消息的功能和过程:

with Interfaces.C, Ada.Unchecked_COnversion, Ada.Text_IO;
use Interfaces.C, Ada.Text_IO;
with System;

package body Communication_pkg is

    package C renames Interfaces.C;

    function open return Integer;
    pragma Interface(C, open);
    pragma Interface_Name(open, "open");

    function close return Integer;
    pragma Interface(C, close);
    pragma Interface_Name(close, "close_connection");

    function read(buffer: in System.Address; size : in Integer; timeout: in Integer) return Integer;
    pragma Interface(C,read);
    pragma Interface_Name(read, "read_message");



    function open_communication return Integer is
    connection_status : Integer;
    begin
            connection_status := open;

            if (connection_status = -1) then
                    raise Open_Error;
            end if;

            return connection_status;

    end open_communication;


    function close_communication return Integer is
    connection_status : Integer;
    begin
            connection_status := close;

            if(connection_status = -1) then
                    raise Close_Error;
            end if;

    return connection_status;

    end close_communication;


    procedure read_message is
    size : Integer:=9;  
    timeout : Integer:=1;
    read_message_status : Integer;
    type byte is range 0..255;

    type byte_array is array (Integer range 0..15) of byte;

    --buffer : System.Address
    buffer : byte_array;    
begin 

    Put_Line("read message in");

            read_message_status:=read(buffer'Address, size, timeout);

    Put_Line(Integer'Image(read_message_status));

            if(read_message_status = -1) then
                    raise Can_not_read_error;
            elsif(read_message_status = -2) then
                    raise Read_timeout_error;
            end if;

    Put_Line("read message out");   

    for i in 0..15 loop
        Put_Line(byte'Image(buffer(i)));
    end loop;

end;

end Communication_pkg;

主程序首先打开连接并等待接收来自客户端的消息。当客户端发送一个 Operation 类型的结构时,我在缓冲区中得到的内容,当客户端类型为 2+5 时,如下所述:

2 0 5 0 12331 11444 32688 0 2848 2737 32688 0 8864 64399 32767 0

缓冲区(byte_array 类型)中的第一个字节和第三个字节始终显示客户端输入的第一个和第二个整数。但是,缓冲区没有操作符(char 类型)。

如何完全获取 Operator 结构?

4

5 回答 5

2

你定义

type byte is range 0..255;

Ada 要求有符号整数类型(用“范围”定义)的基类型是有符号整数类型,大致围绕零对称,范围大到足以容纳类型声明中的范围。这意味着 Byte 的基本类型必须足够大以容纳 -255 .. 255。因此 Byte'Base 可能是一个 16 位有符号二进制补码整数。而且,确实,您表明您的结果包含诸如 12331 之类的值,这些值太大而无法容纳在 0 .. 255 中。

您系统上的 C 的 int 似乎是 32 位,小端。这就是为什么每个 int 都有 2 个值,第一个字中的操作数和第二个字中的零。这意味着运算符将位于第 5 个单词的 LSB 中。

实际上,12331 rem 256 = 43,即“+”。

要正确执行此操作,请声明

type Byte is mod 256;
pragma Convention (C, Byte);

type Byte_List is array (Interfaces.C.int range 0 .. 15) of Byte;
pragma Convention (C, Byte_List);

function Read (List : Byte_List; Size : Interfaces.C.Int; Timeout : Interfaces.C.Int)
return Interfaces.C.Int;
pragma Import (C, Read, "read_message");

然后你可以用

Status : Interfaces.C.Int;
List   : Byte_List;
...
Status := Read (List, 9, 1);

你应该会发现 List 包含

2 0 0 0 5 0 0 0 43

重要规则:

您传递给 C 函数的所有内容都应该是约定 C。(是的,有时非约定 C 类型会起作用,但有时它们不会,而约定 C 类型将始终有效。)

永远不要将 System.Address 传递给 C。要传递数组,只需传递数组;Ada 将指向数组第一个元素的约定 C 指针传递给 C 函数,为您提供所需的内容。在需要指针的其他情况下,您应该传递约定 C 访问类型。(System.Address 可以与某些编译器一起使用,但并非所有编译器都可以使用。仅仅因为它可以与您当前的编译器一起使用并不意味着它可以与其他编译器一起使用,包括您当前编译器的不同版本。)

于 2013-11-01T07:05:03.117 回答
1

如果您希望编译器适合类型或变量,您应该明确告诉它。

type Byte is range 0 .. 255;

仅使用上面的声明,可以为 Byte 类型分配 8 位及以上的任何内容。您可以添加尺寸声明,以指定所需的尺寸:

type Byte is range 0 .. 255;
for Byte'Size use 8;

现在编译器必须将 Byte 类型的变量适应 8 位(或无法编译源文本)。

对于不受约束的数组,执行此操作的方式略有不同:

type Bytes is array (Integer range <>) of Byte;
pragma Pack (Bytes);
于 2013-11-01T08:11:13.370 回答
1

嗯......看起来你可以在这里使用包(和泛型!)。

-- We need to define the base-type for the buffer.
Subtype Byte_Array is Interfaces.C.char_array;


-- Taking an address for the size (size_t type), and an address for the
-- location of the buffer we create one with the package's instantiation.

Generic
    Buffer_Location,
    Size_Location : in System.Address;
Package Buffer_Package is
    Length   : constant Interfaces.C.size_t
      with Address => Size_Location, Import, Convention => C;

    Subtype Buffer_Type is Byte_Array(1..Length);

    Buffer : Buffer_Type
      with Address => Buffer_Location, Import, Convention => C;

    Pragma Assert( Length >= 0 );
End Buffer_Package;


Generic
    with package Buf_Pkg is new Buffer_Package( others => <> );
Package Parse is

    Type Operators is ( Identity, '*', '/', '+', '-' );

    -- There are three forms of Node:
    --   1) Unary operation: only the string-field "right" exists.
    --   2) Binary operation #1: in addition to "Right" is the string "Left"
    --   3) Binary operation #2: in addition to "Right" is a pointer to a node.
    Type Node( Op : Operators; Len_1, Len_2 : Natural ) is record
        Right : String(1..Len_1);
        case Op is
        when Identity => null;
        when others   => 
            case Len_2 is
            when Positive  => Left : String(1..Len_2);
            when 0         => Ptr  : not null access Node;
            end case;
        end case;
    end record;

    Function Exec( Input : Node ) return Float;

    -- YOUR READ FUNCTION HERE! (Use Buf_Pkg.Buffer.)

private
    Function Value( Input : Node ) return Float;
end Parse;

Package body Parse is
    Function Exec( Input : Node ) return Float renames Value;

    Function Value( Input : Node ) return Float is
        Subtype Unary is Operators range Identity..Identity;
    begin
        declare
            Right : constant Float:= Float'value(Input.Right);
            Left  : constant Float:= (if Input.Op in Unary then 0.0 else
                          (if Input.Len_2 in Positive then Float'value(Input.Left)
                           else Value(Input.Ptr.all)
                          ));
        begin
            case Input.Op is
            when Identity => return Right;
            when '*' => return Left * Right;
            when '/' => return Left / Right;
            when '+' => return Left + Right;
            when '-' => return Left - Right;
            end case;
        end;
    end Value;
end Parse;

从缓冲区读取到Node-tree 留给 OP 作为练习。

于 2013-10-31T04:04:32.700 回答
1

每当您与 C 交互时,您应该保留参考手册中第B.3(63-75)段中的映射列表。它可以帮助您跟踪调整 Ada 端以匹配 C 端的程度。

于 2013-11-01T08:24:44.577 回答
0

如果你有一个System.Address值并且你想获取地址指向的对象,你不能Ada.Unchecked_ConversionSystem.Address. 这将转换地址本身中的字节,而不是地址指向的字节。你想要的是像

The_Buffer : Byte_Array;
for The_Buffer'Address use Buffer;

或者,在 Ada 2012 中(我认为):

The_Buffer : Byte_Array with Address => Buffer;

这告诉编译器这The_Buffer是一个对象,其地址是 的值Buffer。然后你可以说

myArray := The_Buffer;

将其复制到局部变量中。

这不会解决您的代码的所有问题。我强烈怀疑这Buffer不是一个有效的System.Address,因为它似乎有来自输入的字节,这对于地址来说是错误的。如果没有看到更多代码(尤其是调用 的代码process_byte_array),很难说出需要做什么。

于 2013-10-30T23:51:39.917 回答