我如何在 Ada 中处理一个 int** ?

How do I handle an int** in Ada?

我正在尝试调用 SDL_LoadWAV 使用预制绑定到它来自的 C 库。 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;

这里是C函数的原型:

SDL_AudioSpec* SDL_LoadWAV_RW(SDL_RWops*     src,
                          int            freesrc,
                          SDL_AudioSpec* spec,
                          Uint8**        audio_buf,
                          Uint32*        audio_len)

(See here for more information)

现在如您所见,它以 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");

如您所见,绑定将 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;

但是,就像我尝试过的或多或少的其他所有操作一样,我在执行 Load 函数时得到 PROGRAM_ERROR/EXCEPTION_ACCESS_VIOLATION。

谁能知道我需要如何处理这个 System.Address?谢谢!

SDL_LoadWAV 将在您提供的位置写入一个指针(指向由 SDL 分配的新缓冲区),即未初始化变量的值 Double_Pointer 这是一个随机地址 -> kaboom !

在调用 SDL_LoadWAV 之前,您需要有如下内容:

Double_Pointer := SDL_Buffer'Address;

其中 SDL_Buffer 之前是这样定义的:

type Buffer_Access_Type is access all Buffer_Type;
SDL_Buffer: Buffer_Access_Type;

根据 API,您需要稍后使用 SDL_FreeWAV 释放 SDL_Buffer

definition of SDL_LoadWAV_RW

This function, if successfully called, returns a pointer to an SDL_AudioSpec structure filled with the audio data format of the wave source data. audio_buf is filled with a pointer to an allocated buffer containing the audio data, and audio_len is filled with the length of that audio buffer in bytes.

这意味着被调用函数分配所需的内存并填充它,然后returns指向分配的内存及其长度的指针。

所以提供给您的绑定不是很好的 Ada。

audio_buf 应该是字节数组的 out 参数,audio_lenUint32.

out 参数

作为演示,使用这个 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;
  }
}

这个艾达

type Raw is array (Interfaces.Unsigned_32) of Interfaces.Unsigned_8
with Convention => C;

定义了一个数组类型(如果我们实际声明一个,它将占用 2^32-1 个字节!),而这个

type Raw_P is access all Raw
with Convention => C, Storage_Size => 0;

定义了一个指向这样一个数组的指针。将存储大小限制为 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;

给出一个程序,当 运行 结果为

$ ./demo
 0 1 2 3 4 5 6 7 8 9
$

----

认识到您可能陷入困境

audio_buf : System.Address;

你可以定义(或使用,如果已经定义)像我的 RawRaw_P 和 say

procedure Get_Data (In_Buffer : System.Address;
                    Length    : out Interfaces.Unsigned_32)
with
  Import,
  Convention    => C,
  External_Name => "get_data";

然后使用

Get_Data (In_Buffer => Allocated'Address,
          Length    => Length);