Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
ahayzen-kdab committed Sep 21, 2023
1 parent f079478 commit f95de78
Show file tree
Hide file tree
Showing 21 changed files with 2,132 additions and 327 deletions.
10 changes: 8 additions & 2 deletions crates/cxx-qt-gen/src/generator/cpp/externcxxqt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,15 @@ use syn::Result;
pub struct GeneratedCppExternCxxQtBlocks {
/// List of includes
pub includes: BTreeSet<String>,
/// List of forward declares before the class and include of the generated CXX header
pub forward_declares: Vec<String>,
/// List of methods
pub method: CppFragment,
/// Namespace of the method block
pub methods: Vec<CppFragment>,
/// List of source which is outside of the namespace
///
/// TODO: should these just be String as they are only source?
pub sources: Vec<CppFragment>,
/// Namespace of the method blocks
pub namespace: String,
}

Expand Down
5 changes: 5 additions & 0 deletions crates/cxx-qt-gen/src/generator/cpp/qobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ pub struct GeneratedCppQObjectBlocks {
pub metaobjects: Vec<String>,
/// List of public methods for the QObject
pub methods: Vec<CppFragment>,
/// List of source which is outside of the QObject namespace
///
/// TODO: should these just be String as they are only source?
pub sources: Vec<CppFragment>,
/// List of private methods for the QObject
pub private_methods: Vec<CppFragment>,
/// List of includes
Expand All @@ -36,6 +40,7 @@ impl GeneratedCppQObjectBlocks {
self.forward_declares.append(&mut other.forward_declares);
self.metaobjects.append(&mut other.metaobjects);
self.methods.append(&mut other.methods);
self.sources.append(&mut other.sources);
self.private_methods.append(&mut other.private_methods);
self.includes.append(&mut other.includes);
self.base_classes.append(&mut other.base_classes);
Expand Down
230 changes: 166 additions & 64 deletions crates/cxx-qt-gen/src/generator/cpp/signal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,45 +22,51 @@ use syn::Result;

/// Combined output of possible parameter lines to be used
struct Parameters {
types_closure: String,
types_signal: String,
values_closure: String,
}

/// Representation of the self pair
///
/// This allows for *this being passed or a parameter in the arguments
struct SelfValue<'a> {
ident: &'a str,
ty: &'a str,
/// name with type of parameters
named_types: String,
/// name with type of parameters including self
named_types_with_self: String,
/// Raw types of the parameters including self
types_with_self: String,
/// Raw ::std::move values of the parameters including self
values_with_self: String,
values_with_self_as_this: String,
}

/// From given parameters, mappings, and self value constructor the combined parameter lines
fn parameter_types_and_values(
parameters: &[ParsedFunctionParameter],
cxx_mappings: &ParsedCxxMappings,
self_value: SelfValue,
self_ty: &str,
) -> Result<Parameters> {
let mut parameter_types_closure = vec![];
let mut parameter_values_closure = vec![];
let mut parameter_named_types_with_self = vec![];
let mut parameter_types_with_self = vec![];
let mut parameter_values_with_self = vec![];

for parameter in parameters {
let cxx_ty = syn_type_to_cpp_type(&parameter.ty, cxx_mappings)?;
let ident_str = parameter.ident.to_string();
parameter_types_closure.push(format!("{cxx_ty} {ident_str}",));
parameter_values_closure.push(format!("::std::move({ident_str})"));
parameter_named_types_with_self.push(format!("{cxx_ty} {ident_str}",));
parameter_types_with_self.push(cxx_ty.clone());
parameter_values_with_self.push(format!("::std::move({ident_str})"));
}

let parameters_types_signal = parameter_types_closure.join(", ");
let parameter_named_types = parameter_named_types_with_self.join(", ");
let mut parameter_values_with_self_as_this = parameter_values_with_self.clone();

// Insert the extra argument into the closure
parameter_types_closure.insert(0, format!("{ty}&", ty = self_value.ty));
parameter_values_closure.insert(0, self_value.ident.to_owned());
let self_ty = cxx_mappings.cxx(self_ty);
parameter_named_types_with_self.insert(0, format!("{self_ty}& self"));
parameter_types_with_self.insert(0, format!("{self_ty}&"));
parameter_values_with_self.insert(0, "self".to_owned());
parameter_values_with_self_as_this.insert(0, "*this".to_owned());

Ok(Parameters {
types_closure: parameter_types_closure.join(", "),
types_signal: parameters_types_signal,
values_closure: parameter_values_closure.join(", "),
named_types: parameter_named_types,
named_types_with_self: parameter_named_types_with_self.join(", "),
types_with_self: parameter_types_with_self.join(", "),
values_with_self: parameter_values_with_self.join(", "),
values_with_self_as_this: parameter_values_with_self_as_this.join(", "),
})
}

Expand All @@ -81,50 +87,100 @@ pub fn generate_cpp_free_signal(
let connect_ident = idents.connect_name.cpp.to_string();

// Retrieve the parameters for the signal
let parameters = parameter_types_and_values(
&signal.parameters,
cxx_mappings,
SelfValue {
ident: "self",
ty: &qobject_ident_namespaced,
},
)?;
let parameters_types_closure = parameters.types_closure;
let parameters_types_signal = parameters.types_signal;
let parameters_values_closure = parameters.values_closure;
let parameters =
parameter_types_and_values(&signal.parameters, cxx_mappings, &qobject_ident_namespaced)?;
let parameters_named_types = parameters.named_types;
let parameters_named_types_with_self = parameters.named_types_with_self;
let parameter_types_with_self = parameters.types_with_self;
let parameter_values_with_self = parameters.values_with_self;

// Build a namespace that includes any namespace for the T
generated.namespace = namespace_externcxxqt_with_qobject_namespace(
cxx_mappings
.namespaces
.get(&signal.qobject_ident.to_string()),
);

generated.method = CppFragment::Pair {
// TODO: put in naming
// let namespace =
// if let Some(namespace) = cxx_mappings.namespace(&signal.qobject_ident.to_string()) {
// format!("::{namespace}")
// } else {
// "".to_owned()
// };
let namespace = {
if !generated.namespace.is_empty() {
format!("::{namespace}", namespace = generated.namespace)
} else {
"".to_owned()
}
};
let param_struct = format!("{qobject_ident}CxxQtSignalParams{signal_ident}");
let signal_handler_impl = format!("SignalHandler<{namespace}::{param_struct} *>");
let signal_handler_alias = format!("{qobject_ident}CxxQtSignalHandler{signal_ident}");
let signal_handler_drop = format!("drop_{qobject_ident}_signal_handler_{signal_ident}");
let signal_handler_call = format!("call_{qobject_ident}_signal_handler_{signal_ident}");
let signal_handler_alias_namespaced = format!("{namespace}::{signal_handler_alias}");

generated.forward_declares.push(format!(
"using {signal_handler_alias} = ::rust::cxxqtlib1::SignalHandler<struct {param_struct} *>;"
));

generated.sources.push(CppFragment::Source(formatdoc! {
r#"
// Define namespace otherwise we hit a GCC bug
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480
namespace rust {{
namespace cxxqtlib1 {{
template <>
{signal_handler_impl}::~SignalHandler()
{{
if (data[0] == nullptr && data[1] == nullptr)
{{
return;
}}
{signal_handler_drop}(::std::move(*this));
}}
template <>
template <>
void {signal_handler_impl}::operator()<{parameter_types_with_self}>({parameters_named_types_with_self})
{{
{signal_handler_call}(*this, {parameter_values_with_self});
}}
}} // cxxqtlib1
}} // rust
"#
}));

generated.methods.push(CppFragment::Pair {
header: formatdoc!(
r#"
::QMetaObject::Connection
{qobject_ident}_{connect_ident}({qobject_ident_namespaced}& self, ::rust::Fn<void({parameters_types_closure})> func, ::Qt::ConnectionType type);
{qobject_ident}_{connect_ident}({qobject_ident_namespaced}& self, {signal_handler_alias_namespaced} closure, ::Qt::ConnectionType type);
"#,
),
source: formatdoc! {
r#"
::QMetaObject::Connection
{qobject_ident}_{connect_ident}({qobject_ident_namespaced}& self, ::rust::Fn<void({parameters_types_closure})> func, ::Qt::ConnectionType type)
{qobject_ident}_{connect_ident}({qobject_ident_namespaced}& self, {signal_handler_alias_namespaced} closure, ::Qt::ConnectionType type)
{{
return ::QObject::connect(
&self,
&{qobject_ident_namespaced}::{signal_ident},
&self,
[&, func = ::std::move(func)]({parameters_types_signal}) {{
[&, closure = ::std::move(closure)]({parameters_named_types}) mutable {{
const ::rust::cxxqtlib1::MaybeLockGuard<{qobject_ident_namespaced}> guard(self);
func({parameters_values_closure});
closure.template operator()<{parameter_types_with_self}>({parameter_values_with_self});
}},
type);
}}
"#,
},
};

// Build a namespace that includes any namespace for the T
generated.namespace = namespace_externcxxqt_with_qobject_namespace(
cxx_mappings
.namespaces
.get(&signal.qobject_ident.to_string()),
);
});

generated
.includes
Expand Down Expand Up @@ -154,40 +210,84 @@ pub fn generate_cpp_signals(
let connect_ident = idents.connect_name.cpp.to_string();

// Generate the parameters
let parameters = parameter_types_and_values(
&signal.parameters,
cxx_mappings,
SelfValue {
ident: "*this",
ty: &qobject_ident,
},
)?;
let parameters_types_closure = parameters.types_closure;
let parameters_types_signal = parameters.types_signal;
let parameters_values_closure = parameters.values_closure;
let parameters =
parameter_types_and_values(&signal.parameters, cxx_mappings, &qobject_ident)?;
let parameters_named_types = parameters.named_types;
let parameters_named_types_with_self = parameters.named_types_with_self;
let parameter_types_with_self = parameters.types_with_self;
let parameter_values_with_self = parameters.values_with_self;
let parameter_values_with_self_as_this = parameters.values_with_self_as_this;

// TODO: put in naming
let namespace =
if let Some(namespace) = cxx_mappings.namespace(&signal.qobject_ident.to_string()) {
format!("::{namespace}")
} else {
"".to_owned()
};
let param_struct = format!("{qobject_ident}CxxQtSignalParams{signal_ident}");
let signal_handler_impl = format!("SignalHandler<{namespace}::{param_struct} *>");
let signal_handler_alias = format!("{qobject_ident}CxxQtSignalHandler{signal_ident}");
let signal_handler_drop = format!("drop_{qobject_ident}_signal_handler_{signal_ident}");
let signal_handler_call = format!("call_{qobject_ident}_signal_handler_{signal_ident}");
let signal_handler_alias_namespaced = format!("{namespace}::{signal_handler_alias}");

generated.forward_declares.push(format!(
"using {signal_handler_alias} = ::rust::cxxqtlib1::SignalHandler<struct {param_struct} *>;"
));

// Generate the Q_SIGNAL if this is not an existing signal
if !signal.inherit {
generated.methods.push(CppFragment::Header(format!(
"Q_SIGNAL void {signal_ident}({parameters_types_signal});"
"Q_SIGNAL void {signal_ident}({parameters_named_types});"
)));
}

generated.sources.push(CppFragment::Source(formatdoc! {
r#"
// Define namespace otherwise we hit a GCC bug
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480
namespace rust {{
namespace cxxqtlib1 {{
template <>
{signal_handler_impl}::~SignalHandler()
{{
if (data[0] == nullptr && data[1] == nullptr)
{{
return;
}}
{signal_handler_drop}(::std::move(*this));
}}
template <>
template <>
void {signal_handler_impl}::operator()<{parameter_types_with_self}>({parameters_named_types_with_self})
{{
{signal_handler_call}(*this, {parameter_values_with_self});
}}
}} // cxxqtlib1
}} // rust
"#
}));

generated.methods.push(CppFragment::Pair {
header: format!(
"::QMetaObject::Connection {connect_ident}(::rust::Fn<void({parameters_types_closure})> func, ::Qt::ConnectionType type);",
"::QMetaObject::Connection {connect_ident}({signal_handler_alias_namespaced} closure, ::Qt::ConnectionType type);",
),
source: formatdoc! {
r#"
::QMetaObject::Connection
{qobject_ident}::{connect_ident}(::rust::Fn<void({parameters_types_closure})> func, ::Qt::ConnectionType type)
{qobject_ident}::{connect_ident}({signal_handler_alias_namespaced} closure, ::Qt::ConnectionType type)
{{
return ::QObject::connect(this,
&{qobject_ident}::{signal_ident},
this,
[&, func = ::std::move(func)]({parameters_types_signal}) {{
[&, closure = ::std::move(closure)]({parameters_named_types}) mutable {{
const ::rust::cxxqtlib1::MaybeLockGuard<{qobject_ident}> guard(*this);
func({parameters_values_closure});
closure.template operator()<{parameter_types_with_self}>({parameter_values_with_self_as_this});
}},
type);
}}
Expand Down Expand Up @@ -415,7 +515,8 @@ mod tests {

let generated = generate_cpp_free_signal(&signal, &ParsedCxxMappings::default()).unwrap();

let (header, source) = if let CppFragment::Pair { header, source } = &generated.method {
assert_eq!(generated.methods.len(), 1);
let (header, source) = if let CppFragment::Pair { header, source } = &generated.methods[0] {
(header, source)
} else {
panic!("Expected Pair")
Expand Down Expand Up @@ -478,7 +579,8 @@ mod tests {

let generated = generate_cpp_free_signal(&signal, &cxx_mappings).unwrap();

let (header, source) = if let CppFragment::Pair { header, source } = &generated.method {
assert_eq!(generated.methods.len(), 1);
let (header, source) = if let CppFragment::Pair { header, source } = &generated.methods[0] {
(header, source)
} else {
panic!("Expected Pair")
Expand Down
Loading

0 comments on commit f95de78

Please sign in to comment.