我一直在研究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之间的接口经验)。
其次,假设我的理解是正确的,有没有解决问题的方法,还是我需要采取完全不同的方法?
谢谢大家。西蒙的回答确实有帮助。一个问题是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代码所做的)。这编译并且工作得很好。
我不认为你理解你想要创建的数组的大小
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);
引用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终止符来处理字符串长度。
在我看来,这可能就是你正在做的事情。这不是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
总结了几个有用的答案,@ Jim Rogers指出here,size_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