调用c函数的Ada程序的interface.c.size_t

时间:2019-03-06 23:11:32

标签: c ada fftw

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

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

生成初步绑定(需要一些调整)。我能够获取我的代码和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的范围。但是,在这种情况下,size_t来自interfaces.c.size_t,它被定义为

type size_t is mod 2 ** System.Parameters.ptr_bits;
在i-c.ads中

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

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

第二,假设我的理解是正确的,是否有解决该问题的方法,还是我需要采取一种完全不同的方法?

5 个答案:

答案 0 :(得分:4)

谢谢大家。西蒙的回答确实有帮助。一个问题是fftw_version []是在fftw的C代码中设置的,而不是我自己设置的,我不能保证在fftw库中的某些东西需要它之前会先调用该过程(fftw3_h.ads在我的任何代码之前都已详细说明) 。我还从Stephen J. Sangwine于2004年开始在线查找旧的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代码就是这样做的)。这样可以编译并很好地工作。

答案 1 :(得分:3)

我认为您不了解您尝试创建的数组的大小

    function uploadAjaxImages() {
    $('#uploadFormAjax').off().submit(function(e) {

        e.preventDefault();

        // Get the selected files from the input.
        var fileSelect = document.getElementById('file-select');
        var files = fileSelect.files;

        console.log(files);...

此行不仅说明数组fftw_version由size_t类型索引,而且还指定数组索引的范围为0..2 ** 32,这显然对于您的硬件来说太大了。 您真正想要的阵列大小是多少?例如,如果您想要一个100个字符的数组,则应指定

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

答案 2 :(得分: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语言中的字符串-它的转换方法要通过搜索空终止符来获取字符串的长度。

答案 3 :(得分: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

答案 4 :(得分:1)

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

  

尽管本国际标准在其他地方说了什么,但对具有“真正导入”方面的声明的详细说明不会创建实体。除了允许定义名称表示外部实体之外,这种详细说明没有其他作用。

以下完整示例说明了与@C在functionhere所示的用法对应的方面。 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
相关问题