Ада-программа, вызывающая функции c, имеет проблемы с interfaces.c.size_t - PullRequest
2 голосов
/ 07 марта 2019

Я работал над проектом Ada и мне нужно взаимодействовать с библиотекой C (fftw3). Я использовал команду

gcc -c -fdump-ada-spec -C /usr/local/include/fftw3.h

для создания предварительной привязки (требуется некоторая настройка). Я смог получить свой код и файл fftw3_h.ads для компиляции в gnat. Однако программа вылетает с

raised STORAGE_ERROR : fftw3_h.ads:733 object too large

Когда я запускаю его через GDB, код падает на строке, которая определяет строку версии,

fftw_version : aliased char_array (size_t);  -- /usr/local/include/fftw3.h:457
pragma Import (C, fftw_version, "fftw_version");

Мое понимание этого состоит в том, что Ада пытается выделить место для всей строки сразу и основывает пространство хранения на диапазоне size_t. Тем не менее, size_t в этом случае происходит от interfaces.c.size_t, который определяется как

type size_t is mod 2 ** System.Parameters.ptr_bits;

в i-c.ads, а для C size_t определяется как unsigned long в stddef.h. Я не уверен, насколько велики 2 ** ptr_bits, но я не вижу никаких причин, почему определение size_t в i-c.ads должно быть ограничено размером беззнакового long в Си. Если он длиннее, чем unsigned long, то я подозреваю, что код пытается создать массив, который использует больше памяти, чем у меня. Я пытался просто использовать interfaces.c.unsigned_long вместо size_t, но Ada не нравится несоответствие типов (что я должен был знать).

На данный момент у меня есть два вопроса. Во-первых, мое понимание проблемы близко (это мой первый опыт взаимодействия между Ада и Си).

Во-вторых, при условии, что мое понимание правильное, есть ли способ обойти проблему или мне нужен совершенно другой подход?

Ответы [ 5 ]

4 голосов
/ 07 марта 2019

Спасибо всем. Ответ Саймона действительно помог. Одна проблема заключается в том, что fftw_version [] установлен в коде C для fftw, а не мной, и я не могу гарантировать, что процедура будет вызвана до того, как что-то в библиотеке fftw понадобится (fftw3_h.ads разрабатывается до того, как какой-либо мой код) , Я также нашел старую привязку FFTW_Ada онлайн от 2004 года Стивена Дж. Сангвина. Хотя я не мог заставить его код работать, я соединил то, как он справлялся со строкой версии, с предложением Саймона и создал функцию, которая возвращает строку, когда это необходимо, чем-то другим в библиотеке fftw.

function FFTW_Version return String is
    tmp_version : aliased char_array(size_t) ;
    pragma Import(C, tmp_version, "fftw_version");
 begin
    return To_Ada(tmp_version, Trim_Nul => True) ;
 end FFTW_Version;

Это дает мне что-то, что создает строку при необходимости, но не делает предположения о размере строки (что сделал код FFTW_Ada). Это компилируется и работает хорошо.

3 голосов
/ 07 марта 2019

Цитировать LRM :

Типы int, short, long, unsigned, ptrdiff_t, size_t, double, char, wchar_t, char16_t и char32_t соответствуют соответственно типам C с одинаковыми именами.

Независимо от того, как выглядит фактическое определение, вы можете доверять компилятору в соответствии с LRM, поэтому size_t в Ada будет фактически иметь тот же размер, что и C.

При этом ваша ошибка связана с тем, что вы получаете переполнение стека при выделении size_t'Last байтов для массива. Я искал в заголовке fftw3.h определение fftw_version, которое вы пытаетесь обернуть, но я не смог его найти, поэтому для более подробного ответа вам нужно показать код, который вы упаковываете. Скорее всего, вы хотите использовать chars_ptr вместо Interfaces.C.Strings для переноса строк из C - его методы преобразования заботятся о получении длины строки путем поиска нулевого терминатора.

3 голосов
/ 07 марта 2019

Мне кажется, это будет то, что вы делаете с этим.Это не минимальный, полный и проверяемый пример , потому что он работает для меня: -)

bartels.adb:

with Ada.Text_IO; use Ada.Text_IO;
with Interfaces.C; use Interfaces.C;
procedure Bartels is
   fftw_version : aliased char_array (size_t);  -- /usr/local/include/fftw3.h:457
   pragma Import (C, fftw_version, "fftw_version");
   V : constant String := To_Ada (fftw_version, Trim_Nul => True);
begin
   Put_Line (V);
end Bartels;

bartels_c.c:

const char fftw_version[] = "the version";

Скомпилируйте файл C, постройте Аду с помощью

$ gnatmake bartels.adb -largs bartels_c.o

и запустите

$ ./bartels 
the version
3 голосов
/ 07 марта 2019

Я не думаю, что вы понимаете размер массива, который вы пытаетесь создать с помощью

fftw_version : aliased char_array (size_t);  -- /usr/local/include/fftw3.h:457

Эта строка не просто указывает, что массив fftw_version индексируется типом size_t, она также указывает, что индексы массива охватывают диапазон 0..2 ** 32, что, очевидно, слишком велико для вашего оборудования. Какой размер массива вы действительно хотите здесь? Например, если вы хотите массив из 100 символов, вы должны указать

fftw_version : aliased char_array(size_t range 0..99);
1 голос
/ 12 марта 2019

Обобщая несколько полезных ответов, @Jim Rogers указывает здесь , что разработка объекта size_t может превысить доступную память.@Simon Wright отмечает здесь и иллюстрирует здесь , что для импортированного объекта не нужно выделять место.Действительно, аспект Import, подразумеваемый соответствующей прагмой , определяет следующую динамическую семантику:

Несмотря на то, что этоВ международном стандарте говорится, что разработка декларации с аспектом истинного импорта не создает сущность.Такая разработка не имеет никакого другого эффекта, кроме как позволить определяющему имени обозначать внешнюю сущность.

Следующий полный пример иллюстрирует аспект, соответствующий прагме, показанной здесь в function при @C.Bartels:

консоль:

$ gprclean -q ; gprbuild -q && ./bartels 
Versioni 1.2.3

bartels.gpr:

project Bartels is

   for Languages use ("Ada", "C");
   for Main use ("bartels.adb");

end Bartels;

bartels.adb:

with Ada.Text_IO;
with Interfaces.C;

procedure Bartels is

   function Get_Version return String is
      version : Interfaces.C.char_array(Interfaces.C.size_t)
         with Import => True, Convention => C;
   begin
      return Interfaces.C.To_Ada(version, Trim_Nul => True) ;
   end Get_Version;

 begin
   Ada.Text_IO.Put_Line (Get_Version);
end Bartels;

version.c:

const char version[] = "Versioni 1.2.3";

Кроме того, может возникнуть некоторая путаница, поскольку этот пример работает с GNAT Community 2018, но не работает с GNAT GPL 2017:

$ gprclean -q ; gprbuild -q && ./bartels 
raised STORAGE_ERROR : bartels.adb:7 object too large
...