将 Ada 连接到 C - 从 wchar_t 获取宽字符串 *
Interfacing Ada to C - getting Wide Strings from wchar_t *
我正在使用 hidraw 连接到 USB 设备(在 Debian Stretch 上),我需要以 wchar_t*
的形式处理 USB 设备提供的一些信息,我需要将其转换成(Ada ) Wide_String
。这带来了一些麻烦,我没有看到使用 Interfaces.C
和 Interfaces.C.Strings
.
中的设施的干净方法
所有文件都在不破坏其一致性的情况下进行了编辑。他们会建造,但如果没有 one of these,他们实际上不会 运行.
问题是 Serial Number
和 Product Name
等设备信息由 Linux 设备驱动程序呈现为 access stddef_h.wchar_t
,我想从中 return 一个 Wide_String
甚至是一个普通的字符串),我没有看到任何到达那里的好方法。
Interfaces.C.Strings 有 function Value (Item : in chars_ptr) return String;
但我可以看到不存在 Wide_String 的等价物。所以我想我需要一个等效的 Value
宽字符函数。
下面的方法使用 To_Ada
(从 Interfaces.C)到 return 给定 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
Wide string
Execution terminated by unhandled exception raised STORAGE_ERROR :
stack overflow or erroneous memory access
一个类似的逐个字符的方法是可能的......如果(我不敢相信我在说这个!)你可以增加访问类型......
感觉Interfaces.C这里少了点什么……我错过了什么?有什么想法可以绕过这个看似微不足道的绊脚石吗?
编辑:我倾向于从 Interfaces.C.Strings
源中进行一些无耻的盗窃并进行适当的更改,但我欢迎其他建议。
下面的其余部分是到目前为止的完整故事(包括重现所需的所有代码)
第 1 步:使用 gcc 自动生成低级 Ada 绑定。
gcc -c -fdump-ada-spec-slim /usr/include/hidapi/hidapi.h
生成低级绑定package 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;
这是一个低级绑定,公开了 C 类型(并且绑定生成器认为 Interfaces.C
中的 wchar_t
不够好,它需要 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 等)在一个更简单、更有用的高级绑定后面,所以 ...(下)
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;
及其实现,包含问题函数value
到return一个Wide_String.
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 字节字符进行缩放。我没有以可移植的方式设置缩放比例,但请注意
"+"
将为每个不同的 return 类型重载,以便为不同的命名访问类型适当地缩放偏移量。
- 允许
stddef_h.wchar_t
在没有任何类型转换函数的情况下被视为 Wide_Wide_Character
。表示是否正确是另一回事(这里是正确的),但是这种技术也可以用来伪造合适的转换函数的输入类型,例如 Interfaces.C 中的 To_Ada
。
其余部分是逐字符处理的直接字符。另一个变化(到目前为止)是 return 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;
我正在使用 hidraw 连接到 USB 设备(在 Debian Stretch 上),我需要以 wchar_t*
的形式处理 USB 设备提供的一些信息,我需要将其转换成(Ada ) Wide_String
。这带来了一些麻烦,我没有看到使用 Interfaces.C
和 Interfaces.C.Strings
.
所有文件都在不破坏其一致性的情况下进行了编辑。他们会建造,但如果没有 one of these,他们实际上不会 运行.
问题是 Serial Number
和 Product Name
等设备信息由 Linux 设备驱动程序呈现为 access stddef_h.wchar_t
,我想从中 return 一个 Wide_String
甚至是一个普通的字符串),我没有看到任何到达那里的好方法。
Interfaces.C.Strings 有 function Value (Item : in chars_ptr) return String;
但我可以看到不存在 Wide_String 的等价物。所以我想我需要一个等效的 Value
宽字符函数。
下面的方法使用 To_Ada
(从 Interfaces.C)到 return 给定 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
Wide stringExecution terminated by unhandled exception raised STORAGE_ERROR : stack overflow or erroneous memory access
一个类似的逐个字符的方法是可能的......如果(我不敢相信我在说这个!)你可以增加访问类型......
感觉Interfaces.C这里少了点什么……我错过了什么?有什么想法可以绕过这个看似微不足道的绊脚石吗?
编辑:我倾向于从 Interfaces.C.Strings
源中进行一些无耻的盗窃并进行适当的更改,但我欢迎其他建议。
下面的其余部分是到目前为止的完整故事(包括重现所需的所有代码)
第 1 步:使用 gcc 自动生成低级 Ada 绑定。
gcc -c -fdump-ada-spec-slim /usr/include/hidapi/hidapi.h
生成低级绑定package 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;
这是一个低级绑定,公开了 C 类型(并且绑定生成器认为 Interfaces.C
中的 wchar_t
不够好,它需要 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 等)在一个更简单、更有用的高级绑定后面,所以 ...(下)
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;
及其实现,包含问题函数value
到return一个Wide_String.
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 字节字符进行缩放。我没有以可移植的方式设置缩放比例,但请注意
"+"
将为每个不同的 return 类型重载,以便为不同的命名访问类型适当地缩放偏移量。 - 允许
stddef_h.wchar_t
在没有任何类型转换函数的情况下被视为Wide_Wide_Character
。表示是否正确是另一回事(这里是正确的),但是这种技术也可以用来伪造合适的转换函数的输入类型,例如 Interfaces.C 中的To_Ada
。
其余部分是逐字符处理的直接字符。另一个变化(到目前为止)是 return 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;