[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH 24/26] rust: qom: move device_id to PL011 class side
From: |
Zhao Liu |
Subject: |
Re: [PATCH 24/26] rust: qom: move device_id to PL011 class side |
Date: |
Wed, 18 Dec 2024 22:46:33 +0800 |
On Wed, Dec 18, 2024 at 11:26:35AM +0100, Paolo Bonzini wrote:
> Date: Wed, 18 Dec 2024 11:26:35 +0100
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: Re: [PATCH 24/26] rust: qom: move device_id to PL011 class side
>
> On 12/18/24 08:14, Paolo Bonzini wrote:
> > Moving on to another topic, about the gap (or question :-)) where a
> > child class inherits the ClassInitImpl trait from the parent, please see
> > my test case example below: Doing something similar to SysBusDevice and
> > DeviceState using a generic T outside of the QOM library would violate
> > the orphan rule.
> >
> > Ugh, you're right. Maybe ClassInitImpl should just be merged into
> > ObjectImpl etc. as a default method implementation. I will check.
>
> Ok, I think we can make it mostly a documentation issue:
>
> diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs
> index 2b472915707..ee95eda0447 100644
> --- a/rust/qemu-api/src/qom.rs
> +++ b/rust/qemu-api/src/qom.rs
> @@ -451,13 +451,14 @@ pub trait ObjectImpl: ObjectType +
> ClassInitImpl<Self::Class> {
> /// Each struct will implement this trait with `T` equal to each
> /// superclass. For example, a device should implement at least
> /// `ClassInitImpl<`[`DeviceClass`](crate::qdev::DeviceClass)`>` and
> -/// `ClassInitImpl<`[`ObjectClass`]`>`.
> +/// `ClassInitImpl<`[`ObjectClass`]`>`. Such implementations are
> +/// made in one of two ways.
> ///
> -/// Fortunately, this is almost never necessary. Instead, the Rust
> -/// implementation of methods will usually come from a trait like
> -/// [`ObjectImpl`] or [`DeviceImpl`](crate::qdev::DeviceImpl).
> -/// `ClassInitImpl` then can be provided by blanket implementations
> -/// that operate on all implementors of the `*Impl`* trait. For example:
> +/// For most superclasses, `ClassInitImpl` is provided by the `qemu-api`
> +/// crate itself. The Rust implementation of methods will come from a
> +/// trait like [`ObjectImpl`] or [`DeviceImpl`](crate::qdev::DeviceImpl),
> +/// and `ClassInitImpl` is provided by blanket implementations that
> +/// operate on all implementors of the `*Impl`* trait. For example:
> ///
> /// ```ignore
> /// impl<T> ClassInitImpl<DeviceClass> for T
> @@ -469,11 +470,37 @@ pub trait ObjectImpl: ObjectType +
> ClassInitImpl<Self::Class> {
> /// after initializing the `DeviceClass` part of the class struct,
> /// the parent [`ObjectClass`] is initialized as well.
> ///
> -/// The only case in which a manual implementation of the trait is needed
> -/// is for interfaces (note that there is no Rust example yet for using
> -/// interfaces). In this case, unlike the C case, the Rust class _has_
> -/// to define its own class struct `FooClass` to go together with
> -/// `ClassInitImpl<FooClass>`.
> +/// In some other cases, manual implementation of the trait is needed.
> +/// These are the following:
> +///
> +/// * for classes that implement interfaces, the Rust code _has_
> +/// to define its own class struct `FooClass` and implement
> +/// `ClassInitImpl<FooClass>`. `ClassInitImpl<FooClass>`'s
> +/// `class_init` method will then forward to multiple other
> +/// `class_init`s, for the interfaces as well as the superclass.
> +/// (Note that there is no Rust example yet for using
> +/// interfaces).
> +///
> +/// * for classes implemented outside the ``qemu-api`` crate, it's
> +/// not possible to add blanket implementations like the above one,
> +/// due to orphan rules. In that case, the easiest solution is to
> +/// implement `ClassInitImpl<YourSuperclass>` for each subclass,
> +/// and not have a `YourSuperclassImpl` trait at all:
> +///
> +/// ```ignore
> +/// impl ClassInitImpl<YourSuperclass> for YourSubclass {
> +/// fn class_init(klass: &mut YourSuperclass) {
> +/// klass.some_method = Some(Self::some_method);
> +/// <Self as ClassInitImpl<SysBusDeviceClass>>::class_init(&mut
> klass.parent_class);
> +/// }
> +/// }
> +/// ```
BTW, maybe we could also squash my previous example in test? :-)
> +///
> +/// While this method incurs a small amount of code duplication,
> +/// it is generally limited to the recursive call on the last line.
> +/// This is because classes defined in Rust do not need the same
> +/// glue code that is needed when the classes are defined in C code.
Now I understand this advantage.
> +/// You may consider using a macro if you have many subclasses.
Yes, a custom macro is enough!
> pub trait ClassInitImpl<T> {
> /// Initialize `klass` to point to the virtual method implementations
> /// for `Self`. On entry, the virtual method pointers are set to
All the above changes look good to me!
> Optionally, something like this can be squashed in this patch, but I
> do not think it's worth the savings of... 3 lines of code:
>
> diff --git a/rust/hw/char/pl011/src/device.rs
> b/rust/hw/char/pl011/src/device.rs
> index 41220c99a83..cbd3abb96ec 100644
> --- a/rust/hw/char/pl011/src/device.rs
> +++ b/rust/hw/char/pl011/src/device.rs
> @@ -46,11 +46,6 @@ fn index(&self, idx: hwaddr) -> &Self::Output {
> }
> }
> -impl DeviceId {
> - const ARM: Self = Self(&[0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05,
> 0xb1]);
> - const LUMINARY: Self = Self(&[0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05,
> 0xb1]);
> -}
> -
> #[repr(C)]
> #[derive(Debug, qemu_api_macros::Object, qemu_api_macros::offsets)]
> /// PL011 Device Model in QEMU
> @@ -112,7 +107,8 @@ unsafe impl ObjectType for PL011State {
> impl ClassInitImpl<PL011Class> for PL011State {
> fn class_init(klass: &mut PL011Class) {
> - klass.device_id = DeviceId::ARM;
> + klass.device_id = DeviceId(&[0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0,
> 0x05, 0xb1]);
> +
> <Self as ClassInitImpl<SysBusDeviceClass>>::class_init(&mut
> klass.parent_class);
> }
> }
> @@ -628,7 +624,8 @@ pub struct PL011Luminary {
> impl ClassInitImpl<PL011Class> for PL011Luminary {
> fn class_init(klass: &mut PL011Class) {
> - klass.device_id = DeviceId::LUMINARY;
> + klass.device_id = DeviceId(&[0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0,
> 0x05, 0xb1]);
> +
> <Self as ClassInitImpl<SysBusDeviceClass>>::class_init(&mut
> klass.parent_class);
> }
> }
Still fine for me. :-)
Regards,
Zhao
- Re: [PATCH 19/26] rust: rename qemu-api modules to follow C code a bit more, (continued)
[PATCH 24/26] rust: qom: move device_id to PL011 class side, Paolo Bonzini, 2024/12/09
- Re: [PATCH 24/26] rust: qom: move device_id to PL011 class side, Zhao Liu, 2024/12/16
- Re: [PATCH 24/26] rust: qom: move device_id to PL011 class side, Paolo Bonzini, 2024/12/17
- Re: [PATCH 24/26] rust: qom: move device_id to PL011 class side, Zhao Liu, 2024/12/18
- Re: [PATCH 24/26] rust: qom: move device_id to PL011 class side, Paolo Bonzini, 2024/12/18
- Re: [PATCH 24/26] rust: qom: move device_id to PL011 class side, Paolo Bonzini, 2024/12/18
- Re: [PATCH 24/26] rust: qom: move device_id to PL011 class side,
Zhao Liu <=
- Re: [PATCH 24/26] rust: qom: move device_id to PL011 class side, Paolo Bonzini, 2024/12/18
Re: [PATCH 24/26] rust: qom: move device_id to PL011 class side, Zhao Liu, 2024/12/18
Re: [PATCH 24/26] rust: qom: move device_id to PL011 class side, Zhao Liu, 2024/12/18
[PATCH 23/26] rust: qom: add initial subset of methods on Object, Paolo Bonzini, 2024/12/09
[PATCH 21/26] rust: tests: allow writing more than one test, Paolo Bonzini, 2024/12/09
[PATCH 26/26] rust: callbacks: allow passing optional callbacks as (), Paolo Bonzini, 2024/12/09