调用c函数的Ada程序存在接口问题
我一直在从事Ada项目,需要与C库(fftw3)接口。我使用了命令调用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
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源文件中应该没有分配的空间,就像在Cextern const char fftw_version[]
@SimonWright中一样。声明和错误消息似乎使发生的事情变得很明显,我没有正确地考虑它。即使导入了变量,编译器仍可能包含导致存储错误的检查?不,这是导入的;Ada源中没有相应的对象。它将被nul终止,因此通过例如到_Ada(fftw\u版本,Trim\u nul=>True)获取内容
(这是默认值)。我有origina