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