将Ada与C接口-从wchar\u t获取宽字符串*

将Ada与C接口-从wchar\u t获取宽字符串*,ada,gnat,Ada,Gnat,我正在使用hidraw连接到一个USB设备(Debian Stretch上),我需要处理USB设备以wchar\u t*的形式提供的一些信息,我需要将这些信息转换为(Ada)Wide\u String。这给我带来了一些麻烦,我没有看到一个干净的方法来使用Interfaces.C和Interfaces.C.Strings中的工具 所有文件都会向下编辑,而不会破坏其一致性。他们会建造,但如果没有,他们实际上不会运行 问题是,Linux设备驱动程序将诸如序列号和产品名称之类的设备信息显示为访问stdd

我正在使用hidraw连接到一个USB设备(Debian Stretch上),我需要处理USB设备以
wchar\u t*
的形式提供的一些信息,我需要将这些信息转换为(Ada)
Wide\u String
。这给我带来了一些麻烦,我没有看到一个干净的方法来使用
Interfaces.C
Interfaces.C.Strings
中的工具

所有文件都会向下编辑,而不会破坏其一致性。他们会建造,但如果没有,他们实际上不会运行

问题是,Linux设备驱动程序将诸如
序列号
产品名称
之类的设备信息显示为
访问stddef_h.wchar_t
,我想从中返回一个
宽字符串
,甚至是一个普通字符串),我没有找到任何好的方法

Interfaces.C.Strings具有
函数值(项:在chars\u ptr中)返回字符串但我看不到宽字符串的等效项。所以我想我需要一个等价的
函数来处理宽字符

下面的方法使用
To_Ada
(来自Interfaces.C)返回给定
wchar\u数组的宽字符串。当然,它失败了,因为
访问wchar\u t
不能转换为
wchar\u数组

-- helper function to deal with wchar_t * to wide_string
   function Value (P : access stddef_h.wchar_t) return Wide_String is
      temp : Wide_String(1 .. 256);
      count : natural := 0;
      -- ugliness to convert pointer types
      type sd_wchar_ptr is access all stddef_h.wchar_t;
      type wchar_array_ptr is access wchar_array;
      Function To_Wchar_Array_Ptr is new Ada.Unchecked_Conversion(sd_wchar_ptr, wchar_array_ptr);

      -- this does NOT create the required wchar_array pointer
      WCP : wchar_array_ptr := To_Wchar_Array_Ptr(sd_wchar_ptr(P));
   begin
      Put_Line("Wide string");
      To_Ada(WCP.all, temp, count);
      Put_Line("Wide string length " & natural'image(count));
      return temp(1..count); 
   end Value;
这是必然的结果

/测试\u hid
宽弦

由未处理的异常引发的存储错误终止执行: 堆栈溢出或错误的内存访问

类似的逐个字符的方法是可能的。。。如果(我真不敢相信我会这么说!)您可以增加访问类型

感觉界面缺少了一些东西。这里是C。。。我错过了什么?有没有办法绕过这个看似微不足道的绊脚石

编辑:我倾向于对
Interfaces.C.Strings
源代码进行一些无耻的盗用,并进行了适当的修改,但我欢迎其他建议


下面的其余部分是到目前为止的完整故事(包括复制所需的所有代码)

步骤1:使用gcc自动生成低级Ada绑定

gcc-c-fdump ada spec slim/usr/include/hidapi/hidapi.h

生成低级别绑定
包hidapi\u hidapi\u h

pragma Ada_2005;
pragma Style_Checks (Off);

with Interfaces.C; use Interfaces.C;
with Interfaces.C.Strings;
with stddef_h;
with System;

package hidapi_hidapi_h is

   --  see source file /usr/include/hidapi/hidapi.h

   type hid_device_info is record
      path : Interfaces.C.Strings.chars_ptr;  -- /usr/include/hidapi/hidapi.h:51
      vendor_id : aliased unsigned_short;  -- /usr/include/hidapi/hidapi.h:53
      product_id : aliased unsigned_short;  -- /usr/include/hidapi/hidapi.h:55
      serial_number : access stddef_h.wchar_t;  -- /usr/include/hidapi/hidapi.h:57
      release_number : aliased unsigned_short;  -- /usr/include/hidapi/hidapi.h:60
      manufacturer_string : access stddef_h.wchar_t;  -- /usr/include/hidapi/hidapi.h:62
      product_string : access stddef_h.wchar_t;  -- /usr/include/hidapi/hidapi.h:64
      usage_page : aliased unsigned_short;  -- /usr/include/hidapi/hidapi.h:67
      usage : aliased unsigned_short;  -- /usr/include/hidapi/hidapi.h:70
      interface_number : aliased int;  -- /usr/include/hidapi/hidapi.h:75
      next : access hid_device_info;  -- /usr/include/hidapi/hidapi.h:78
   end record;
   pragma Convention (C_Pass_By_Copy, hid_device_info);  -- /usr/include/hidapi/hidapi.h:49

   function hid_enumerate (arg1 : unsigned_short; arg2 : unsigned_short) return access hid_device_info;  -- /usr/include/hidapi/hidapi.h:132
   pragma Import (C, hid_enumerate, "hid_enumerate");

end hidapi_hidapi_h;
这是一个低级绑定,公开C类型(绑定生成器已决定
接口中的
wchar\u t
。C
不够好,它还需要
stddef.h
中的一个,因此

pragma Ada_2005;
pragma Style_Checks (Off);

with Interfaces.C; use Interfaces.C;

package stddef_h is

   --  unsupported macro: NULL ((void *)0)
   subtype size_t is unsigned_long;  -- /usr/lib/gcc/x86_64-linux-gnu/6/include/stddef.h:216

   subtype wchar_t is int;  -- /usr/lib/gcc/x86_64-linux-gnu/6/include/stddef.h:328

end stddef_h;
因为它是一个低级绑定;我们希望将它隐藏(并实现RAII等)在一个更简单、更可用的高级绑定后面,所以…(下面)

及其实现,包含返回宽字符串的问题函数
value

with hidapi_hidapi_h;
with Interfaces.C; use Interfaces.C;
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Unchecked_Conversion;
with stddef_h;

package body hidapi is

   function enumerate (vendor_id, product_id : id) return hid_device is
      use hidapi_hidapi_h;
      first  : access hid_device_info;
   begin
      first  := hid_enumerate(unsigned_short(vendor_id), unsigned_short(product_id));
      if first /= null then
         return H : hid_device do
            H.member := first;
            H.addr := System.Null_Address;
         end return;
      else raise Program_Error;
      end if;
   end enumerate;

-- helper function to deal with wchar_t * to wide_string
   function Value (P : access stddef_h.wchar_t) return Wide_String is
      temp : Wide_String(1 .. 256);
      count : natural := 0;

      type sd_wchar_ptr is access all stddef_h.wchar_t;
      type wchar_array_ptr is access wchar_array;
      Function To_Wchar_Array_Ptr is new Ada.Unchecked_Conversion(sd_wchar_ptr, wchar_array_ptr);

      WCP : wchar_array_ptr := To_Wchar_Array_Ptr(sd_wchar_ptr(P));
   begin
      Put_Line("Wide string");
      To_Ada(WCP.all, temp, count);
      Put_Line("Wide string length " & natural'image(count));
      return temp(1..count); 
   end Value;

   function Serial_No (D : hid_device) return Wide_String is
   use hidapi_hidapi_h;
   begin
      return Value(D.member.serial_number);
   end Serial_No;

   function Product_String (D : hid_device) return Wide_String is
   use hidapi_hidapi_h;
   begin
      return Value(D.member.product_string);
   end Product_String;

end hidapi;
当然还有一个测试用例来练习它

with Hidapi;
with Ada.Wide_Text_IO;

procedure Test_Hid is

      usbrelay_vendor_id  : constant Hidapi.id := 16#16c0#;
      usbrelay_product_id : constant Hidapi.id := 16#05df#;

      Device : Hidapi.hid_device := Hidapi.Enumerate(usbrelay_vendor_id, usbrelay_product_id);
begin

   Ada.Wide_Text_IO.Put_Line("Serial  : " & Device.Serial_No);
   Ada.Wide_Text_IO.Put_Line("Product : " & Device.Product_String);

end Test_Hid;

一个答案是,在对
Tnterfaces.C.Strings
进行必要更改的情况下,盲目地复制包体中的方法

顽皮的东西在函数
“+”
Peek
中,它们在指针上使用未经检查的转换

  • 允许地址运算。不是指针增量,而是指针+偏移量。一个变化是偏移量必须缩放为4字节字符。我没有以可移植的方式设置这种缩放,但请注意,
    “+”
    将为每个不同的返回类型重载,以便为不同的命名访问类型适当缩放偏移量
  • 在没有任何类型转换函数的情况下,允许将
    stddef_h.wchar_t
    视为
    Wide_Wide_字符
    。表示是否正确是另一回事(这里是)但是这种技术也可以用来伪造接口中合适的转换函数的输入类型,如
    到_Ada
剩下的是简单的逐字符处理。另一个变化(到目前为止)是返回
Wide\u Wide\u character
,而不是
Wide\u character
(因为正如上面的
stddef_h
包所示,存储的字符是32位的,与
Interfaces.C.int
大小相同。我很乐意更改我的接口,但是宽字符串可以由
Ada.Strings
包轻松处理

   type sd_wchar_ptr is access all stddef_h.wchar_t;
   type w_w_char_ptr is access all char32_t;

   -- Two Unchecked_Conversions to allow pointer arithmetic
   -- And a third to allow the resulting storage to be interpreted as Wide_Wide_Char
   function To_Sd_wchar_ptr is new Ada.Unchecked_Conversion (System.Address, sd_wchar_ptr);
   function To_Address is new Ada.Unchecked_Conversion (sd_wchar_ptr, System.Address);
   function To_Wchar_Ptr is new Ada.Unchecked_Conversion (sd_wchar_ptr, w_w_char_ptr);

   -- pointer + offset arithmetic, with offset scaled for size of stddef_h.wchar_t;
   -- TODO: attempted better way of computing word size ran into type errors
   function "+" (Left : sd_wchar_ptr; Right : size_t) return sd_wchar_ptr is
   begin
      return To_Sd_wchar_ptr (To_Address (Left) + Storage_Offset (Right) * 4);
   end "+";

   function Peek (From : sd_wchar_ptr) return char32_t is
   begin
      return To_Wchar_Ptr(From).all;
   end Peek;

   function Strlen (Item : sd_wchar_ptr) return size_t is
      Item_Index : size_t := 0;

   begin
      if Item = Null then
         raise Program_Error;
      end if;

      loop
         if Peek (Item + Item_Index) = char32_nul then
            return Item_Index;
         end if;

         Item_Index := Item_Index + 1;
      end loop;
   end Strlen;

   function Value (Item : sd_wchar_ptr) return char32_array is
      Result : char32_array (0 .. Strlen (Item));

   begin
      if Item = Null then
         raise Program_Error;
      end if;
      Put_Line("String length " & size_t'image(Strlen(Item)));
      --  Note that the following loop will also copy the terminating Nul

      for J in Result'Range loop
         Result (J) := Peek (Item + J);
      end loop;

      return Result;
   end Value;

-- helper function to deal with wchar_t * to wide_wide_string
   function Value (Item : access stddef_h.wchar_t) return Wide_Wide_String is
   begin
      return To_Ada (Value (sd_wchar_ptr(Item)));
   end Value;
   type sd_wchar_ptr is access all stddef_h.wchar_t;
   type w_w_char_ptr is access all char32_t;

   -- Two Unchecked_Conversions to allow pointer arithmetic
   -- And a third to allow the resulting storage to be interpreted as Wide_Wide_Char
   function To_Sd_wchar_ptr is new Ada.Unchecked_Conversion (System.Address, sd_wchar_ptr);
   function To_Address is new Ada.Unchecked_Conversion (sd_wchar_ptr, System.Address);
   function To_Wchar_Ptr is new Ada.Unchecked_Conversion (sd_wchar_ptr, w_w_char_ptr);

   -- pointer + offset arithmetic, with offset scaled for size of stddef_h.wchar_t;
   -- TODO: attempted better way of computing word size ran into type errors
   function "+" (Left : sd_wchar_ptr; Right : size_t) return sd_wchar_ptr is
   begin
      return To_Sd_wchar_ptr (To_Address (Left) + Storage_Offset (Right) * 4);
   end "+";

   function Peek (From : sd_wchar_ptr) return char32_t is
   begin
      return To_Wchar_Ptr(From).all;
   end Peek;

   function Strlen (Item : sd_wchar_ptr) return size_t is
      Item_Index : size_t := 0;

   begin
      if Item = Null then
         raise Program_Error;
      end if;

      loop
         if Peek (Item + Item_Index) = char32_nul then
            return Item_Index;
         end if;

         Item_Index := Item_Index + 1;
      end loop;
   end Strlen;

   function Value (Item : sd_wchar_ptr) return char32_array is
      Result : char32_array (0 .. Strlen (Item));

   begin
      if Item = Null then
         raise Program_Error;
      end if;
      Put_Line("String length " & size_t'image(Strlen(Item)));
      --  Note that the following loop will also copy the terminating Nul

      for J in Result'Range loop
         Result (J) := Peek (Item + J);
      end loop;

      return Result;
   end Value;

-- helper function to deal with wchar_t * to wide_wide_string
   function Value (Item : access stddef_h.wchar_t) return Wide_Wide_String is
   begin
      return To_Ada (Value (sd_wchar_ptr(Item)));
   end Value;