我如何处理Ada中的int **?

问题描述 投票:0回答:2

我正在尝试使用到它所来自的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;

这里是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吗?谢谢!

c pass-by-reference ada memory-address ffi
2个回答
2
投票

definition of SDL_LoadWAV_RW

如果成功调用,此函数将返回一个指向SDL_AudioSpec结构的指针,该结构填充有波形源数据的音频数据格式。 audio_buf用指向分配的包含音频数据的缓冲区的指针填充,而audio_len用该音频缓冲区的长度(以字节为单位)填充。

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

因此您提供的绑定不是很好的Ada。

[SDL_LoadWAV_RW应该是字节数组的audio_buf参数,outaudio_lenout参数。

作为演示,使用此C:

Uint32

此Ada

#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;
  }
}

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

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

定义一个指向这样的数组的指针。将存储空间限制为0意味着我们不能说type Raw_P is access all Raw with Convention => C, Storage_Size => 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; Raw并说]

Raw_P

然后使用

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

-1
投票

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

在调用SDL_LoadWAV之前,您需要输入以下内容:

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

先前Double_Pointer := SDL_Buffer'Address; 的定义如下:

SDL_Buffer

根据API,您稍后需要使用type Buffer_Access_Type is access all Buffer_Type; SDL_Buffer: Buffer_Access_Type; 释放SDL_Buffer

© www.soinside.com 2019 - 2024. All rights reserved.