Skip to content

Commit 96e0770

Browse files
committed
cxx-qt-gen: add support for multiple QObjects in writer/cpp
1 parent 44f3cfe commit 96e0770

File tree

6 files changed

+687
-251
lines changed

6 files changed

+687
-251
lines changed

cxx-qt-gen/src/gen_cpp.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ use proc_macro2::TokenStream;
1010
use syn::Ident;
1111

1212
use crate::extract::{Invokable, Parameter, ParameterType, Property, QObject, QtTypes, Signal};
13-
use crate::generator::cpp::{fragment::CppFragmentPair, GeneratedCppBlocks};
13+
use crate::generator::cpp::{
14+
fragment::CppFragmentPair, qobject::GeneratedCppQObjectBlocks, GeneratedCppBlocks,
15+
};
1416
use crate::writer::cpp::write_cpp;
1517

1618
/// A trait which we implement on QtTypes allowing retrieval of attributes of the enum value.
@@ -634,13 +636,11 @@ pub fn generate_qobject_cpp(obj: &QObject) -> Result<CppObject, TokenStream> {
634636
// TODO: later this will be shared with gen_rs in the generator phase
635637
let cxx_qt_thread_ident = format!("{}CxxQtThread", struct_ident_str);
636638

637-
// For now convert our gen_cpp code into the GeneratedCppBlocks struct
638-
let generated = GeneratedCppBlocks {
639-
cxx_stem: struct_ident_str.to_case(Case::Snake),
640-
ident: struct_ident_str,
639+
// For now we only create a single QObject
640+
let qobjects = vec![GeneratedCppQObjectBlocks {
641+
ident: struct_ident_str.clone(),
641642
rust_ident: rust_struct_ident,
642643
cxx_qt_thread_ident,
643-
namespace: obj.namespace.clone(),
644644
namespace_internals: namespace_internals.join("::"),
645645
base_class: obj
646646
.base_class
@@ -651,6 +651,13 @@ pub fn generate_qobject_cpp(obj: &QObject) -> Result<CppObject, TokenStream> {
651651
slots,
652652
signals,
653653
members,
654+
}];
655+
656+
// For now convert our gen_cpp code into the GeneratedCppBlocks struct
657+
let generated = GeneratedCppBlocks {
658+
cxx_stem: struct_ident_str.to_case(Case::Snake),
659+
namespace: obj.namespace.clone(),
660+
qobjects,
654661
};
655662

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

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

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,33 +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>,
34-
/// List of private members for the QObject
35-
pub members: Vec<String>,
15+
/// Generated QObject blocks
16+
pub qobjects: Vec<qobject::GeneratedCppQObjectBlocks>,
3617
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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+
/// List of private members for the QObject
28+
pub members: Vec<String>,
29+
}

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

Lines changed: 99 additions & 65 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,91 @@ 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+
bool m_initialised = false;
86+
std::shared_ptr<rust::cxxqtlib1::CxxQtGuardedPointer<{ident}>> m_cxxQtThreadObj;
87+
88+
{members}
89+
}};
90+
91+
static_assert(std::is_base_of<QObject, {ident}>::value, "{ident} must inherit from QObject");
92+
{namespace_end}
93+
94+
namespace {namespace_internals} {{
95+
std::unique_ptr<{ident}>
96+
newCppObject();
97+
}} // namespace {namespace_internals}
98+
99+
Q_DECLARE_METATYPE({metatype}*)
100+
"#,
101+
ident = qobject.ident,
102+
cxx_qt_thread_ident = qobject.cxx_qt_thread_ident,
103+
namespace_start = namespace_start,
104+
namespace_end = namespace_end,
105+
namespace_internals = qobject.namespace_internals,
106+
rust_ident = qobject.rust_ident,
107+
base_class = qobject.base_class,
108+
metaobjects = qobject.metaobjects.join("\n "),
109+
methods = create_block("public", &qobject.methods.iter().map(pair_as_header).collect::<Vec<&str>>()),
110+
slots = create_block("public Q_SLOTS", &qobject.slots.iter().map(pair_as_header).collect::<Vec<&str>>()),
111+
signals = create_block("Q_SIGNALS", &qobject.signals.iter().map(AsRef::as_ref).collect::<Vec<&str>>()),
112+
members = qobject.members.join("\n "),
113+
metatype = if generated.namespace.is_empty() {
114+
qobject.ident.clone()
115+
} else {
116+
format!("{namespace}::{ident}", namespace = generated.namespace, ident = qobject.ident)
117+
},
118+
}
119+
}).collect::<Vec<String>>()
120+
}
121+
36122
/// For a given GeneratedCppBlocks write this into a C++ header
37123
pub fn write_cpp_header(generated: &GeneratedCppBlocks) -> String {
38124
formatdoc! {r#"
@@ -46,74 +132,14 @@ pub fn write_cpp_header(generated: &GeneratedCppBlocks) -> String {
46132
class CxxQtThread;
47133
}}
48134
49-
{namespace_start}
50-
class {ident};
51-
using {cxx_qt_thread_ident} = rust::cxxqtlib1::CxxQtThread<{ident}>;
52-
{namespace_end}
53-
135+
{forward_declare}
54136
#include "cxx-qt-gen/include/{cxx_stem}.cxx.h"
55137
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-
bool m_initialised = false;
76-
std::shared_ptr<rust::cxxqtlib1::CxxQtGuardedPointer<{ident}>> m_cxxQtThreadObj;
77-
78-
{members}
79-
}};
80-
81-
static_assert(std::is_base_of<QObject, {ident}>::value, "{ident} must inherit from QObject");
82-
{namespace_end}
83-
84-
namespace {namespace_internals} {{
85-
std::unique_ptr<{ident}>
86-
newCppObject();
87-
}} // namespace {namespace_internals}
88-
89-
Q_DECLARE_METATYPE({metatype}*)
138+
{qobjects}
90139
"#,
91140
cxx_stem = generated.cxx_stem,
92-
ident = generated.ident,
93-
cxx_qt_thread_ident = generated.cxx_qt_thread_ident,
94-
namespace_start = if generated.namespace.is_empty() {
95-
"".to_owned()
96-
} else {
97-
format!("namespace {namespace} {{", namespace = generated.namespace)
98-
},
99-
namespace_end = if generated.namespace.is_empty() {
100-
"".to_owned()
101-
} else {
102-
format!("}} // namespace {namespace}", namespace = generated.namespace)
103-
},
104-
namespace_internals = generated.namespace_internals,
105-
rust_ident = generated.rust_ident,
106-
base_class = generated.base_class,
107-
metaobjects = generated.metaobjects.join("\n "),
108-
methods = create_block("public", &generated.methods.iter().map(pair_as_header).collect::<Vec<&str>>()),
109-
slots = create_block("public Q_SLOTS", &generated.slots.iter().map(pair_as_header).collect::<Vec<&str>>()),
110-
signals = create_block("Q_SIGNALS", &generated.signals.iter().map(AsRef::as_ref).collect::<Vec<&str>>()),
111-
members = generated.members.join("\n "),
112-
metatype = if generated.namespace.is_empty() {
113-
generated.ident.clone()
114-
} else {
115-
format!("{namespace}::{ident}", namespace = generated.namespace, ident = generated.ident)
116-
},
141+
forward_declare = forward_declare(generated).join("\n"),
142+
qobjects = qobjects_header(generated).join("\n"),
117143
}
118144
}
119145

@@ -122,7 +148,8 @@ mod tests {
122148
use super::*;
123149

124150
use crate::writer::cpp::tests::{
125-
create_generated_cpp, create_generated_cpp_no_namespace, expected_header,
151+
create_generated_cpp, create_generated_cpp_multi_qobjects,
152+
create_generated_cpp_no_namespace, expected_header, expected_header_multi_qobjects,
126153
expected_header_no_namespace,
127154
};
128155
use indoc::indoc;
@@ -157,6 +184,13 @@ mod tests {
157184
assert_str_eq!(output, expected_header());
158185
}
159186

187+
#[test]
188+
fn test_write_cpp_header_multi_qobjects() {
189+
let generated = create_generated_cpp_multi_qobjects();
190+
let output = write_cpp_header(&generated);
191+
assert_str_eq!(output, expected_header_multi_qobjects());
192+
}
193+
160194
#[test]
161195
fn test_write_cpp_header_no_namespace() {
162196
let generated = create_generated_cpp_no_namespace();

0 commit comments

Comments
 (0)