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