Как я могу вернуть строку, выделенную в C для Ады и освободить ее в Аде? - PullRequest
5 голосов
/ 28 января 2020

Это обратный этот вопрос .

У меня есть строка, выделенная в функции C (в частности, библиотекой cJSON), которую я хочу верните вызывающему абоненту Ada в качестве параметра out и освободите строку из Ada.

Интерфейс C выглядит следующим образом:

typedef struct
{
    int f1;
    int f2;
    // ...
} A;

int Dump_Record(const A& rec, char** outstr);

Функция Dump_Record устанавливает значение аутс значение, возвращаемое cJSON_Print, которое выделяет строку через malloc.

Как указать привязку Ada и как я могу получить выходную строку и правильно освободить ее в Ada?

1 Ответ

7 голосов
/ 28 января 2020

Ниже показан файл Ada, C и GPR для возврата char ** в качестве типа возврата и в качестве выходного параметра Ada.

with Ada.Text_IO; use Ada.Text_IO;
with Interfaces.C.Strings; use Interfaces.C.Strings;

procedure Str is
   function Get_Str return chars_ptr with
     Import => True,
     Convention => C,
     External_Name => "get_str";

   procedure Get_Str (Str : in out chars_ptr) with
     Import => True,
     Convention => C,
     External_Name => "get_str2";

   Str  : chars_ptr := Get_Str;
   Str2 : chars_ptr := Null_Ptr;
begin
   Get_Str (Str2);

   Put ("==> " & Value (Str));
   Put ("==> " & Value (Str2));

   Free (Str);
   Free (Str2);
end Str;
#include <malloc.h>
#include <string.h>

const char *str  = "Hello, Ada\n";
const char *str2 = "This is another C string!\n";

// As a return type.
const char *get_str (void) {
    char *ptr = malloc (strlen (str) + 1);

    strcpy (ptr, str);

    return ptr;
}

// As a return / out parameter.
void get_str2 (char **ptr) {
    *ptr = malloc (strlen (str2) + 1);

    strcpy (*ptr, str2);
}
project Str is
   for Languages use ("C", "Ada");
   for Source_Dirs use (".");
   for Exec_Dir use ".";
   for Main use ("str.adb");
end Str;

Скомпилировать с помощью gprbuild -P str.

Итак, чтобы реализовать вашу функцию в Ada:

package C renames Interfaces.C;

type A is
   record
      F1, F2 : C.int;
   end record with
     Convention => C;

   function Dump_Record (Rec : aliased A; Out_Str : out chars_ptr) return C.int with
     Import => True,
     Convention => C,
     External_Name => "Dump_Record";

По-видимому, параметр Re c должен иметь псевдоним, чтобы компилятор знал, что это ссылка в C и не перераспределяет его где-то еще, то есть вторичный стек.

...