编辑:我简化了我的代码以更好地展示情况。
任务:我有一个用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 五 0 12331 11444 32688 0 2848 2737 32688 0 8864 64399 32767 0
缓冲区中的第一个字节和第三个字节(类型为byte_array)始终显示客户端输入的第一个和第二个整数。但是,缓冲区没有运算符(char类型)。
如何完全获得Operator结构?
答案 0 :(得分:2)
您定义
type byte is range 0..255;
Ada要求有符号整数类型的基类型(用“range”定义)是有符号整数类型,大致对称于零,其范围足以容纳类型声明中的范围。这意味着Byte的基本类型必须足够大才能保持-255 .. 255.所以Byte'Base可能是一个16位有符号的二进制补码整数。而且,实际上,您显示您的结果包含12331之类的值,这些值太大而不适合0 .. 255。
系统上的C的int似乎是32位,小端。这就是为什么每个int得到2个值,第一个字中的操作数和第二个字中的0。这意味着运算符将位于第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将与某些编译器一起使用,但不适用于所有编译器。仅仅因为它适用于您当前的编译器并不意味着它可以与其他编译器一起使用,包括使用当前编译器的不同版本。)
答案 1 :(得分: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
- 树留作OP的练习。
答案 2 :(得分:1)
如果您希望编译器适合类型或变量,您应该明确告诉它。
type Byte is range 0 .. 255;
只有上面的声明,类型字节可以分配8位及以上的任何内容。您可以添加大小声明,以指定所需的大小:
type Byte is range 0 .. 255;
for Byte'Size use 8;
现在编译器必须在8位中拟合Byte类型的变量(或者编译源文本失败)。
对于不受约束的数组,实现方式略有不同:
type Bytes is array (Integer range <>) of Byte;
pragma Pack (Bytes);
答案 3 :(得分:1)
每当与C接口时,都应该在参考手册的第B.3(63-75)段中保留映射列表。它可以帮助您跟踪调整Ada侧与C侧匹配的距离。
答案 4 :(得分:0)
如果您有System.Address
值,并且想要获取地址指向的对象,则无法在Ada.Unchecked_Conversion
上使用System.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
的代码),很难说明需要做什么。