Skip to content

Commit 8fe9276

Browse files
committed
cxx-qt-gen: add support for multiple QObjects in writer/cpp
1 parent cffa211 commit 8fe9276

File tree

6 files changed

+641
-233
lines changed

6 files changed

+641
-233
lines changed

cxx-qt-gen/src/gen_cpp.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ use proc_macro2::TokenStream;
99
use syn::Ident;
1010

1111
use crate::extract::{Invokable, Parameter, ParameterType, Property, QObject, QtTypes, Signal};
12-
use crate::generator::cpp::{fragment::CppFragmentPair, GeneratedCppBlocks};
12+
use crate::generator::cpp::{
13+
fragment::CppFragmentPair, qobject::GeneratedCppQObjectBlocks, GeneratedCppBlocks,
14+
};
1315
use crate::generator::{naming, naming::property::QPropertyName};
1416
use crate::writer::cpp::write_cpp;
1517

@@ -605,13 +607,11 @@ pub fn generate_qobject_cpp(obj: &QObject) -> Result<CppObject, TokenStream> {
605607
let namespace_internals =
606608
naming::namespace::NamespaceName::from_pair_str(&obj.namespace, &obj.ident).internal;
607609

608-
// For now convert our gen_cpp code into the GeneratedCppBlocks struct
609-
let generated = GeneratedCppBlocks {
610-
cxx_stem: naming::module::cxx_stem_from_ident(&obj.ident).to_string(),
610+
// For now we only create a single QObject
611+
let qobjects = vec![GeneratedCppQObjectBlocks {
611612
ident: obj.ident.to_string(),
612613
rust_ident: rust_struct_ident,
613614
cxx_qt_thread_ident: qobject_idents.cxx_qt_thread_class.to_string(),
614-
namespace: obj.namespace.clone(),
615615
namespace_internals,
616616
base_class: obj
617617
.base_class
@@ -621,6 +621,13 @@ pub fn generate_qobject_cpp(obj: &QObject) -> Result<CppObject, TokenStream> {
621621
methods,
622622
slots,
623623
signals,
624+
}];
625+
626+
// For now convert our gen_cpp code into the GeneratedCppBlocks struct
627+
let generated = GeneratedCppBlocks {
628+
cxx_stem: naming::module::cxx_stem_from_ident(&obj.ident).to_string(),
629+
namespace: obj.namespace.clone(),
630+
qobjects,
624631
};
625632

626633
// Use our writer phase to convert to a string

cxx-qt-gen/src/generator/cpp/mod.rs

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,14 @@
44
// SPDX-License-Identifier: MIT OR Apache-2.0
55

66
pub mod fragment;
7+
pub mod qobject;
78

8-
use fragment::CppFragmentPair;
9-
10-
/// Representation of the generated C++ code for a QObject
9+
/// Representation of the generated C++ code for a group of QObjects
1110
pub struct GeneratedCppBlocks {
1211
/// Stem of the CXX header to include
1312
pub cxx_stem: String,
14-
/// Ident of the C++ QObject
15-
pub ident: String,
16-
/// Ident of the Rust object
17-
pub rust_ident: String,
18-
/// Ident of the CxxQtThread object
19-
pub cxx_qt_thread_ident: String,
20-
/// Ident of the namespace of the QObject
13+
/// Ident of the common namespace of the QObjects
2114
pub namespace: String,
22-
/// Ident of the namespace for CXX-Qt internals of the QObject
23-
pub namespace_internals: String,
24-
/// Base class of the QObject
25-
pub base_class: String,
26-
/// List of Qt Meta Object items (eg Q_PROPERTY)
27-
pub metaobjects: Vec<String>,
28-
/// List of public methods for the QObject
29-
pub methods: Vec<CppFragmentPair>,
30-
/// List of public Q_SLOTS for the QObject
31-
pub slots: Vec<CppFragmentPair>,
32-
/// List of public Q_SIGNALS for the QObject
33-
pub signals: Vec<String>,
15+
/// Generated QObject blocks
16+
pub qobjects: Vec<qobject::GeneratedCppQObjectBlocks>,
3417
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// SPDX-FileCopyrightText: 2022 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]>
2+
// SPDX-FileContributor: Andrew Hayzen <[email protected]>
3+
//
4+
// SPDX-License-Identifier: MIT OR Apache-2.0
5+
6+
use crate::generator::cpp::fragment::CppFragmentPair;
7+
8+
pub struct GeneratedCppQObjectBlocks {
9+
/// Ident of the C++ QObject
10+
pub ident: String,
11+
/// Ident of the Rust object
12+
pub rust_ident: String,
13+
/// Ident of the CxxQtThread object
14+
pub cxx_qt_thread_ident: String,
15+
/// Ident of the namespace for CXX-Qt internals of the QObject
16+
pub namespace_internals: String,
17+
/// Base class of the QObject
18+
pub base_class: String,
19+
/// List of Qt Meta Object items (eg Q_PROPERTY)
20+
pub metaobjects: Vec<String>,
21+
/// List of public methods for the QObject
22+
pub methods: Vec<CppFragmentPair>,
23+
/// List of public Q_SLOTS for the QObject
24+
pub slots: Vec<CppFragmentPair>,
25+
/// List of public Q_SIGNALS for the QObject
26+
pub signals: Vec<String>,
27+
}

cxx-qt-gen/src/writer/cpp/header.rs

Lines changed: 95 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
// SPDX-License-Identifier: MIT OR Apache-2.0
55

66
use crate::generator::cpp::{fragment::CppFragmentPair, GeneratedCppBlocks};
7+
use crate::writer::cpp::namespace_pair;
78
use indoc::formatdoc;
89

910
/// Extract the header from a given CppFragmentPair
@@ -33,6 +34,87 @@ fn create_block(block: &str, items: &[&str]) -> String {
3334
}
3435
}
3536

37+
/// For a given GeneratedCppBlocks write the forward declare
38+
fn forward_declare(generated: &GeneratedCppBlocks) -> Vec<String> {
39+
let (namespace_start, namespace_end) = namespace_pair(generated);
40+
41+
generated
42+
.qobjects
43+
.iter()
44+
.map(|qobject| {
45+
formatdoc! { r#"
46+
{namespace_start}
47+
class {ident};
48+
using {cxx_qt_thread_ident} = rust::cxxqtlib1::CxxQtThread<{ident}>;
49+
{namespace_end}
50+
"#,
51+
ident = qobject.ident,
52+
cxx_qt_thread_ident = qobject.cxx_qt_thread_ident,
53+
namespace_start = namespace_start,
54+
namespace_end = namespace_end,
55+
}
56+
})
57+
.collect::<Vec<String>>()
58+
}
59+
60+
/// For a given GeneratedCppBlocks write the classes
61+
fn qobjects_header(generated: &GeneratedCppBlocks) -> Vec<String> {
62+
let (namespace_start, namespace_end) = namespace_pair(generated);
63+
64+
generated.qobjects.iter().map(|qobject| {
65+
formatdoc! { r#"
66+
{namespace_start}
67+
class {ident} : public {base_class}
68+
{{
69+
Q_OBJECT
70+
{metaobjects}
71+
72+
public:
73+
explicit {ident}(QObject* parent = nullptr);
74+
~{ident}();
75+
const {rust_ident}& unsafeRust() const;
76+
{rust_ident}& unsafeRustMut();
77+
std::unique_ptr<{cxx_qt_thread_ident}> qtThread() const;
78+
79+
{methods}
80+
{slots}
81+
{signals}
82+
private:
83+
rust::Box<{rust_ident}> m_rustObj;
84+
std::shared_ptr<std::mutex> m_rustObjMutex;
85+
std::shared_ptr<rust::cxxqtlib1::CxxQtGuardedPointer<{ident}>> m_cxxQtThreadObj;
86+
}};
87+
88+
static_assert(std::is_base_of<QObject, {ident}>::value, "{ident} must inherit from QObject");
89+
{namespace_end}
90+
91+
namespace {namespace_internals} {{
92+
std::unique_ptr<{ident}>
93+
newCppObject();
94+
}} // namespace {namespace_internals}
95+
96+
Q_DECLARE_METATYPE({metatype}*)
97+
"#,
98+
ident = qobject.ident,
99+
cxx_qt_thread_ident = qobject.cxx_qt_thread_ident,
100+
namespace_start = namespace_start,
101+
namespace_end = namespace_end,
102+
namespace_internals = qobject.namespace_internals,
103+
rust_ident = qobject.rust_ident,
104+
base_class = qobject.base_class,
105+
metaobjects = qobject.metaobjects.join("\n "),
106+
methods = create_block("public", &qobject.methods.iter().map(pair_as_header).collect::<Vec<&str>>()),
107+
slots = create_block("public Q_SLOTS", &qobject.slots.iter().map(pair_as_header).collect::<Vec<&str>>()),
108+
signals = create_block("Q_SIGNALS", &qobject.signals.iter().map(AsRef::as_ref).collect::<Vec<&str>>()),
109+
metatype = if generated.namespace.is_empty() {
110+
qobject.ident.clone()
111+
} else {
112+
format!("{namespace}::{ident}", namespace = generated.namespace, ident = qobject.ident)
113+
},
114+
}
115+
}).collect::<Vec<String>>()
116+
}
117+
36118
/// For a given GeneratedCppBlocks write this into a C++ header
37119
pub fn write_cpp_header(generated: &GeneratedCppBlocks) -> String {
38120
formatdoc! {r#"
@@ -46,70 +128,14 @@ pub fn write_cpp_header(generated: &GeneratedCppBlocks) -> String {
46128
class CxxQtThread;
47129
}}
48130
49-
{namespace_start}
50-
class {ident};
51-
using {cxx_qt_thread_ident} = rust::cxxqtlib1::CxxQtThread<{ident}>;
52-
{namespace_end}
53-
131+
{forward_declare}
54132
#include "cxx-qt-gen/include/{cxx_stem}.cxx.h"
55133
56-
{namespace_start}
57-
class {ident} : public {base_class}
58-
{{
59-
Q_OBJECT
60-
{metaobjects}
61-
62-
public:
63-
explicit {ident}(QObject* parent = nullptr);
64-
~{ident}();
65-
const {rust_ident}& unsafeRust() const;
66-
{rust_ident}& unsafeRustMut();
67-
std::unique_ptr<{cxx_qt_thread_ident}> qtThread() const;
68-
69-
{methods}
70-
{slots}
71-
{signals}
72-
private:
73-
rust::Box<{rust_ident}> m_rustObj;
74-
std::shared_ptr<std::mutex> m_rustObjMutex;
75-
std::shared_ptr<rust::cxxqtlib1::CxxQtGuardedPointer<{ident}>> m_cxxQtThreadObj;
76-
}};
77-
78-
static_assert(std::is_base_of<QObject, {ident}>::value, "{ident} must inherit from QObject");
79-
{namespace_end}
80-
81-
namespace {namespace_internals} {{
82-
std::unique_ptr<{ident}>
83-
newCppObject();
84-
}} // namespace {namespace_internals}
85-
86-
Q_DECLARE_METATYPE({metatype}*)
134+
{qobjects}
87135
"#,
88136
cxx_stem = generated.cxx_stem,
89-
ident = generated.ident,
90-
cxx_qt_thread_ident = generated.cxx_qt_thread_ident,
91-
namespace_start = if generated.namespace.is_empty() {
92-
"".to_owned()
93-
} else {
94-
format!("namespace {namespace} {{", namespace = generated.namespace)
95-
},
96-
namespace_end = if generated.namespace.is_empty() {
97-
"".to_owned()
98-
} else {
99-
format!("}} // namespace {namespace}", namespace = generated.namespace)
100-
},
101-
namespace_internals = generated.namespace_internals,
102-
rust_ident = generated.rust_ident,
103-
base_class = generated.base_class,
104-
metaobjects = generated.metaobjects.join("\n "),
105-
methods = create_block("public", &generated.methods.iter().map(pair_as_header).collect::<Vec<&str>>()),
106-
slots = create_block("public Q_SLOTS", &generated.slots.iter().map(pair_as_header).collect::<Vec<&str>>()),
107-
signals = create_block("Q_SIGNALS", &generated.signals.iter().map(AsRef::as_ref).collect::<Vec<&str>>()),
108-
metatype = if generated.namespace.is_empty() {
109-
generated.ident.clone()
110-
} else {
111-
format!("{namespace}::{ident}", namespace = generated.namespace, ident = generated.ident)
112-
},
137+
forward_declare = forward_declare(generated).join("\n"),
138+
qobjects = qobjects_header(generated).join("\n"),
113139
}
114140
}
115141

@@ -118,7 +144,8 @@ mod tests {
118144
use super::*;
119145

120146
use crate::writer::cpp::tests::{
121-
create_generated_cpp, create_generated_cpp_no_namespace, expected_header,
147+
create_generated_cpp, create_generated_cpp_multi_qobjects,
148+
create_generated_cpp_no_namespace, expected_header, expected_header_multi_qobjects,
122149
expected_header_no_namespace,
123150
};
124151
use indoc::indoc;
@@ -153,6 +180,13 @@ mod tests {
153180
assert_str_eq!(output, expected_header());
154181
}
155182

183+
#[test]
184+
fn test_write_cpp_header_multi_qobjects() {
185+
let generated = create_generated_cpp_multi_qobjects();
186+
let output = write_cpp_header(&generated);
187+
assert_str_eq!(output, expected_header_multi_qobjects());
188+
}
189+
156190
#[test]
157191
fn test_write_cpp_header_no_namespace() {
158192
let generated = create_generated_cpp_no_namespace();

0 commit comments

Comments
 (0)