调用c函数的Ada程序存在接口问题

调用c函数的Ada程序存在接口问题,c,ada,fftw,C,Ada,Fftw,我一直在从事Ada项目,需要与C库(fftw3)接口。我使用了命令 gcc -c -fdump-ada-spec -C /usr/local/include/fftw3.h 生成初步绑定(需要进行一些调整)。我能够用gnat编译我的代码和fftw3_h.ads。然而,程序崩溃了 raised STORAGE_ERROR : fftw3_h.ads:733 object too large 当我通过gdb运行它时,代码在定义版本字符串的行上崩溃 fftw_version : aliased c

我一直在从事Ada项目,需要与C库(fftw3)接口。我使用了命令

gcc -c -fdump-ada-spec -C /usr/local/include/fftw3.h
生成初步绑定(需要进行一些调整)。我能够用gnat编译我的代码和fftw3_h.ads。然而,程序崩溃了

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来自interfaces.c.size_t,定义为

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

现在,我有两个问题。首先,我对这个问题的理解是否接近(这是我第一次在Ada和C之间进行接口)


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

我认为您不了解您试图创建的阵列的大小

fftw_version : aliased char_array (size_t);  -- /usr/local/include/fftw3.h:457
此行不仅说明数组fftw_版本是由类型size_t索引的,还指定数组索引的范围是0..2**32,这显然太大了,硬件无法处理。 这里您真正想要的阵列大小是多少?例如,如果需要100个字符的数组,则应指定

fftw_version : aliased char_array(size_t range 0..99);
引述:

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

不管实际定义看起来如何,您都可以相信编译器会遵守LRM,因此实际上,Ada的
size\u t
将与C的大小完全相同


也就是说,您的错误源于这样一个事实:在为数组分配
size\u t'Last
字节时,堆栈溢出。我在
fftw3.h
标题中搜索了您试图包装的
fftw\u版本
的定义,但找不到任何定义,因此要获得更详细的答案,您需要显示正在包装的代码。很可能您想使用
chars\u ptr
而不是from来包装C中的字符串–它的转换方法通过搜索空终止符来获取字符串长度。

在我看来,这很可能就是您使用它所做的。这不是一个,因为它适合我:-)

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;
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;
巴特尔斯大学c.c:

const char fftw_version[] = "the version";
编译C文件,用

$ gnatmake bartels.adb -largs bartels_c.o

$ ./bartels 
the version

谢谢大家。西蒙的回答真的很有帮助。一个问题是fftw_version[]是在fftw的C代码中设置的,不是由我设置的,我不能保证在fftw库中的某些东西需要它之前调用该过程(fftw3_h.ads是在我的任何代码之前详细阐述的)。我还发现了2004年斯蒂芬·J·桑格温(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代码就是这样做的)。这可以很好地编译和工作

Jim Rogers总结了几个有用的答案,指出对
size\t
对象的细化可能会超过可用内存@Simon Wright观察并说明了不需要为导入的对象分配空间。事实上,对应的方面隐含了以下动态语义:

尽管本国际标准在其他地方有规定,但具有真正进口方面的声明的详细阐述并不能创建实体。这种细化除了允许定义名称表示外部实体之外没有其他效果

下面的完整示例说明了@C.Bartels在
函数中显示的对应于pragma的方面:

控制台:

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

谢谢你的信息。我会按照你的建议去做,大约一天后再发回来。至于fftw_版本在fftw3.h中的位置,这是一个大型宏(fftw_DEFINE_API(X,R,C))扩展的结果,该宏生成整个API的三个版本,分别用于单精度、双精度和长双精度。因此,“FFTW_EXTERN const char X(version)[]”在C中被扩展为“EXTERN const char FFTW_version[]”,并按照我所描述的gcc-fdump ada spec命令进行转换。但是它是导入的!执行导入的Ada源文件中应该没有分配的空间,就像在C
extern const char fftw_version[]
@SimonWright中一样。声明和错误消息似乎使发生的事情变得很明显,我没有正确地考虑它。即使导入了变量,编译器仍可能包含导致存储错误的检查?不,这是导入的;Ada源中没有相应的对象。它将被nul终止,因此通过例如
到_Ada(fftw\u版本,Trim\u nul=>True)获取内容
(这是默认值)。我有origina