Skip to main content

qtbridge_runtime/
qmetatypeforqobject.rs

1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only
3
4use std::rc::Rc;
5use std::cell::RefCell;
6use std::any::TypeId;
7use std::collections::HashMap;
8use qtbridge_type_lib::{QMetaTypeInterface, QMetaTypeFlag, QMetaObject, QObject};
9use qtbridge_type_lib::core::{qmetatypeinterface, qobject};
10use crate::qproxies::QCppProxy;
11use crate::QObjectHolder;
12use crate::qobjectholder::CppProxyOf;
13use crate::registry::Owner;
14
15fn monomorphize_meta_object_fn<T: QObjectHolder>() -> extern "C" fn(*const QMetaTypeInterface) -> *mut QMetaObject {
16    extern "C" fn meta_object_fn<T: QObjectHolder>(_iface: *const QMetaTypeInterface) -> *mut QMetaObject {
17        let meta_obj_data =
18        <T as QObjectHolder>::get_shared_dynamic_meta_object_data();
19        meta_obj_data.get_meta_object()
20    }
21    meta_object_fn::<T>
22}
23
24fn monomorphize_default_ctor<T: QObjectHolder + Default>() -> extern "C" fn(*const QMetaTypeInterface, *mut u8) {
25    extern "C" fn default_ctor<T: QObjectHolder + Default>(_iface: *const QMetaTypeInterface, addr: *mut u8) {
26        let instance =
27        Rc::new(RefCell::new(<T as Default>::default()));
28        unsafe { <T as QObjectHolder>::register_instance_at(instance, Owner::Engine, Some(addr)) };
29    }
30    default_ctor::<T>
31}
32
33extern "C" fn dtor(_iface: *const QMetaTypeInterface, obj: *mut u8) {
34    unsafe { qobject::destruct(obj.cast()) };
35}
36
37/// Builds the [`QMetaTypeInterface`] describing `T`.
38pub fn init_interface_for<T: QObjectHolder + Default>()-> QMetaTypeInterface {
39    let flags: u32 =
40        (QMetaTypeFlag::NeedsConstruction as u32)
41        | (QMetaTypeFlag::NeedsDestruction as u32)
42        | (QMetaTypeFlag::NeedsCopyConstruction as u32)
43        | (QMetaTypeFlag::NeedsMoveConstruction as u32)
44        | (QMetaTypeFlag::PointerToQObject as u32);
45
46    qmetatypeinterface::fill_fields(
47        <CppProxyOf<T> as QCppProxy>::get_align(),
48        <CppProxyOf<T> as QCppProxy>::get_size(),
49        flags,
50        std::any::type_name::<T>(),
51        monomorphize_meta_object_fn::<T>() as usize,
52        monomorphize_default_ctor::<T>() as usize,
53        0,
54        dtor as *const () as usize,
55    )
56}
57
58/// Builds the [`QMetaTypeInterface`] describing `T*` - a pointer to the
59/// QObject-derived type `T`.
60pub fn init_ptr_interface_for<T: QObjectHolder>() -> QMetaTypeInterface {
61    let flags: u32 =
62        (QMetaTypeFlag::IsPointer as u32)
63        | (QMetaTypeFlag::PointerToQObject as u32)
64        | (QMetaTypeFlag::RelocatableType as u32);
65
66    qmetatypeinterface::fill_fields(
67        std::mem::align_of::<*mut QObject>(),
68        std::mem::size_of::<*mut QObject>(),
69        flags,
70        &format!("{}*", std::any::type_name::<T>()),
71        monomorphize_meta_object_fn::<T>() as usize,
72        0,
73        0,
74        0,
75    )
76}
77
78/// HashMap cached [`QMetaTypeInterface`], used for generic QObject types
79/// where a per-instantiation `OnceLock` is not available.
80pub fn interface_for_generic<T: QObjectHolder + Default>() -> &'static QMetaTypeInterface {
81    thread_local!(static IFACE_MAP: RefCell<HashMap<TypeId , *const QMetaTypeInterface>> = RefCell::new(HashMap::new ()));
82    let type_id = TypeId::of::<T>();
83    {
84        let iface_ptr = IFACE_MAP
85            .with_borrow(|iface_map| iface_map.get(&type_id).copied().unwrap_or_default());
86        if let Some(iface_ref) = unsafe { iface_ptr.as_ref() } {
87            return iface_ref;
88        }
89    }
90    let iface_ref = Box::leak(Box::new(init_interface_for::<T>()));
91    let iface_ptr = std::ptr::from_ref(iface_ref);
92    IFACE_MAP.with_borrow_mut(|iface_map| iface_map.insert(type_id, iface_ptr));
93    iface_ref
94}
95
96/// Pointer-metatype counterpart of [`interface_for_generic`], used for generic
97/// QObject types where a per-instantiation `OnceLock` is not available.
98pub fn ptr_interface_for_generic<T: QObjectHolder>() -> &'static QMetaTypeInterface {
99    thread_local!(static PTR_IFACE_MAP: RefCell<HashMap<TypeId, *const QMetaTypeInterface>> = RefCell::new(HashMap::new()));
100    let type_id = TypeId::of::<T>();
101    {
102        let iface_ptr = PTR_IFACE_MAP
103            .with_borrow(|iface_map| iface_map.get(&type_id).copied().unwrap_or_default());
104        if let Some(iface_ref) = unsafe { iface_ptr.as_ref() } {
105            return iface_ref;
106        }
107    }
108    let iface_ref = Box::leak(Box::new(init_ptr_interface_for::<T>()));
109    let iface_ptr = std::ptr::from_ref(iface_ref);
110    PTR_IFACE_MAP.with_borrow_mut(|iface_map| iface_map.insert(type_id, iface_ptr));
111    iface_ref
112}