调用c函数的Ada程序在interfaces.c.size_t中存在问题

问题描述 投票:2回答:5

我一直在研究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");

我对此的理解是,Ada正在尝试同时为整个字符串分配空间,并将存储空间基于size_t的范围。但是,在这种情况下,size_t来自interfaces.c.size_t,定义为

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

在i-c.ads中,对于C,size_t在stddef.h中定义为unsigned long。我不确定2 ** ptr_bits有多大,但我没有看到任何理由为什么i-c.ads中size_t的定义应该被限制为C的无符号long的大小。如果它比C的unsigned long长,那么我怀疑代码正在尝试创建一个使用比我更多内存的数组。我试图使用interfaces.c.unsigned_long而不是size_t,但Ada不喜欢类型不匹配(我应该知道)。

在这一点上,我有两个问题。首先,我对问题的理解很接近(这是我第一次与Ada和C之间的接口经验)。

其次,假设我的理解是正确的,有没有解决问题的方法,还是我需要采取完全不同的方法?

c ada fftw
5个回答
4
投票

谢谢大家。西蒙的回答确实有帮助。一个问题是fftw_version []在fftw的C代码中设置,而不是由我设置,我不能保证在fftw库中的某些东西需要它之前调用该过程(在我的任何代码之前详细说明了fftw3_h.ads) 。我还在2004年由Stephen J. Sangwine在线发现了旧的FFTW_Ada绑定。虽然我无法让他的代码工作,但我结合了他如何使用Simon的建议处理版本字符串,并创建了一个函数,在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
投票

我不认为你理解你想要创建的数组的大小

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

3
投票

引用the LRM

int,short,long,unsigned,ptrdiff_t,size_t,double,char,wchar_t,char16_t和char32_t类型分别对应于具有相同名称的C类型。

无论实际定义如何,您都可以信任编译器符合LRM,因此Ada的size_t实际上与C的大小完全相同。

话虽这么说,你的错误源于你在为数组分配size_t'Last字节时得到堆栈溢出的事实。我在fftw3.h标题中搜索了你想要包装的fftw_version的定义,但我找不到任何内容,所以对于更详细的答案,你需要显示你正在包装的代码。很可能你想使用chars_ptr而不是Interfaces.C.Strings来包装C中的字符串 - 它的转换方法通过搜索null终止符来处理字符串长度。


3
投票

在我看来,这可能就是你正在做的事情。这不是Minimal, Complete and Verifiable Example,因为它对我有用:-)

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文件,使用构建Ada

$ gnatmake bartels.adb -largs bartels_c.o

并运行

$ ./bartels 
the version

1
投票

总结了几个有用的答案,@ Jim Rogers指出heresize_t对象的详细描述可能会超过可用内存。 @Simon Wright观察here并说明here不需要为导入的对象分配空间。实际上,相应的Import暗示的pragma方面指定了以下动态语义:

尽管本国际标准在其他地方所说的是,具有真正导入方面的声明的详细说明并不能创建该实体。除了允许定义名称表示外部实体之外,这样的详细说明没有其他效果。

下面的完整示例说明了与@C在here中显示的function的编译指示相对应的方面。巴特尔:

安慰:

$ 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
© www.soinside.com 2019 - 2024. All rights reserved.