我终于用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,如编辑后的代码所示。
编辑:我为MYREC 的str 属性使用了一个“愚蠢的”access all Char,但如果需要,您可以(并且应该)使用几乎与Interfaces.C.Pointers 相同的东西。我测试了它,但不想把它放在答案中,因为它没有添加任何东西。