Skip to content

Commit

Permalink
cxx-qt-gen: remove wrapper method for C++ -> Rust invokables
Browse files Browse the repository at this point in the history
This then avoids us needing to generate Rust methods with
fully qualified types on the Rust side and removes a load of
generation.

Related to #404
  • Loading branch information
ahayzen-kdab committed Jul 19, 2023
1 parent a9c1b4e commit 0805af7
Show file tree
Hide file tree
Showing 14 changed files with 164 additions and 236 deletions.
88 changes: 79 additions & 9 deletions crates/cxx-qt-gen/src/generator/cpp/invokable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,11 @@ pub fn generate_cpp_invokables(
.collect::<Result<Vec<CppNamedType>>>()?;

let body = format!(
"m_rustObj->{ident}({parameter_names})",
"{ident}({parameter_names})",
ident = idents.wrapper.cpp,
parameter_names = vec!["*this"]
.into_iter()
.chain(parameters.iter().map(|parameter| parameter.ident.as_str()))
parameter_names = parameters
.iter()
.map(|parameter| parameter.ident.as_str())
.collect::<Vec<&str>>()
.join(", "),
);
Expand Down Expand Up @@ -140,6 +140,23 @@ pub fn generate_cpp_invokables(
},
},
});

// Note that we are generating a header to match the extern "Rust" method
// in Rust for our invokable.
//
// CXX generates the source and we just need the matching header.
//
// TODO: will this always be noexcept ? If the return type is Result is this then removed?
generated.private_methods.push(CppFragment::Header(format!(
"{return_cxx_ty} {ident}({parameter_types}){is_const} noexcept;",
return_cxx_ty = if let Some(return_cxx_ty) = &return_cxx_ty {
return_cxx_ty.as_cxx_ty()
} else {
"void"
},
ident = idents.wrapper.cpp,
parameter_types = parameter_types,
)));
}

Ok(generated)
Expand Down Expand Up @@ -234,7 +251,7 @@ mod tests {
MyObject::voidInvokable() const
{
// ::std::lock_guard
m_rustObj->voidInvokableWrapper(*this);
voidInvokableWrapper();
}
"#}
);
Expand All @@ -255,7 +272,7 @@ mod tests {
MyObject::trivialInvokable(::std::int32_t param) const
{
// ::std::lock_guard
return m_rustObj->trivialInvokableWrapper(*this, param);
return trivialInvokableWrapper(param);
}
"#}
);
Expand All @@ -276,7 +293,7 @@ mod tests {
MyObject::opaqueInvokable(QColor const& param)
{
// ::std::lock_guard
return m_rustObj->opaqueInvokableWrapper(*this, param);
return opaqueInvokableWrapper(param);
}
"#}
);
Expand All @@ -297,10 +314,50 @@ mod tests {
MyObject::specifiersInvokable(::std::int32_t param) const
{
// ::std::lock_guard
return m_rustObj->specifiersInvokableWrapper(*this, param);
return specifiersInvokableWrapper(param);
}
"#}
);

// private methods
assert_eq!(generated.private_methods.len(), 4);

let header = if let CppFragment::Header(header) = &generated.private_methods[0] {
header
} else {
panic!("Expected header")
};
assert_str_eq!(header, "void voidInvokableWrapper() const noexcept;");

let header = if let CppFragment::Header(header) = &generated.private_methods[1] {
header
} else {
panic!("Expected header")
};
assert_str_eq!(
header,
"::std::int32_t trivialInvokableWrapper(::std::int32_t param) const noexcept;"
);

let header = if let CppFragment::Header(header) = &generated.private_methods[2] {
header
} else {
panic!("Expected header")
};
assert_str_eq!(
header,
"::std::unique_ptr<QColor> opaqueInvokableWrapper(QColor const& param) noexcept;"
);

let header = if let CppFragment::Header(header) = &generated.private_methods[3] {
header
} else {
panic!("Expected header")
};
assert_str_eq!(
header,
"::std::int32_t specifiersInvokableWrapper(::std::int32_t param) const noexcept;"
);
}

#[test]
Expand Down Expand Up @@ -350,9 +407,22 @@ mod tests {
MyObject::trivialInvokable(A1 param) const
{
// ::std::lock_guard
return m_rustObj->trivialInvokableWrapper(*this, param);
return trivialInvokableWrapper(param);
}
"#}
);

// private methods
assert_eq!(generated.private_methods.len(), 1);

let header = if let CppFragment::Header(header) = &generated.private_methods[0] {
header
} else {
panic!("Expected header")
};
assert_str_eq!(
header,
"B2 trivialInvokableWrapper(A1 param) const noexcept;"
);
}
}
106 changes: 22 additions & 84 deletions crates/cxx-qt-gen/src/generator/rust/invokable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,34 +12,29 @@ use crate::{
};
use proc_macro2::TokenStream;
use quote::quote;
use syn::{Ident, Result, ReturnType};
use syn::Result;

pub fn generate_rust_invokables(
invokables: &Vec<ParsedQInvokable>,
qobject_idents: &QObjectName,
) -> Result<GeneratedRustQObjectBlocks> {
let mut generated = GeneratedRustQObjectBlocks::default();
let cpp_class_name_rust = &qobject_idents.cpp_class.rust;
let rust_struct_name_rust = &qobject_idents.rust_struct.rust;

for invokable in invokables {
let idents = QInvokableName::from(invokable);
let wrapper_ident_cpp = idents.wrapper.cpp.to_string();
let wrapper_ident_rust = &idents.wrapper.rust;
let invokable_ident_rust = &idents.name.rust;

// TODO: once we aren't using qobject::T in the extern "RustQt"
// we can just pass through the original ExternFn block and add the attribute?
let cpp_struct = if invokable.mutable {
quote! { Pin<&mut #cpp_class_name_rust> }
quote! { Pin<&mut #cpp_class_name_rust> }
} else {
quote! { &#cpp_class_name_rust }
};
let rust_struct = if invokable.mutable {
quote! { &mut #rust_struct_name_rust }
} else {
quote! { &#rust_struct_name_rust }
};
let parameter_signatures = if invokable.parameters.is_empty() {
quote! { self: #rust_struct, cpp: #cpp_struct }
quote! { self: #cpp_struct }
} else {
let parameters = invokable
.parameters
Expand All @@ -50,46 +45,29 @@ pub fn generate_rust_invokables(
quote! { #ident: #ty }
})
.collect::<Vec<TokenStream>>();
quote! { self: #rust_struct, cpp: #cpp_struct, #(#parameters),* }
quote! { self: #cpp_struct, #(#parameters),* }
};
let return_type = &invokable.method.sig.output;
let has_return = if matches!(invokable.method.sig.output, ReturnType::Default) {
quote! {}
} else {
quote! { return }
};

let mut unsafe_block = None;
let mut unsafe_call = Some(quote! { unsafe });
if invokable.safe {
std::mem::swap(&mut unsafe_call, &mut unsafe_block);
}

let parameter_names = invokable
.parameters
.iter()
.map(|parameter| parameter.ident.clone())
.collect::<Vec<Ident>>();

let fragment = RustFragmentPair {
cxx_bridge: vec![quote! {
// TODO: is an unsafe block valid?
// Note: extern "Rust" block does not need to be unsafe
extern "Rust" {
// Note that we are exposing a Rust method on the C++ type to C++
//
// CXX ends up generating the source, then we generate the matching header.
#[doc(hidden)]
#[cxx_name = #wrapper_ident_cpp]
#unsafe_call fn #wrapper_ident_rust(#parameter_signatures) #return_type;
#unsafe_call fn #invokable_ident_rust(#parameter_signatures) #return_type;
}
}],
implementation: vec![
// TODO: not all methods have a wrapper
quote! {
impl #rust_struct_name_rust {
#[doc(hidden)]
pub #unsafe_call fn #wrapper_ident_rust(#parameter_signatures) #return_type {
#has_return cpp.#invokable_ident_rust(#(#parameter_names),*);
}
}
},
],
implementation: vec![],
};

generated
Expand Down Expand Up @@ -164,26 +142,16 @@ mod tests {
let generated = generate_rust_invokables(&invokables, &qobject_idents).unwrap();

assert_eq!(generated.cxx_mod_contents.len(), 4);
assert_eq!(generated.cxx_qt_mod_contents.len(), 4);
assert_eq!(generated.cxx_qt_mod_contents.len(), 0);

// void_invokable
assert_tokens_eq(
&generated.cxx_mod_contents[0],
quote! {
extern "Rust" {
#[cxx_name = "voidInvokableWrapper"]
fn void_invokable_wrapper(self: &MyObjectRust, cpp: &MyObject);
}
},
);
assert_tokens_eq(
&generated.cxx_qt_mod_contents[0],
quote! {
impl MyObjectRust {
#[doc(hidden)]
pub fn void_invokable_wrapper(self: &MyObjectRust, cpp: &MyObject) {
cpp.void_invokable();
}
#[cxx_name = "voidInvokableWrapper"]
fn void_invokable(self: &MyObject);
}
},
);
Expand All @@ -193,19 +161,9 @@ mod tests {
&generated.cxx_mod_contents[1],
quote! {
extern "Rust" {
#[cxx_name = "trivialInvokableWrapper"]
fn trivial_invokable_wrapper(self: &MyObjectRust, cpp: &MyObject, param: i32) -> i32;
}
},
);
assert_tokens_eq(
&generated.cxx_qt_mod_contents[1],
quote! {
impl MyObjectRust {
#[doc(hidden)]
pub fn trivial_invokable_wrapper(self: &MyObjectRust, cpp: &MyObject, param: i32) -> i32 {
return cpp.trivial_invokable(param);
}
#[cxx_name = "trivialInvokableWrapper"]
fn trivial_invokable(self: &MyObject, param: i32) -> i32;
}
},
);
Expand All @@ -215,19 +173,9 @@ mod tests {
&generated.cxx_mod_contents[2],
quote! {
extern "Rust" {
#[cxx_name = "opaqueInvokableWrapper"]
fn opaque_invokable_wrapper(self: &mut MyObjectRust, cpp: Pin<&mut MyObject>, param: &QColor) -> UniquePtr<QColor>;
}
},
);
assert_tokens_eq(
&generated.cxx_qt_mod_contents[2],
quote! {
impl MyObjectRust {
#[doc(hidden)]
pub fn opaque_invokable_wrapper(self: &mut MyObjectRust, cpp: Pin<&mut MyObject>, param: &QColor) -> UniquePtr<QColor> {
return cpp.opaque_invokable(param);
}
#[cxx_name = "opaqueInvokableWrapper"]
fn opaque_invokable(self: Pin<&mut MyObject>, param: &QColor) -> UniquePtr<QColor>;
}
},
);
Expand All @@ -237,19 +185,9 @@ mod tests {
&generated.cxx_mod_contents[3],
quote! {
extern "Rust" {
#[cxx_name = "unsafeInvokableWrapper"]
unsafe fn unsafe_invokable_wrapper(self: &MyObjectRust, cpp: &MyObject, param: *mut T) -> *mut T;
}
},
);
assert_tokens_eq(
&generated.cxx_qt_mod_contents[3],
quote! {
impl MyObjectRust {
#[doc(hidden)]
pub unsafe fn unsafe_invokable_wrapper(self: &MyObjectRust, cpp: &MyObject, param: *mut T) -> *mut T {
return cpp.unsafe_invokable(param);
}
#[cxx_name = "unsafeInvokableWrapper"]
unsafe fn unsafe_invokable(self:&MyObject, param: *mut T) -> *mut T;
}
},
);
Expand Down
4 changes: 2 additions & 2 deletions crates/cxx-qt-gen/test_outputs/inheritance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ QVariant
MyObject::data(QModelIndex const& _index, ::std::int32_t _role) const
{
const ::std::lock_guard<::std::recursive_mutex> guard(*m_rustObjMutex);
return m_rustObj->dataWrapper(*this, _index, _role);
return dataWrapper(_index, _role);
}

bool
MyObject::hasChildren(QModelIndex const& _parent) const
{
const ::std::lock_guard<::std::recursive_mutex> guard(*m_rustObjMutex);
return m_rustObj->hasChildrenWrapper(*this, _parent);
return hasChildrenWrapper(_parent);
}

MyObject::MyObject(QObject* parent)
Expand Down
5 changes: 5 additions & 0 deletions crates/cxx-qt-gen/test_outputs/inheritance.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ class MyObject : public QAbstractItemModel
}
explicit MyObject(QObject* parent = nullptr);

private:
QVariant dataWrapper(QModelIndex const& _index,
::std::int32_t _role) const noexcept;
bool hasChildrenWrapper(QModelIndex const& _parent) const noexcept;

private:
::rust::Box<MyObjectRust> m_rustObj;
::std::shared_ptr<::std::recursive_mutex> m_rustObjMutex;
Expand Down
Loading

0 comments on commit 0805af7

Please sign in to comment.