在编写与 C 代码接口的绑定时,我发现将具有灵活数组成员的结构的大量实例转换为 Ada 时存在问题,例如
struct example {
size_t length;
int body[];
};
Run Code Online (Sandbox Code Playgroud)
有人告诉我,Ada 可以使用可区分的类型来复制类似的行为,但我找不到一种方法来使用该字段length作为判别式,同时保持结构的布局,以便记录可以用于与C代码,类似
type Example (Count : Integer := Length) is record
Length : Unsigned_64;
Body : Integer (1 .. Count);
end record;
Run Code Online (Sandbox Code Playgroud)
有什么方法可以用该数组创建类似的类型吗?我现在一直默认获取该位置的地址并自己声明数组以供使用,有没有更好的方法?提前致谢。
下面是 Ada 代码调用 C 代码的示例,将此类对象从 Ada 传递到 C,再从 C 传递到 Ada。C 头文件是 c_example.h:
typedef struct {
size_t length;
int body[];
} example_t;
extern void example_print (example_t *e /* in */);
// Print the contents of e.
extern example_t * example_get (void);
// Return a pointer to an example_t that remains owned
// by the C part (that is, the caller can use it, but should
// not attempt to deallocate it).
Run Code Online (Sandbox Code Playgroud)
C代码是c_example.c:
#include <stdio.h>
#include "c_example.h"
void example_print (example_t *e /* in */)
{
printf ("C example: length = %zd\n", e->length);
for (int i = 0; i < e->length; i++)
{
printf ("body[ %d ] = %d\n", i, e->body[i]);
}
} // example_print
static example_t datum = {4,{6,7,8,9}};
example_t * example_get (void)
{
return &datum;
} // example_get
Run Code Online (Sandbox Code Playgroud)
C 到 Ada 的绑定在 c_binding.ads 中定义:
with Interfaces.C;
package C_Binding
is
pragma Linker_Options ("c_example.o");
use Interfaces.C;
type Int_Array is array (size_t range <>) of int
with Convention => C;
type Example_t (Length : size_t) is record
Bod : Int_Array(1 .. Length);
end record
with Convention => C;
type Example_ptr is access all Example_t
with Convention => C;
procedure Print (e : in Example_t)
with Import, Convention => C, External_Name => "example_print";
function Get return Example_Ptr
with Import, Convention => C, External_Name => "example_get";
end C_Binding;
Run Code Online (Sandbox Code Playgroud)
测试主程序为flexarr.adb:
with Ada.Text_IO;
with C_Binding;
procedure FlexArr
is
for_c : constant C_Binding.Example_t :=
(Length => 5, Bod => (55, 66, 77, 88, 99));
from_c : C_Binding.Example_ptr;
begin
C_Binding.Print (for_c);
from_c := C_Binding.Get;
Ada.Text_IO.Put_Line (
"Ada example: length =" & from_c.Length'Image);
for I in 1 .. from_c.Length loop
Ada.Text_IO.Put_Line (
"body[" & I'Image & " ] =" & from_c.Bod(I)'Image);
end loop;
end FlexArr;
Run Code Online (Sandbox Code Playgroud)
我这样构建程序:
gcc -c -Wall c_example.c
gnatmake -Wall flexarr.adb
Run Code Online (Sandbox Code Playgroud)
这是 ./flexarr 的输出:
C example: length = 5
body[ 0 ] = 55
body[ 1 ] = 66
body[ 2 ] = 77
body[ 3 ] = 88
body[ 4 ] = 99
Ada example: length = 4
body[ 1 ] = 6
body[ 2 ] = 7
body[ 3 ] = 8
body[ 4 ] = 9
Run Code Online (Sandbox Code Playgroud)
所以这似乎有效。然而,Ada 编译器 (gnat) 给了我一些来自 C_Binding 包的警告:
c_binding.ads:14:12: warning: discriminated record has no direct equivalent in C
c_binding.ads:14:12: warning: use of convention for type "Example_t" is dubious
Run Code Online (Sandbox Code Playgroud)
这意味着虽然此接口方法适用于 gnat,但它可能不适用于其他编译器,例如与记录的固定大小部分分开分配 Bod 组件数组的 Ada 编译器(无论此类编译器是否接受 Convention => C对于这种记录类型是有疑问的)。
为了使界面更加可移植,请进行以下更改。在 C_Binding 中,将 Length 从判别式更改为普通组件,并使 Bod 成为固定大小的数组,使用一些最大大小,这里以 1000 个元素为例:
type Int_Array is array (size_t range 1 .. 1_000) of int
with Convention => C;
type Example_t is record
Length : size_t;
Bod : Int_Array;
end record
with Convention => C;
Run Code Online (Sandbox Code Playgroud)
在测试主程序中,更改 for_c 的声明以用零填充数组:
for_c : constant C_Binding.Example_t :=
(Length => 5, Bod => (55, 66, 77, 88, 99, others => 0));
Run Code Online (Sandbox Code Playgroud)
为了提高速度,您可以让数组中未使用的部分未初始化:“others => <>”。
如果找不到相当小的最大大小,则应该可以将 C 绑定定义为实际大小的通用值。但这变得相当混乱。
请注意,如果所有记录/结构对象都是在 C 端创建的,并且 Ada 端仅读取和写入它们,则绑定中定义的最大大小仅用于索引边界检查,并且可以非常大而不影响内存使用情况。
在此示例中,我使 Ada 端从 1 开始索引,但如果您想让它更类似于 C 代码,您可以将其更改为从 0 开始。
最后,在非歧视的情况下,我建议将 Example_t 设为“有限”类型(“Example_t 类型是有限记录...”),这样您就不能分配该类型的整个值,也不能比较它们。原因是,当C端向Ada端提供Example_t对象时,该对象的实际大小可能小于Ada端定义的最大大小,但Ada赋值或比较会尝试使用最大大小,这可能会使程序读取或写入不应读取或写入的内存。
| 归档时间: |
|
| 查看次数: |
217 次 |
| 最近记录: |