将从C例程分配的数组传递给ADA

将结构/记录数组从ADA传递到C例程是一回事。 在这种情况下,内存管理在ADA中完成。 但是当与第三方库连接时,通常存在内存管理在C部分中完成的问题。

例如:对于C结构:

typedef struct _MYREC { int n; char *str; } MYREC; 

以下C-routine分配Memory并返回一个指向带有n个元素的MYREC数组的指针:

 MYREC * allocMyrec(int n); 

问题是返回的指针不包含ADA中内存安全计算所需的大小信息。

在ADA中我想对(MYREC *)指针使用相应的Array-Definition:

 type MYREC_Array is array (Int range ) of aliased MYREC; pragma Convention (C, MYREC_Array); 

相应的(大小很大的)ADA-Function allocMyrec会是什么样子或者什么是正确的策略?

对于一个元素,可以映射C指针以access MYREC 。 这就是Gnat-Binding生成器的function。 但这没有用。

提示高度赞赏。

我终于使用包Interface.C.Pointers ,这很容易,这里是代码:

 with Interfaces.C, Interfaces.C.Pointers ; use Interfaces.C ; with Ada.Text_IO ; -- To Check procedure MYRECTest is package IIO is new Ada.Text_IO.Integer_IO (Int) ; type PChar is access all Char ; type MYREC is record N : Int ; Str : PChar ; end record ; pragma Convention(C, MYREC); DefaultMyrec : MYREC := (0, null) ; type MYREC_Array is array (Int range <>) of aliased MYREC ; pragma Convention(C, MYREC_Array); -- Not sure if useful... -- Here is the trick package PMYREC is new Interfaces.C.Pointers (Int, MYREC, MYREC_Array, DefaultMyrec) ; function AllocMyrec (N : in Int) return PMYREC.Pointer ; pragma Import (C, AllocMyrec, "allocMyrec"); P : PMYREC.Pointer := AllocMyrec(5) ; StartP : PMYREC.Pointer := P ; -- Initial pointer A : MYREC_Array(0..4) := PMYREC.Value(P, 5) ; -- Here is a copy begin for I in A'Range loop -- Real access: IIO.Put(P.all.N) ; P.all.N := P.all.N + 3 ; -- Here you're really accessing the allocated memory, not just a copy PMYREC.Increment(P) ; -- 'Fake' access: IIO.Put(A(I).N) ; A(I).N := A(I).N + 3 ; -- Here you're accessing a copy, so the modification is not made on the allocated memory end loop ; Ada.Text_IO.New_Line ; end MYRECTest ; 

我测试了上面的代码,将C函数中所有_MYREC元素的n属性设置为42 + i并将其打印在Ada体中,它有效(我得到了42,43,44,45,46)。 C函数看起来像(测试):

 typedef struct _MYREC { int n ; char *str ; } MYREC ; MYREC * allocMyrec (int n) { MYREC *res = malloc(n * sizeof(MYREC)) ; int i ; for (i = 0 ; i < n ; i++) { res[i].n = 42 + i; } return res ; } 

DefaultMyrec变量没用,但是创建包是必需的。 我假设你总是使用带有length参数的Value函数来检索值。

有关该软件包的更多信息: http : //www.adaic.org/resources/add_content/standards/05rm/html/RM-B-3-2.html

编辑:原始代码正在制作P指向的内存的副本,因此如果更新数组A任何内容,则不会更改分配的内存。 实际上你应该直接使用编辑代码中显示的指针P.

编辑:我使用'愚蠢' access all Char MYRECstr属性的access all Char ,但你可以(并且应该)使用Interfaces.C.Pointers几乎相同的东西。 我测试了它,但不想把它放在答案中因为它没有添加任何东西。

对不起,我认为没有一般解决方案。 如果您声明一个可以访问MYREC_Array的类型,例如

 type MYREC_Array is array (Int range <>) of aliased MYREC; type MYREC_Array_Access is access all MYREC_Array; 

我认为GNAT编译器会期望这指向包含紧跟数据的边界的数据。 并且您不会让C allocMyrec为边界留出空间,除非您可以访问C代码并且可以编写一个为边界留出空间的包装器。 但要做到这一点,你必须确切知道GNAT编译器的工作原理,答案将与特定的实现相关联。 如果有一些特殊声明可以告诉GNAT将边界分开,我就不知道了。

有些编译器可能能够处理这个问题; 例如,Irvine编译器的Ada编译器将绑定信息保留为MYREC_Array_Access类型的一部分,而不是与数据连续。 (这种方法有优点和缺点。)对于这样的编译器,您可以构造一个具有所需边界的指针,并指向allocMyrec返回的数据。 但这样做需要使用未经检查的操作,并且高度针对特定于实现。

在某些情况下,您可以执行以下操作:

 procedure Do_Some_Work (Size : Integer) is subtype MYREC_Array_Subtype is MYREC_Array (1 .. Size); type MYREC_Array_Access is access all MYREC_Array_Subtype; function allocMyrec (Size : Interfaces.C.int) return MYREC_Array_Subtype; pragma Import (C, allocMyrec); begin ... 

现在,由于边界内置于子类型中,因此无需将它们存储在内存中的任何位置; 所以这将工作,并且每当你引用allocMyrec返回的数组元素时,编译器将确保索引在1..Size范围内。 但是您将无法在Do_Some_Work之外使用此结果。 您将无法将访问对象转换为Do_Some_Work外部定义的任何其他访问类型。 所以这是一个相当有限的解决方案。

编辑:我假设你想要一个指向数组的Ada访问对象; 如果没有,那么霍尔特的回答是好的。 对不起,如果我误解了你在找什么。

这是您在预览答案的评论中提出的问题的另一个答案(与之前的答案太长且太不同,只能进行编辑)。

所以你的C代码看起来像:

 typedef struct { ... } MYREC ; MYREC *last_allocated ; // Allocate an array of N MYREC and return N. // The allocated array is "stored" in last_allocated. int alloc_myrec (void) { ... } MYREC* get_last_allocated (void) { return last_allocated ; } 

然后你的阿达身体:

 procedure MYREC_Test is type MYREC is record ... end record ; pragma Convention(C, MYREC) ; -- Global and unconstrained array type MYREC_Array is array (Int range <>) of aliased MYREC ; pragma Convention(C, MYREC_Array); begin declare -- Allocate and retrieve the array Size : Int := AllocMyrec ; subtype MYREC_Array_Subtype is MYREC_Array (1 .. Size); type MYREC_Array_Access is access all MYREC_Array_Subtype; function GetAlloc return MYREC_Array_Access; pragma Import (C, GetAlloc, "get_last_alloc"); MyArray : MYREC_Array_Access := GetAlloc ; begin -- Do whatever you want with MyArray end ; end ; 

正如我在之前的评论中所说,以这种方式工作有点难看。 Interfaces.C.Pointers包意味着你可以做你想做的事情,如果你把所需的一切都放在一个包中,它会更容易使用。