1
votes

EDIT: I simplified my codes to better show the situation.

TASK: I have a working socket server/client program written in C. I want to improve it by using Ada-C interfacing.

C function that gets the user input when the user enters a mathematical operation as 2+5, 5*9, 10/2 or 10-9

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;
}

The client sends a struct to the server as stated below:

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

Ada main procedure:

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;

Package called "communication_pkg" that has functions and procedures which opens, closes and reads client message:

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;

The main procedure first opens the connection and waits for receiving a message from the client. When the client sends a struct of type Operation, what I get in the buffer, when 2+5 is type by the client, is something as stated below:

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

The first byte and the third byte in the buffer(of type byte_array) always show the first and second integer entered by the client. However, the buffer does not have the operator(of type char).

How can I get the Operator struct completely?

5
It's not clear to me what you're asking -- perhaps you can provide some more context, or an example showing what you're looking for? Be sure also to check the question checklist. Thanks! - Christian Ternus
(1) An Unchecked_Conversion from a System.Address to a Byte_Array is almost certainly not what you want. This takes an address (which is either 4 or 8 bytes on this system) and reinterprets the bits of the address as a Byte_Array, which is 256 bytes. (2) We don't know what an Operation is. (3) for i in 0..8 executes the loop nine times. i will take every value 0, 1, ..., 7, 8. Is that what you want? (4) We may need to see just how the Ada program is calling the C function, including any Import/Export aspects/pragmas you're using. - ajb
Only as a matter of style, I think you should change the name of communication_pkg to just Communications. We can all see that it's a package! - Simon Wright

5 Answers

2
votes

You define

type byte is range 0..255;

Ada requires that the base type of a signed integer type (defined with "range") be a signed integer type, roughly symmetric around zero, with a range large enough to hold the range in the type declaration. That means the base type of Byte must be large enough to hold -255 .. 255. So Byte'Base is probably a 16-bit, signed, two's complement integer. And, indeed, you show that your result contains values such as 12331, which are too large to fit in 0 .. 255.

C's int on your system appears to be 32 bits, little endian. That is why you get 2 values for each int, with the operand in the 1st word and zero in the 2nd. This implies that the operator will be in the LSB of the 5th word.

Indeed, 12331 rem 256 = 43, which is '+'.

To do this correctly, declare

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");

You can then call this with

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

and you should find that List contains

2 0 0 0 5 0 0 0 43

Important rules:

Everything you pass to a C function should be convention C. (Yes, sometimes non-convention-C types will work, but sometimes they won't, while convention-C types will always work.)

Never pass System.Address to C. To pass an array, just pass the array; Ada passes a convention-C pointer to the 1st element of the array to the C function, giving you what you want. In other cases where a pointer is needed you should pass a convention-C access type. (System.Address will work with some compilers, but not with all. Just because it works with your current compiler doesn't mean it will work with another, including with a different version of your current compiler.)

1
votes

Hmmm... looks like you could use packages (and generics!) here.

-- 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;

Reading from a buffer into a Node-tree is left as an exercise to the OP.

1
votes

If you want the compiler to fit a type or variable, you should tell it explicitly.

type Byte is range 0 .. 255;

With only the declaration above, type Byte may be allocated anything from 8 bits and up. You can add a size declaration, to specify the required size:

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

Now the compiler has to fit variables of type Byte in 8 bits (or fail compiling the source text).

With unconstrained arrays, the way to do it is slightly different:

type Bytes is array (Integer range <>) of Byte;
pragma Pack (Bytes);
1
votes

Whenever you interface with C, you should keep the list of mappings in paragraphs B.3(63-75) in the reference manual. It helps you keep track of how far you have to adjust the Ada side to match the C side.

0
votes

If you have a System.Address value and you want to get an object that the address points to, you can't use Ada.Unchecked_Conversion on the System.Address. That will convert the bytes in the address itself, not the bytes that the address points to. What you want is something like

The_Buffer : Byte_Array;
for The_Buffer'Address use Buffer;

or, in Ada 2012 (I think):

The_Buffer : Byte_Array with Address => Buffer;

That tells the compiler that The_Buffer is an object whose address is the value of Buffer. Then you can say

myArray := The_Buffer;

to copy it into a local variable.

That won't solve all the problems with your code. I strongly suspect that Buffer isn't really a valid System.Address, since it seems to have bytes from the input in it, which is wrong for an address. Without seeing more of the code (especially the code that calls process_byte_array), it's hard to tell just what needs to be done.