Как импортировать C-функцию в Аду с аргументами передачи по ссылке?

У меня есть функция C int func( int a, int* b), которую мне нужно импортировать и использовать в Аде 95. Там, где функция C обычно вызывается в C как c = func(a, &b);

Я постоянно импортирую функции C в Ada, но всегда избегал использования функций с передачей аргументов по ссылке, но, наконец, пришло время учиться.

Я хотел бы знать, как объявить эту функцию в Аде, а также хотел бы получить быстрый пример ее использования с показанными объявленными переменными (потому что я все еще немного не понимаю типы доступа).

Спасибо всем!


ada
person gjcamann    schedule 25.06.2012    source источник
comment
C не имеет ссылок. Вот почему вы должны использовать указатели. Они передаются по значению.   -  person Rommudoh    schedule 25.06.2012


Ответы (2)


В C int* b может означать многое. Возможно, это просто указатель на одну переменную, но это может быть и массив, длина которого равна int a. Этот код предполагает, что int* b на самом деле просто значение, которое передается по ссылке:

with Interfaces.C;

-- ...
package C renames Interfaces.C;

function Func (A : C.int; B : access C.int) return C.int;
pragma Import (Convention => C, Entity => Func,
               External_Name => "func");

-- ...

declare
   A : C.int := 42;
   B : aliased C.int := 23;
   C : C.int;
begin
   C := Func (A, B'Access);
   -- ...
end;

Вы можете использовать 'Access для aliased переменных. Это безопасно, если вы уверены, что указатель не будет храниться на стороне C и к нему не будут обращаться после окончания жизни переменной B. (Если в объявлении C используется ключевое слово const, вы можете использовать access constant на стороне Ады, но это только для Ады 2005.)

Вы также можете использовать именованный тип:

-- ...
type Int_Access is access C.int;
function Func (A : C.int; B : Int_Access) return C.int;
-- ...
C := Func (A, B'Unchecked_Access);
-- ...

Теперь нам нужно использовать 'Unchecked_Access, потому что Ада обычно не позволяет нелокальному типу доступа (как Int_Access) ссылаться на локальную переменную. Если вы знаете, что код C будет делать с указателем (а вы должны), вы можете использовать именованные типы, чтобы указать, что ссылки на локальные переменные не должны передаваться.

Примечание 1: если у вас есть процедура (в C: функция, которая возвращает void), вы можете указать переменную, которая будет передаваться по ссылке, используя in out в объявлении процедуры Ada вместо access. Таким образом, вам вообще не нужно беспокоиться о типах доступа. Как и раньше, вы должны быть уверены, что указатель не хранится на стороне C.

Примечание 2. Типы записей и массивы в любом случае передаются по ссылке, если только вы не укажете pragma Convention (C_Pass_By_Copy, Your_Type);. Это обычная ошибка при переносе функций C в Ada.

person flyx    schedule 25.06.2012
comment
Спасибо за отличный ответ, это также проясняет некоторые другие вопросы, которые у меня были о том, как все работало между Ada и C. - person gjcamann; 25.06.2012
comment
Меня всегда раздражала передача записей по ссылке для функций C-интерфейса; Структуры C, как и все типы C, передаются копированием. Было бы разумнее передать их копированием по умолчанию и явно передать указатель, если функция C принимает указатель на структуру. - person Keith Thompson; 25.06.2012
comment
И я совершенно уверен, что C_Pass_By_Value специфичен для GNAT (хотя я не удивлюсь, если другие компиляторы Ады примут его). - person Keith Thompson; 25.06.2012
comment
Я ошибся в имени. Правильное имя прагмы — C_Pass_By_Copy, и это действительно прагма, специфичная для GNAT в Аде 95. Она была добавлена ​​в Ада 2005. - person flyx; 29.06.2012

В дополнение (и плагиат с flyx) предлагаю полное решение:

Файл c_thing.c

#include <stdio.h>

int foo (int a, int * b) {
  printf ("A:%d, B %d\n",a, *b);
}

Файл ada_main.adb

with Interfaces.C;

procedure Ada_Main is 

   package C renames Interfaces.C;

   function Func (A : C.int; B : access C.int) return C.int;
   pragma Import (Convention => C, Entity => Func,
                  External_Name => "foo");

   A : C.int := 42;
   B : aliased C.int := 23;
   R : C.int;

begin

   R := Func (A, B'Access);

end Ada_Main;

Для компиляции (линукс):

gcc -c c_thing.c
gnatmake -c ada_main.adb
gnatbind ada_main.ali
gnatlink ada_main.ali c_thing.o

Бежать

./ada_main 
A:42, B 23
person NWS    schedule 25.06.2012