Resolve union structure in Rust FFI

谁说我不能喝 提交于 2021-02-07 14:23:20

问题


I have problem with resolving c-union structure XEvent.

I'm experimenting with Xlib and X Record Extension in Rust. I'm generate ffi-bindings with rust-bindgen. All code hosted on github alxkolm/rust-xlib-record.

Trouble happen on line src/main.rs:106 when I try extract data from XEvent structure.


let key_event: *mut xlib::XKeyEvent = event.xkey();
println!("KeyPress {}", (*key_event).keycode); // this always print 128 on any key

My program listen key events and print out keycode. But it is always 128 on any key I press. I think this wrong conversion from C union type to Rust type.

Definition of XEvent starts here src/xlib.rs:1143. It's the c-union. Original C definition here.

Code from GitHub can be run by cargo run command. It's compile without errors.

What I do wrong?


回答1:


Beware that rustbindgen generates binding to C union with as much safety as in C; as a result, when calling:

event.xkey(); // gets the C union 'xkey' field

There is no runtime check that xkey is the field currently containing a value.

This is because since C does not have tagged union (ie, the union knowing which field is currently in use), developers came up with various ways of encoding this information (*), the two that I know of being:

  • an external supplier; typically another field of the structure right before the union
  • the first field of each of the structures in the union

Here, you are in the latter case int type; is the first field of the union and each nested structure starts with int _type; to denote this. As a result, you need a two-steps approach:

  1. consult type()
  2. depending on the value, call the correct reinterpretation

The mapping from the value of type to the actual field being used should be part of the documentation of the C library, hopefully.

I invite you to come up with a wrapper around this low-level union that will make it safer to retrieve the result. At the very least, you could check it is the right type in the accessor; the full approach being to come up with a Rust enum that would wrap proxies to all the fields and allow pattern-matching.

(*) and actually sometimes disregard it altogether, for example in C99 to reinterpret a float as an int a union { float f; int i; } can be used.



来源:https://stackoverflow.com/questions/26710596/resolve-union-structure-in-rust-ffi

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!