Skip to content

Commit 752d0f2

Browse files
Improve manual implementations of up- and downcasting (#1204)
* cxx-qt: use qobject_cast for downcastPtr * cxx-qt-lib: implement Upcast<QVector<QPoint>> for QPolygon * cxx-qt-lib: implement Upcast<QVector<QPointF>> for QPolygonF * cxx-qt-lib: implement Deref for QQmlApplicationEngine * cxx-qt-lib: remove null-pointer dereference from QStringList's downcast * cxx-qt-lib: implement Upcast<QCoreApplication> for QGuiApplication * cxx-qt-lib: implement Upcast<QGuiApplication> for QApplication * Revert "cxx-qt-lib: implement Upcast<QGuiApplication> for QApplication" This reverts commit 311ddd9. * Revert "cxx-qt-lib: implement Upcast<QCoreApplication> for QGuiApplication" This reverts commit 8e829ef. * Revert "cxx-qt-lib: implement Deref for QQmlApplicationEngine" This reverts commit b939a8d. * cxx-qt: use qobject_cast for downcastPtr * cxx-qt-lib: implement Upcast<QVector<QPoint>> for QPolygon * cxx-qt-lib: implement Upcast<QVector<QPointF>> for QPolygonF * cxx-qt-lib: remove null-pointer dereference from QStringList's downcast * Improve downcast C++ implementations Add detection whether to use qobject_cast or dynamic_cast via template specialization. Also add additional checks that a static_cast downcast is sane to do (i.e. at least the size of the types must be the same). --------- Co-authored-by: Leon Matthes <[email protected]>
1 parent fb83a89 commit 752d0f2

File tree

9 files changed

+103
-105
lines changed

9 files changed

+103
-105
lines changed

crates/cxx-qt-lib/include/gui/qpolygon.h

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,3 @@ struct IsRelocatable<QPolygon> : ::std::true_type
2121
{};
2222

2323
} // namespace rust
24-
25-
namespace rust {
26-
namespace cxxqtlib1 {
27-
28-
const QVector<QPoint>&
29-
qpolygonAsQVectorQPointRef(const QPolygon& shape);
30-
QVector<QPoint>&
31-
qpolygonAsQVectorQPointRef(QPolygon& shape);
32-
33-
}
34-
}

crates/cxx-qt-lib/include/gui/qpolygonf.h

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,3 @@ struct IsRelocatable<QPolygonF> : ::std::true_type
2121
{};
2222

2323
} // namespace rust
24-
25-
namespace rust {
26-
namespace cxxqtlib1 {
27-
28-
const QVector<QPointF>&
29-
qpolygonfAsQVectorQPointFRef(const QPolygonF& shape);
30-
QVector<QPointF>&
31-
qpolygonfAsQVectorQPointFRef(QPolygonF& shape);
32-
33-
}
34-
}

crates/cxx-qt-lib/src/core/qstringlist.rs

Lines changed: 16 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// SPDX-FileContributor: Andrew Hayzen <[email protected]>
33
//
44
// SPDX-License-Identifier: MIT OR Apache-2.0
5-
use crate::core::qstringlist::ffi::QList_QString;
65
use crate::{QList, QString};
76
use core::mem::MaybeUninit;
87
use cxx::{type_id, ExternType};
@@ -28,21 +27,6 @@ mod ffi {
2827
include!("cxx-qt-lib/qstringlist.h");
2928
type QStringList = super::QStringList;
3029

31-
include!("cxx-qt/casting.h");
32-
33-
#[doc(hidden)]
34-
#[rust_name = "upcast_qstringlist"]
35-
#[cxx_name = "upcastPtr"]
36-
#[namespace = "rust::cxxqt1"]
37-
unsafe fn upcast(thiz: *const QStringList) -> *const QList_QString;
38-
39-
#[doc(hidden)]
40-
#[rust_name = "downcast_qlist_qstring"]
41-
#[cxx_name = "downcastPtr"]
42-
#[namespace = "rust::cxxqt1"]
43-
#[cfg(cxxqt_qt_version_at_least_6)]
44-
unsafe fn downcast(base: *const QList_QString) -> *const QStringList;
45-
4630
/// Returns true if the list contains the string str; otherwise returns false.
4731
fn contains(self: &QStringList, str: &QString, cs: CaseSensitivity) -> bool;
4832

@@ -67,6 +51,19 @@ mod ffi {
6751
) -> &mut QStringList;
6852
}
6953

54+
#[namespace = "rust::cxxqt1"]
55+
unsafe extern "C++" {
56+
include!("cxx-qt/casting.h");
57+
58+
#[doc(hidden)]
59+
#[rust_name = "upcast_qstringlist"]
60+
unsafe fn upcastPtr(thiz: *const QStringList) -> *const QList_QString;
61+
62+
#[doc(hidden)]
63+
#[rust_name = "downcast_qlist_qstring"]
64+
unsafe fn downcastPtrStatic(base: *const QList_QString) -> *const QStringList;
65+
}
66+
7067
#[namespace = "rust::cxxqtlib1"]
7168
unsafe extern "C++" {
7269
include!("cxx-qt-lib/common.h");
@@ -207,20 +204,14 @@ impl DerefMut for QStringList {
207204
}
208205
}
209206

210-
impl Upcast<QList_QString> for QStringList {
211-
unsafe fn upcast_ptr(this: *const Self) -> *const QList_QString {
207+
impl Upcast<QList<QString>> for QStringList {
208+
unsafe fn upcast_ptr(this: *const Self) -> *const QList<QString> {
212209
ffi::upcast_qstringlist(this)
213210
}
214211

215-
#[cfg(cxxqt_qt_version_at_least_6)]
216-
unsafe fn from_base_ptr(base: *const QList_QString) -> *const Self {
212+
unsafe fn from_base_ptr(base: *const QList<QString>) -> *const Self {
217213
ffi::downcast_qlist_qstring(base)
218214
}
219-
220-
#[cfg(cxxqt_qt_version_major = "5")]
221-
unsafe fn from_base_ptr(_base: *const QList_QString) -> *const Self {
222-
std::ptr::null()
223-
}
224215
}
225216

226217
// Safety:

crates/cxx-qt-lib/src/gui/qpolygon.cpp

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -32,20 +32,3 @@ static_assert(!::std::is_trivially_copy_constructible<QPolygon>::value);
3232
static_assert(!::std::is_trivially_destructible<QPolygon>::value);
3333

3434
static_assert(QTypeInfo<QPolygon>::isRelocatable);
35-
36-
namespace rust {
37-
namespace cxxqtlib1 {
38-
const QVector<QPoint>&
39-
qpolygonAsQVectorQPointRef(const QPolygon& shape)
40-
{
41-
return static_cast<const QVector<QPoint>&>(shape);
42-
}
43-
44-
QVector<QPoint>&
45-
qpolygonAsQVectorQPointRef(QPolygon& shape)
46-
{
47-
return static_cast<QVector<QPoint>&>(shape);
48-
}
49-
50-
}
51-
}

crates/cxx-qt-lib/src/gui/qpolygon.rs

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use crate::{QPoint, QRect, QVector};
66
use core::mem::MaybeUninit;
77
use cxx::{type_id, ExternType};
8+
use cxx_qt::Upcast;
89
use std::fmt;
910
use std::ops::{Deref, DerefMut};
1011

@@ -74,6 +75,19 @@ mod ffi {
7475
fn united(self: &QPolygon, r: &QPolygon) -> QPolygon;
7576
}
7677

78+
#[namespace = "rust::cxxqt1"]
79+
unsafe extern "C++" {
80+
include!("cxx-qt/casting.h");
81+
82+
#[doc(hidden)]
83+
#[rust_name = "upcast_qpolygon"]
84+
unsafe fn upcastPtr(thiz: *const QPolygon) -> *const QVector_QPoint;
85+
86+
#[doc(hidden)]
87+
#[rust_name = "downcast_qvector_qpoint"]
88+
unsafe fn downcastPtrStatic(base: *const QVector_QPoint) -> *const QPolygon;
89+
}
90+
7791
#[namespace = "rust::cxxqtlib1"]
7892
unsafe extern "C++" {
7993
include!("cxx-qt-lib/common.h");
@@ -102,15 +116,6 @@ mod ffi {
102116
#[rust_name = "qpolygon_to_debug_qstring"]
103117
fn toDebugQString(value: &QPolygon) -> QString;
104118
}
105-
106-
#[namespace = "rust::cxxqtlib1"]
107-
unsafe extern "C++" {
108-
#[doc(hidden)]
109-
#[rust_name = "qpolygon_as_qvector_qpoint_ref"]
110-
fn qpolygonAsQVectorQPointRef(shape: &QPolygon) -> &QVector_QPoint;
111-
#[rust_name = "qpolygon_as_qvector_qpoint_ref_mut"]
112-
fn qpolygonAsQVectorQPointRef(shape: &mut QPolygon) -> &mut QVector_QPoint;
113-
}
114119
}
115120

116121
/// The QPolygon class provides a list of QPoint.
@@ -178,13 +183,23 @@ impl Deref for QPolygon {
178183
type Target = QVector<QPoint>;
179184

180185
fn deref(&self) -> &Self::Target {
181-
ffi::qpolygon_as_qvector_qpoint_ref(self)
186+
self.upcast()
182187
}
183188
}
184189

185190
impl DerefMut for QPolygon {
186191
fn deref_mut(&mut self) -> &mut Self::Target {
187-
ffi::qpolygon_as_qvector_qpoint_ref_mut(self)
192+
self.upcast_mut()
193+
}
194+
}
195+
196+
impl Upcast<QVector<QPoint>> for QPolygon {
197+
unsafe fn upcast_ptr(this: *const Self) -> *const QVector<QPoint> {
198+
ffi::upcast_qpolygon(this)
199+
}
200+
201+
unsafe fn from_base_ptr(base: *const QVector<QPoint>) -> *const Self {
202+
ffi::downcast_qvector_qpoint(base)
188203
}
189204
}
190205

crates/cxx-qt-lib/src/gui/qpolygonf.cpp

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -32,20 +32,3 @@ static_assert(!::std::is_trivially_copy_constructible<QPolygonF>::value);
3232
static_assert(!::std::is_trivially_destructible<QPolygonF>::value);
3333

3434
static_assert(QTypeInfo<QPolygonF>::isRelocatable);
35-
36-
namespace rust {
37-
namespace cxxqtlib1 {
38-
const QVector<QPointF>&
39-
qpolygonfAsQVectorQPointFRef(const QPolygonF& shape)
40-
{
41-
return static_cast<const QVector<QPointF>&>(shape);
42-
}
43-
44-
QVector<QPointF>&
45-
qpolygonfAsQVectorQPointFRef(QPolygonF& shape)
46-
{
47-
return static_cast<QVector<QPointF>&>(shape);
48-
}
49-
50-
}
51-
}

crates/cxx-qt-lib/src/gui/qpolygonf.rs

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
// SPDX-License-Identifier: MIT OR Apache-2.0
55
use core::mem::MaybeUninit;
66
use cxx::{type_id, ExternType};
7+
use cxx_qt::Upcast;
78
use std::fmt;
89
use std::ops::{Deref, DerefMut};
910

@@ -69,6 +70,19 @@ mod ffi {
6970
fn united(self: &QPolygonF, r: &QPolygonF) -> QPolygonF;
7071
}
7172

73+
#[namespace = "rust::cxxqt1"]
74+
unsafe extern "C++" {
75+
include!("cxx-qt/casting.h");
76+
77+
#[doc(hidden)]
78+
#[rust_name = "upcast_qpolygonf"]
79+
unsafe fn upcastPtr(thiz: *const QPolygonF) -> *const QVector_QPointF;
80+
81+
#[doc(hidden)]
82+
#[rust_name = "downcast_qvector_qpointf"]
83+
unsafe fn downcastPtrStatic(base: *const QVector_QPointF) -> *const QPolygonF;
84+
}
85+
7286
#[namespace = "rust::cxxqtlib1"]
7387
unsafe extern "C++" {
7488
include!("cxx-qt-lib/common.h");
@@ -93,15 +107,6 @@ mod ffi {
93107
#[rust_name = "qpolygonf_to_debug_qstring"]
94108
fn toDebugQString(value: &QPolygonF) -> QString;
95109
}
96-
97-
#[namespace = "rust::cxxqtlib1"]
98-
unsafe extern "C++" {
99-
#[doc(hidden)]
100-
#[rust_name = "qpolygonf_as_qvector_qpointf_ref"]
101-
fn qpolygonfAsQVectorQPointFRef(shape: &QPolygonF) -> &QVector_QPointF;
102-
#[rust_name = "qpolygonf_as_qvector_qpointf_ref_mut"]
103-
fn qpolygonfAsQVectorQPointFRef(shape: &mut QPolygonF) -> &mut QVector_QPointF;
104-
}
105110
}
106111

107112
/// The QPolygonF class provides a list of QPointF.
@@ -160,13 +165,23 @@ impl Deref for QPolygonF {
160165
type Target = QVector<QPointF>;
161166

162167
fn deref(&self) -> &Self::Target {
163-
ffi::qpolygonf_as_qvector_qpointf_ref(self)
168+
self.upcast()
164169
}
165170
}
166171

167172
impl DerefMut for QPolygonF {
168173
fn deref_mut(&mut self) -> &mut Self::Target {
169-
ffi::qpolygonf_as_qvector_qpointf_ref_mut(self)
174+
self.upcast_mut()
175+
}
176+
}
177+
178+
impl Upcast<QVector<QPointF>> for QPolygonF {
179+
unsafe fn upcast_ptr(this: *const Self) -> *const QVector<QPointF> {
180+
ffi::upcast_qpolygonf(this)
181+
}
182+
183+
unsafe fn from_base_ptr(base: *const QVector<QPointF>) -> *const Self {
184+
ffi::downcast_qvector_qpointf(base)
170185
}
171186
}
172187

crates/cxx-qt/include/casting.h

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
//
66
// SPDX-License-Identifier: MIT OR Apache-2.0
77
#pragma once
8+
#include <QtCore/QObject>
89
#include <type_traits>
910

1011
namespace rust::cxxqt1 {
@@ -17,12 +18,44 @@ upcastPtr(const Sub* sub)
1718
return static_cast<const Base*>(sub);
1819
}
1920

20-
template<typename Sub, typename Base>
21+
// Main downcasting function, for non-QObject types
22+
template<typename Sub,
23+
typename Base,
24+
std::enable_if_t<!std::is_base_of_v<QObject, Sub>, bool> = true>
2125
const Sub*
2226
downcastPtr(const Base* base)
2327
{
2428
static_assert(std::is_base_of_v<Base, Sub>);
29+
static_assert(std::is_polymorphic_v<Sub>,
30+
"Downcasting requires a polymorphic type (e.g. a type with at "
31+
"least one virtual method)!");
2532
return dynamic_cast<const Sub*>(base);
2633
}
2734

35+
// Downcasting function for QObject types, enabled via SFINAE
36+
template<typename Sub,
37+
typename Base,
38+
std::enable_if_t<std::is_base_of_v<QObject, Sub>, bool> = true>
39+
const Sub*
40+
downcastPtr(const Base* base)
41+
{
42+
static_assert(std::is_base_of_v<Base, Sub>);
43+
return qobject_cast<const Sub*>(base);
44+
}
45+
46+
// Warning: This function is highly unsafe, use with caution!
47+
// It is only safe to use if you are 100% sure that the Sub and Base types are
48+
// of the same size and that all Base instances are also valid Sub instances!
49+
//
50+
// This is mostly the case for types like QStringList, that are just an
51+
// extension of their parent types and don't add any data members.
52+
template<typename Sub, typename Base>
53+
const Sub*
54+
downcastPtrStatic(const Base* base)
55+
{
56+
static_assert(std::is_base_of_v<Base, Sub>);
57+
static_assert(sizeof(Base) == sizeof(Sub));
58+
return static_cast<const Sub*>(base);
59+
}
60+
2861
}

crates/cxx-qt/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ pub trait Upcast<T> {
236236
/// Automatically available for types in RustQt blocks in [cxx_qt::bridge](bridge)s.
237237
/// Downcasts a pointer to base class `T` to a pointer to `Self`.
238238
/// Return a null pointer if `Self` is not actually a child of base.
239-
/// > Note: Internal implementation uses `dynamic_cast`.
239+
/// > Note: Internal implementation uses `qobject_cast`.
240240
unsafe fn from_base_ptr(base: *const T) -> *const Self;
241241

242242
/// Upcast a reference to self to a reference to the base class

0 commit comments

Comments
 (0)