Skip to main content

qtbridge_interfaces/
genericrustproxy.rs

1// Copyright (C) 2026 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only
3
4use crate::{RustObjAccess, call_rust_trait_impl, call_cpp_impl};
5use qtbridge_runtime::qproxies::{QRustProxy, QCppProxy, PlacementAddress};
6use qtbridge_runtime::{DispatchMetaCall, DynamicMetaObjectData};
7use qtbridge_type_lib::QVariant;
8use std::cell::RefCell;
9use std::pin::Pin;
10use std::rc::Rc;
11
12pub struct GenericRustProxy<CppProxy: QCppProxy, Adapter: ?Sized> {
13    pub(crate) cpp_proxy: *mut CppProxy,
14    pub(crate) rust_obj: RustObjAccess<Adapter>,
15    pub(crate) on_drop: Box<dyn FnOnce()>,
16}
17
18impl<CppProxy: QCppProxy, Adapter: ?Sized> GenericRustProxy<CppProxy, Adapter> {
19    pub(crate) fn cpp_proxy_ref(&self) -> &CppProxy {
20        // SAFETY: set in `new()` and valid until the paired C++ destructor drops `self`.
21        unsafe { self.cpp_proxy.as_ref() }.expect("cpp_proxy was null")
22    }
23
24    // &mut from &self is deliberate: the proxy is interior-mutable and C++ re-enters it;
25    // CppProxy is an opaque cxx type, all state lives in C++.
26    #[allow(clippy::mut_from_ref)]
27    pub(crate) fn cpp_proxy_pin_mut(&self) -> Pin<&mut CppProxy> {
28        // SAFETY: as in `cpp_proxy_ref`.
29        let proxy = unsafe { self.cpp_proxy.as_mut() }.expect("cpp_proxy was null");
30        // SAFETY: the C++ object is heap- or placement-allocated and never moves.
31        unsafe { Pin::new_unchecked(proxy) }
32    }
33}
34
35// SAFETY: `new` boxes `self` and pairs it with a freshly created C++ proxy,
36// which owns `self` and outlives it. `rust_obj` keeps the `Rc` passed to `new`.
37unsafe impl<CppProxy, Adapter> QRustProxy for GenericRustProxy<CppProxy, Adapter>
38where
39    CppProxy: QCppProxy<ProxyRustType = Self>,
40    Adapter: ?Sized + DispatchMetaCall,
41{
42    type ProxyCppType = CppProxy;
43    type AdapterType = Adapter;
44
45    fn new(
46        rust_obj: &Rc<RefCell<Adapter>>,
47        metatype: &'static DynamicMetaObjectData,
48        at_address: Option<PlacementAddress>,
49        on_drop: Box<dyn FnOnce() + 'static>,
50    ) -> *mut Self {
51        let boxed_self = Box::new(Self {
52            cpp_proxy: std::ptr::null_mut(),
53            rust_obj: RustObjAccess::new(rust_obj.clone()),
54            on_drop,
55        });
56        let raw_self = Box::into_raw(boxed_self);
57        unsafe {
58            (*raw_self).cpp_proxy = match at_address {
59                Some(addr) => CppProxy::create_at(raw_self, metatype, addr),
60                None => CppProxy::create(raw_self, metatype),
61            }
62        };
63        raw_self
64    }
65
66    fn get_cpp_proxy(&self) -> *const CppProxy {
67        self.cpp_proxy
68    }
69
70    fn get_cpp_proxy_mut(&self) -> *mut CppProxy {
71        self.cpp_proxy
72    }
73
74    fn emit_signal(&self, mut_ref: &mut Adapter, signal_name: &str, argv: &[*const u8]) {
75        call_cpp_impl!(mut self, mut_ref, emit_signal(signal_name, argv))
76    }
77
78    fn with_rust_ref<R, F: FnOnce(&Adapter) -> R>(&self, f: F) -> R {
79        self.rust_obj
80            .try_call_rust_with_handle(|adapter| f(adapter))
81            .expect("Failed to access Rust object via shared handle")
82    }
83
84    fn with_rust_ref_mut<R, F: FnOnce(&mut Adapter) -> R>(&self, f: F) -> R {
85        self.rust_obj
86            .try_call_rust_with_handle_mut(|adapter| f(adapter))
87            .expect("Failed to access Rust object via mutable handle")
88    }
89
90    fn get_rust_object_rc(&self) -> Rc<RefCell<Adapter>> {
91        self.rust_obj.get_rc()
92    }
93}
94
95impl<CppProxy, Adapter> GenericRustProxy<CppProxy, Adapter>
96where
97    CppProxy: QCppProxy<ProxyRustType = Self>,
98    Adapter: ?Sized + DispatchMetaCall,
99{
100    /// Drops the instance pointed to by `self_ptr`.
101    ///
102    /// Intended to be called exclusively from the paired C++ destructor.
103    ///
104    /// # Safety
105    ///
106    /// `self_ptr` must be a pointer previously returned by [`QRustProxy::new`].
107    /// It must not have been dropped already.
108    pub unsafe fn drop_self(self_ptr: *mut Self) {
109        let boxed_self = unsafe { Box::from_raw(self_ptr) };
110        (boxed_self.on_drop)();
111    }
112
113    /// Call the slot with id `slot_id` from the underlying rust object
114    ///
115    /// # Safety
116    ///
117    /// `inputs` and `outputs` must match the slots arguments.
118    pub unsafe fn invoke_slot(&self, slot_id: u32, inputs: &[*const u8], outputs: &[*mut u8]) {
119        unsafe { call_rust_trait_impl!(self, invoke_slot(slot_id, inputs, outputs)) }
120    }
121
122    // `&self` deliberately: mutability of the user object is interior (`RustObjAccess`)
123    /// Call the mutable slot with id `slot_id` from the underlying rust object
124    ///
125    /// # Safety
126    ///
127    /// `inputs` and `outputs` must match the slots arguments.
128    pub unsafe fn invoke_slot_mut(&self, slot_id: u32, inputs: &[*const u8], outputs: &[*mut u8]) {
129        unsafe {call_rust_trait_impl!(mut self, invoke_slot_mut(slot_id, inputs, outputs)) }
130    }
131
132    /// Return the property with id `prop_id` as a `QVariant` from the underlying rust object
133    ///
134    /// # Safety
135    ///
136    /// The returning QVariant might contain an untracked, raw `QObject*`. Calling this
137    /// function makes you responsible for tracking its lifetime.
138    pub unsafe fn read_property(&self, prop_id: u32) -> QVariant {
139        unsafe { call_rust_trait_impl!(self, read_property(prop_id)) }
140    }
141
142    // `&self` deliberately: mutability of the user object is interior (`RustObjAccess`)
143    /// Return the property with id `prop_id` as a `QVariant` from the underlying rust object
144    ///
145    /// # Safety
146    ///
147    /// If the QVariant contains an untracked, raw `QObject*`, it is your responsiblity
148    /// to guarantee its lifetime and validity.
149    pub unsafe fn write_property(&self, prop_id: u32, value: &QVariant) {
150        unsafe { call_rust_trait_impl!(mut self, write_property(prop_id, value)) }
151    }
152
153}
154
155#[macro_export]
156macro_rules! impl_qcpp_proxy {
157    ($cpp_proxy:ty, $rust_proxy:ty) => {
158        // SAFETY: The C++ proxies derive from `QObject`; size and alignment
159        // come from C++ `sizeof`/`alignof`.
160        unsafe impl QCppProxy for $cpp_proxy {
161            type ProxyRustType = $rust_proxy;
162            fn get_static_meta_object() -> &'static QMetaObject {
163                Self::static_qmeta_object()
164            }
165            fn get_size() -> usize {
166                Self::size_of()
167            }
168            fn get_align() -> usize {
169                Self::align_of()
170            }
171            fn parser_status_cast() -> i32 {
172                Self::parser_status_cast()
173            }
174            unsafe fn create(rust_proxy: *mut Self::ProxyRustType, metaobject: &'static DynamicMetaObjectData) -> *mut Self {
175                unsafe { Self::create(rust_proxy, metaobject) }
176            }
177            unsafe fn create_at(rust_proxy: *mut Self::ProxyRustType, metaobject: &'static DynamicMetaObjectData, addr: *mut u8) -> *mut Self {
178                unsafe { Self::create_at(rust_proxy, metaobject, addr) }
179            }
180            fn emit_signal(self: std::pin::Pin<&mut Self>, signal_name: &str, argv: &[*const u8]) {
181                self.emit_signal_cpp(signal_name, argv)
182            }
183        }
184    };
185}