将Ada连接到C - 从wchar_t获取宽字符串*

时间:2018-01-19 15:10:29

标签: ada gnat

我正在使用hidraw连接到USB设备(在Debian Stretch上),我需要以wchar_t*的形式处理USB设备提供的一些信息,我需要将其转换为(Ada){ {1}}。这给我们带来了一些麻烦,而且我没有看到使用Wide_StringInterfaces.C中的工具的清晰方法。

在不破坏其一致性的情况下编辑所有文件。他们将构建,但没有one of these,他们实际上不会运行。

问题是,Interfaces.C.StringsSerial Number等设备信息由Linux设备驱动程序显示为Product Name,我想从中返回access stddef_h.wchar_t甚至是Wide_String正常的字符串),我没有看到任何好的方法去那里。

Interfaces.C.Strings有function Value (Item : in chars_ptr) return String;但是我看不到Wide_String的等价物。所以我认为对于宽字符我需要一个等效的Value函数。

下面的方法使用To_Ada(来自Interfaces.C)返回给定wchar_array的Wide_String。当然,它失败了,因为access wchar_t无法转换为wchar_array

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

和不可避免的结果

  

./ test_hid
  宽字符串

     

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

类似的字符方法是可能的......如果(我不相信我这么说!)你可以增加访问类型......

感觉Interfaces.C中缺少一些东西......我错过了什么?任何想法绕过这个相对微不足道的看似绊脚石?

编辑:我倾向于Interfaces.C.Strings来自package hidapi_hidapi_h来源的一些无耻盗窃,并做出适当修改,但我欢迎其他建议。

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

第1步:使用gcc自动生成低级别的Ada绑定。

  

gcc -c -fdump-ada-spec-slim /usr/include/hidapi/hidapi.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;

wchar_t

这是一个低级绑定,暴露了C类型(并且绑定生成器已经确定Interfaces.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; 的一个,所以...

with Ada.Finalization;  use Ada.Finalization;
private with hidapi_hidapi_h;
private with System;

package hidapi is

   type id is new natural range 0 .. 2**16 - 1;
   type hid_device is new Limited_Controlled with private;

   -- find first matching devices by enumeration : the RA part of RAII. 
   function enumerate (vendor_id, product_id : id) return hid_device;

   -- accessors for device characteristics on enumerated device 
   function Serial_No      (D : hid_device) return Wide_String;
   function Product_String (D : hid_device) return Wide_String;

private

   type hid_device is new Limited_Controlled with record
      member : access hidapi_hidapi_h.hid_device_info;  
      addr : System.Address;
   end record;

end hidapi;

因为它是低级绑定;我们想隐藏它(并实现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; 以返回Wide_String。

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;

当然还有一个测试用例来运用它......

{{1}}

1 个答案:

答案 0 :(得分:1)

一个答案,在Tnterfaces.C.Strings的包体中盲目地复制方法,并进行必要的修改。

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

  • 允许地址算术。不是指针增量,而是指针+偏移量。一个变化是必须为4字节字符缩放偏移量。我没有以可移植的方式设置缩放,但请注意"+"将为每个不同的返回类型重载,以便针对不同的命名访问类型适当缩放偏移。
  • 允许在没有任何类型转换功能的情况下将stddef_h.wchar_t视为Wide_Wide_Character。表示是否正确是另一个问题(在这里,它是),但是这种技术也可以用来伪造适当的转换函数的输入类型,如Interfaces.C中的To_Ada

其余部分通过字符处理是直截了当的。另一个变化(到目前为止)是返回Wide_Wide_Character而不是Wide_Character(因为上面的stddef_h包显示,存储的字符是32位,大小与Interfaces.C.int相同我很高兴改变我的界面,但Ada.Strings包很容易处理Wide_String。

   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;