Skip to main content

qtbridge_runtime/
qproxies.rs

1// Copyright (C) 2026 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only
3
4
5use qtbridge_type_lib::QMetaObject;
6use crate::DispatchMetaCall;
7use crate::DynamicMetaObjectData;
8use std::rc::Rc;
9use std::cell::RefCell;
10use std::pin::Pin;
11
12
13/// Address of engine-allocated memory in which a C++ proxy is
14/// placement-constructed. QML-created elements pass `Some`; Rust-created
15/// objects pass `None` and allocate on the C++ heap.
16pub type PlacementAddress = *mut u8;
17
18/// `QCppProxy` defines what a C++ proxy to a QObject (C++) must implement.
19///
20/// This includes access to the C++ static meta-object, which is then extended
21/// on the Rust side to create a dynamic meta-object.
22///
23/// # Safety
24///
25/// `Self` must be a `QObject` subclass, so that a `*mut Self` can be used as
26/// a `*mut QObject`. [`get_size`](QCppProxy::get_size) and
27/// [`get_align`](QCppProxy::get_align) must return the size and alignment of
28/// `Self`: the QML engine allocates the storage for
29/// [`create_at`](QCppProxy::create_at) from them.
30pub unsafe trait QCppProxy {
31    type ProxyRustType: QRustProxy;
32    fn get_static_meta_object() -> &'static QMetaObject;
33    fn get_size() -> usize;
34    fn get_align() -> usize;
35    fn parser_status_cast() -> i32;
36    /// # Safety
37    ///
38    /// rust_proxy must be a valid and live pointer to Self::ProxyRustType.
39    /// The QCppProxy must be droped first and will drop rust_proxy in its
40    /// destructor. Ensure that rust_proxy is not droped by anything but QCppProxy.
41    unsafe fn create(rust_proxy: *mut Self::ProxyRustType, metaobject: &'static DynamicMetaObjectData) -> *mut Self;
42    /// # Safety
43    ///
44    /// Same contract as [`QCppProxy::create`].
45    unsafe fn create_at(rust_proxy: *mut Self::ProxyRustType, metaobject: &'static DynamicMetaObjectData, addr: PlacementAddress) -> *mut Self;
46    fn emit_signal(self: Pin<&mut Self>, signal_name: &str, argv: &[*const u8]);
47}
48
49/// `QRustProxy` defines the Rust-side bridge object that binds:
50///
51/// - A Rust object stored in `Rc<RefCell<dyn _>>`
52/// - A corresponding C++ QObject-based proxy
53///
54/// Implementations of this trait are the concrete glue layer between
55/// Rust and Qt, usually using Cxx.
56///
57/// # Purpose
58///
59/// A `QRustProxy` implementation:
60///
61/// - Stores a raw pointer to the C++ proxy (`cpp_proxy`)
62/// - Stores access to the Rust object through `RustObjAccess<dyn _>` (`rust_obj`)
63/// - Coordinates destruction, layout and Qt meta-object information
64/// - Forwards all foreign function calls to the C++ proxy
65///
66/// Typical structure:
67///
68/// ```rust, ignore
69/// pub struct QObjectProxyRust {
70///     cpp_proxy: *mut QObjectProxyCpp,
71///     rust_obj: RustObjAccess<dyn QObjectProxyGet>,
72///     on_drop: fn(rust_obj: *const u8),
73/// }
74/// ```
75///
76/// Where:
77///
78/// - `cpp_proxy` points to the actual C++ QObject subclass.
79/// - `rust_obj` wraps access to the users rust object.
80/// - `on_drop` cleaning up memory.
81///
82/// # Lifetime and Ownership
83///
84/// A `QRustProxy` instance is heap-allocated and owned through a raw pointer, because its
85/// lifetime is jointly managed with C++ code and the C++ side requires a stable, non-movable
86/// address.
87///
88/// Destruction is always initiated from the C++ side: the paired `CppProxy` destructor calls
89/// `GenericRustProxy::drop_self`, which converts the raw pointer back to a `Box`, invokes
90/// the stored `on_drop` callback, and then drops this instance along with its reference to the
91/// Rust object.
92///
93/// The proxy always holds a strong `Rc` to the user's Rust object: the
94/// object lives exactly as long as its proxy pair, plus any user handles.
95///
96/// # Associated Types
97///
98/// ## `ProxyCppType`
99///
100/// The concrete C++ proxy type. Has to implement [`QCppProxy`].
101///
102/// ## `AdapterType`
103///
104/// A wrapper trait for the interface trait that QtBridge users implement. This wrapper
105/// is required because not all traits can be used with dyn and are thus incompatible with
106/// `RustObjAccess` (See "object safety" or "dyn compatibility").
107///
108/// # Safety
109///
110/// [`new`](QRustProxy::new) must return a valid pointer to a live proxy, and
111/// [`get_cpp_proxy`](QRustProxy::get_cpp_proxy) and
112/// [`get_cpp_proxy_mut`](QRustProxy::get_cpp_proxy_mut) must return the
113/// paired C++ proxy, valid for as long as `self` lives.
114/// [`get_rust_object_rc`](QRustProxy::get_rust_object_rc) must return the
115/// allocation passed to [`new`](QRustProxy::new): callers reinterpret it as
116/// the concrete type.
117pub unsafe trait QRustProxy {
118    type ProxyCppType: QCppProxy<ProxyRustType = Self>;
119    type AdapterType: DispatchMetaCall + ?Sized;
120
121    /// Creates a new instance of this struct on the heap and returns a raw pointer to it.
122    ///
123    /// Initializes the proxy pair by:
124    /// - Creating a `RustObjAccess` wrapper holding a strong reference to `rust_obj`.
125    /// - Constructing the paired C++ proxy: with placement new at `at_address`
126    ///   if given (QML-created elements), on the heap otherwise.
127    /// - Storing `on_drop` for invocation when the C++ proxy is eventually destroyed.
128    fn new(rust_obj: &Rc<RefCell<Self::AdapterType>>, metaobject: &'static DynamicMetaObjectData, at_address: Option<PlacementAddress>, on_drop: Box<dyn FnOnce() + 'static>) -> *mut Self;
129    fn get_cpp_proxy(&self) -> *const Self::ProxyCppType;
130    fn get_cpp_proxy_mut(&self) -> *mut Self::ProxyCppType;
131    fn emit_signal(&self, mut_ref: &mut Self::AdapterType, signal_name: &str, argv: &[*const u8]);
132    fn with_rust_ref<R, F: FnOnce(&Self::AdapterType) -> R>(&self, f: F) -> R;
133    fn with_rust_ref_mut<R, F: FnOnce(&mut Self::AdapterType) -> R>(&self, f: F) -> R;
134
135    /// Returns an owned handle to the Rust object behind this proxy, or `None`
136    /// if it has already been dropped (the proxy may outlive a weakly-held
137    /// object). The handle is typed as the adapter trait object; recovering the
138    /// concrete type requires a checked reinterpret (see
139    /// `QObjectHolder::qobject_to_rc_ref_cell`).
140    fn get_rust_object_rc(&self) -> Rc<RefCell<Self::AdapterType>>;
141}
142
143/// Coerces a concrete user type into the proxy's type-erased adapter handle.
144///
145/// Implemented once per interface.
146///
147/// # Safety
148///
149/// [`upcast`](AdapterUpcast::upcast) must return the allocation it was
150/// passed (an unsizing coercion): callers reinterpret the adapter handle
151/// back as `Rc<RefCell<T>>`.
152pub unsafe trait AdapterUpcast<T>: QRustProxy {
153    fn upcast(rc: std::rc::Rc<std::cell::RefCell<T>>) -> std::rc::Rc<std::cell::RefCell<Self::AdapterType>>;
154}