Dev*_*man 2 c ada ffi pass-by-reference memory-address
我正在尝试使用对它来自的C库的预先绑定来调用SDL_LoadWAV。SDL_LoadWAV只是SDL_LoadWAV_RW的包装:
function SDL_LoadWAV
(file : C.char_array;
spec : access SDL_AudioSpec;
audio_buf : System.Address;
audio_len : access Uint32) return access SDL_AudioSpec
is
begin
return SDL_LoadWAV_RW
(SDL_RWFromFile (file, C.To_C ("rb")),
1,
spec,
audio_buf,
audio_len);
end SDL_LoadWAV;
Run Code Online (Sandbox Code Playgroud)
这是C函数的原型:
SDL_AudioSpec* SDL_LoadWAV_RW(SDL_RWops* src,
int freesrc,
SDL_AudioSpec* spec,
Uint8** audio_buf,
Uint32* audio_len)
Run Code Online (Sandbox Code Playgroud)
现在您可以看到,它以Uint8 **的形式通过引用传递了一个Uint8(无符号8位整数)数组。这使我非常烦恼。这是适当的绑定:
function SDL_LoadWAV_RW
(src : access SDL_RWops;
freesrc : C.int;
spec : access SDL_AudioSpec;
audio_buf : System.Address;
audio_len : access Uint32) return access SDL_AudioSpec;
pragma Import (C, SDL_LoadWAV_RW, "SDL_LoadWAV_RW");
Run Code Online (Sandbox Code Playgroud)
如您所见,绑定将Uint8 **映射到System.Address。我尝试了一些技巧来将数据获取到想要的位置,但是似乎没有任何效果。现在,我的代码看起来像这样(其中包含一些自定义类型和异常):
type Music is new Resource with
record
--Id : Integer; (Inherited from Resource)
--Filename : Unbounded_String; (Inherited from Resource)
--Archive_Name : Unbounded_String; (Inherited from Resource)
--Zzl_Size : Integer; (Inherited from Resource)
Audio : access SDL_AudioSpec_Access;
Length : aliased Uint32;
Buffer : System.Address;
Position : Integer := 1;
end record;
overriding procedure Load(Mus : in out Music) is
Double_Pointer : System.Address;
begin
Log("Loading music " & To_Ada(Get_Audio_Filepath(Mus)));
Audio_Load_Lock.Seize;
if null = SDL_LoadWAV(Get_Audio_Filepath(Mus), Mus.Audio.all, Double_Pointer, Mus.Length'access) then
raise Audio_Load_Failed with To_String(Mus.Filename) & "&Stack=" & Get_Call_Stack;
end if;
Log("Music length =" & Integer'Image(Integer(Mus.Length)));
declare
type Sample_Array is array(1..Mus.Length) of Uint8;
Single_Pointer : System.Address;
for Single_Pointer'address use Double_Pointer;
pragma Import(Ada, Single_Pointer);
Source : Sample_Array;
for Source'address use Single_Pointer;
pragma Import(Ada, Source);
Dest : Sample_Array;
for Dest'address use Mus.Buffer;
pragma Import(Ada, Dest);
begin
Dest := Source;
end;
Audio_Load_Lock.Release;
end Load;
Run Code Online (Sandbox Code Playgroud)
但是,就像我尝试过的所有其他内容一样,执行Load函数时,我得到一个PROGRAM_ERROR / EXCEPTION_ACCESS_VIOLATION。
谁能弄清楚我需要如何处理此System.Address?谢谢!
如果成功调用此函数,则该函数返回指向SDL_AudioSpec结构的指针,该结构填充有波形源数据的音频数据格式。audio_buf用指向分配的包含音频数据的缓冲区的指针填充,而audio_len用该音频缓冲区的长度(以字节为单位)填充。
这意味着被调用函数分配所需的内存并填充它,然后返回指向分配的内存及其长度的指针。
因此,您提供的绑定不是很好的Ada。
audio_buf应该是out字节数组audio_len的out参数,是的参数Uint32。
作为演示,使用此C:
#include <stdlib.h>
void get_data (char **buf, int *len)
{
*len = 10;
*buf = malloc(*len);
for (int j = 0; j < *len; j++) {
(*buf)[j] = j;
}
}
Run Code Online (Sandbox Code Playgroud)
这个阿达
type Raw is array (Interfaces.Unsigned_32) of Interfaces.Unsigned_8
with Convention => C;
Run Code Online (Sandbox Code Playgroud)
定义一个数组类型(如果我们实际声明一个数组类型,它将占用2 ^ 32-1个字节!),
type Raw_P is access all Raw
with Convention => C, Storage_Size => 0;
Run Code Online (Sandbox Code Playgroud)
定义指向此类数组的指针。将存储大小限制为0意味着我们不能说new Raw_P。
放在一起,
with Ada.Text_IO; use Ada.Text_IO;
with Interfaces;
procedure Demo is
type Raw is array (Interfaces.Unsigned_32) of Interfaces.Unsigned_8
with Convention => C;
type Raw_P is access all Raw
with Convention => C, Storage_Size => 0;
procedure Get_Data (In_Buffer : out Raw_P;
Length : out Interfaces.Unsigned_32)
with
Import,
Convention => C,
External_Name => "get_data";
Allocated : Raw_P;
Length : Interfaces.Unsigned_32;
use type Interfaces.Unsigned_32;
begin
Get_Data (In_Buffer => Allocated,
Length => Length);
for J in 0 .. Length - 1 loop
Put (Allocated (J)'Image);
end loop;
New_Line;
end Demo;
Run Code Online (Sandbox Code Playgroud)
给出一个程序,在运行时会导致
$ ./demo
0 1 2 3 4 5 6 7 8 9
$
Run Code Online (Sandbox Code Playgroud)
认识到您可能会坚持
audio_buf : System.Address;
Run Code Online (Sandbox Code Playgroud)
您可以定义(或使用,如果已经定义的话)类似my的内容Raw,Raw_P然后说
procedure Get_Data (In_Buffer : System.Address;
Length : out Interfaces.Unsigned_32)
with
Import,
Convention => C,
External_Name => "get_data";
Run Code Online (Sandbox Code Playgroud)
然后使用
Get_Data (In_Buffer => Allocated'Address,
Length => Length);
Run Code Online (Sandbox Code Playgroud)