Невозможно преобразовать void * в массив байтов

РЕДАКТИРОВАТЬ: я упростил свои коды, чтобы лучше показать ситуацию.

ЗАДАЧА: У меня есть работающая программа сокет-сервер/клиент, написанная на C. Я хочу улучшить ее, используя интерфейс Ada-C.

Функция C, которая получает пользовательский ввод, когда пользователь вводит математическую операцию как 2 + 5, 5 * 9, 10/2 или 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;
}

Клиент отправляет структуру на сервер, как указано ниже:

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

Основная процедура Ады:

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;

Пакет под названием «связь_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).

Как я могу полностью получить структуру оператора?


person Encinaar    schedule 30.10.2013    source источник
comment
Мне непонятно, о чем вы спрашиваете - возможно, вы можете предоставить больше контекста или пример, показывающий, что вы ищете? Не забудьте также проверить контрольный список вопросов. Спасибо!   -  person Christian Ternus    schedule 31.10.2013
comment
(1) Unchecked_Conversion от System.Address до Byte_Array почти наверняка не то, что вам нужно. Это берет адрес (который в этой системе равен 4 или 8 байтам) и переинтерпретирует биты адреса как Byte_Array, что составляет 256 байтов. (2) Мы не знаем, что такое Operation. (3) for i in 0..8 выполняет цикл девять раз. i будет принимать каждое значение 0, 1, ..., 7, 8. Это то, что вы хотите? (4) Возможно, нам потребуется посмотреть, как программа на языке Ada вызывает функцию C, включая любые Import/Export аспекты/прагмы, которые вы используете.   -  person ajb    schedule 31.10.2013
comment
Только из соображений стиля, я думаю, вам следует изменить имя communication_pkg на просто Communications. Мы все видим, что это пакет!   -  person Simon Wright    schedule 02.11.2013


Ответы (5)


Вы определяете

type byte is range 0..255;

Ада требует, чтобы базовый тип целочисленного типа со знаком (определяемый с помощью «диапазона») был целочисленным типом со знаком, примерно симметричным относительно нуля, с диапазоном, достаточно большим, чтобы удерживать диапазон в объявлении типа. Это означает, что базовый тип Byte должен быть достаточно большим, чтобы вмещать -255 .. 255. Таким образом, Byte'Base, вероятно, является 16-разрядным целым числом с дополнением до двух со знаком. И действительно, вы показываете, что ваш результат содержит такие значения, как 12331, которые слишком велики, чтобы поместиться в 0..255.

C int в вашей системе выглядит 32-битным, с прямым порядком байтов. Вот почему вы получаете 2 значения для каждого int, с операндом в 1-м слове и нулем во 2-м. Это означает, что оператор будет находиться в младшем разряде 5-го слова.

В самом деле, 12331 бэр 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. Чтобы передать массив, просто передайте массив; Ада передает указатель C-конвенции на 1-й элемент массива функции C, что дает вам то, что вы хотите. В других случаях, когда требуется указатель, вы должны передать тип доступа Convention-C. (System.Address будет работать с некоторыми компиляторами, но не со всеми. Тот факт, что он работает с вашим текущим компилятором, не означает, что он будет работать с другим, в том числе с другой версией вашего текущего компилятора.)

person Jeff Carter    schedule 01.11.2013

Хммм... похоже, здесь можно использовать пакеты (и дженерики!).

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

person Shark8    schedule 31.10.2013

Если вы хотите, чтобы компилятор соответствовал типу или переменной, вы должны указать это явно.

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);
person Jacob Sparre Andersen    schedule 01.11.2013

Всякий раз, когда вы взаимодействуете с C, вы должны хранить список сопоставлений в абзацах B.3(63-75) в справочном руководстве. Это поможет вам отслеживать, насколько сильно вы должны настроить сторону Ada, чтобы она соответствовала стороне C.

person Jacob Sparre Andersen    schedule 01.11.2013

Если у вас есть значение System.Address и вы хотите получить объект, на который указывает адрес, вы не можете использовать Ada.Unchecked_Conversion для System.Address. Это преобразует байты в самом адресе, а не байты, на которые указывает адрес. То, что вы хотите, это что-то вроде

The_Buffer : Byte_Array;
for The_Buffer'Address use Buffer;

или, в Аде 2012 (я думаю):

The_Buffer : Byte_Array with Address => Buffer;

Это сообщает компилятору, что The_Buffer — это объект, адрес которого является значением Buffer. Тогда вы можете сказать

myArray := The_Buffer;

чтобы скопировать его в локальную переменную.

Это не решит всех проблем с вашим кодом. Я сильно подозреваю, что Buffer на самом деле не является действительным System.Address, поскольку в нем, похоже, есть байты из ввода, что неверно для адреса. Не видя больше кода (особенно кода, который вызывает process_byte_array), трудно сказать, что именно нужно сделать.

person ajb    schedule 30.10.2013