On Thu, Jun 05, 2025 at 12:11:21PM +0200, Paolo Bonzini wrote:
> Date: Thu,  5 Jun 2025 12:11:21 +0200
> From: Paolo Bonzini <pbonz...@redhat.com>
> Subject: [PATCH preview 0/3] reviving minimal QAPI generation from 2021
> X-Mailer: git-send-email 2.49.0
> 
> This is just an extremely minimal extraction from the patches at
> https://patchew.org/QEMU/20210907121943.3498701-1-marcandre.lur...@redhat.com/,
> limited to generating structs and enums from the QAPI schema.
> It does not include them in any crate and does not compile them.
> 
> While I'm not going to work on this, I was curious how much work it
> was to produce *some* kind of Rust QAPI struct, which could be a first
> step towards using serde as an interface to C visitors, like this:
> 
> trait QapiType: FreeForeign {
>     unsafe fn visit(v: bindings::Visitor, name: *const c_char, obj: *mut 
> <Self as FreeForeign>::Foreign, errp: *mut *mut bindings::Error);
> }
> 
> fn to_c<T: QAPIType>(obj: &T) -> *mut <T as FreeForeign>::Foreign {
>     let mut ptr = libc::calloc(...);
>     let mut ser = QapiSerializer::<T>::new(ptr);
>     obj.serialize(&mut ser).unwrap();
>     ptr.cast()
> }
> 
> unsafe fn from_c<T: QAPIType>(obj: *const <T as FreeForeign>::Foreign) -> T {
>     let mut de = QapiDeserializer::new(T::visit, obj as *const c_void);
>     let value = de::Deserialize::deserialize(&mut de).unwrap();
>     de.end().unwrap();
>     value
> }
> 
> /* Generated code below: */
> 
> impl QapiType for UefiVariable {
>     unsafe fn visit(v: bindings::Visitor, name: *const c_char, obj: *mut 
> bindings::UefiVariable, errp: *mut *mut bindings::Error) {
>         unsafe extern "C" visit_type_DumpGuestMemoryFormat(v: 
> bindings::Visitor, name: *const c_char, obj: *mut bindings::UefiVariable, 
> errp: *mut *mut bindings::Error) {
>         unsafe { visit_type_DumpGuestMemoryFormat(v, name, obj, errp); }
>     }
> }
> 
> impl FreeForeign for UefiVariable {
>     type Foreign = bindings::UefiVariable;

My question seems to be different from Marc-André's...

As patch 3 did, qapi will generate Rust types:

- char* is mapped to String, scalars to there corresponding Rust types
- enums are simply aliased from FFI
- has_foo/foo members are mapped to Option<T>
- lists are represented as Vec<T>
- structures have Rust versions, with To/From FFI conversions

It seems we still need some raw bindings (generated by bindgen) as the
`type Foreign`, and then implement Foreign traits for the Rust
structures generated by this patch.

For this example, UefiVariable is generated by qapi (patch 3) and
bindings::UefiVariable is generated by bindgen. Ah! I feel I'm wrong,
could you please correct me?

Thanks,
Zhao

>     unsafe fn free_foreign(p: *mut bindings::UefiVariable) {
>         unsafe extern "C" qapi_free_UefiVariable(p: *mut 
> bindings::UefiVariable);
>         unsafe { qapi_free_UefiVariable(p); }
>     }
> }
> 
> impl CloneToForeign for UefiVariable {
>     fn clone_to_foreign(&self) -> OwnedPointer<Self> {
>         OwnedPointer::new(qapi::to_c(self))
>     }
> }
> 
> impl FromForeign for UefiVariable {
>     unsafe fn cloned_from_foreign(obj: *const bindings::UefiVariable) -> Self 
> {
>         qapi::from_c(obj)
>     }
> }
> 
> The FFI types could be generated by qapi-gen, as in Marc-André's
> proposal, or from bindgen.
> 
> I am not sure what approach is better---whether to use serde or to
> automatically generate the marshaling and unmarshaling code; and whether
> to use bindgen or generate C-compatible FFI types---but it made sense,
> from the point of view of extracting "some" code from Marc-André's
> proof of concept and enticing other people, :) to start from high-level
> types.
> 

Reply via email to