Skip to content

Commit

Permalink
Add a function to upcast UniquePtr
Browse files Browse the repository at this point in the history
Upcasting can change the value of a pointer in C++. This means anything
upcasting needs both the parent and child class defined, which is
awkward in user code when wrapping functions that accept ownership of a
superclass object (it requires C++ code that depends on
autocxx-generated C++ code that depends on the main C++ code, which then
needs to be depended on by another iteration of autocxx to call it from
Rust).

Ideally this would be a static function in C++ to avoid some potential
name conflicts that this approach produces (with C++ types sharing a
name in separate namespaces), but I don't think cxx supports those yet
(dtolnay/cxx#1036 and dtolnay/cxx#447).
  • Loading branch information
bsilver8192 committed May 22, 2022
1 parent f9b24b9 commit 7bf667b
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 1 deletion.
9 changes: 8 additions & 1 deletion engine/src/conversion/codegen_cpp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,13 @@ impl<'a> CppCodeGenerator<'a> {
"{}& As_{}_mut() {{ return *this; }}",
super_name, super_name
));
self.additional_functions.push(ExtraCpp {
declaration: Some(format!(
"inline std::unique_ptr<{}> {}_As_{}_UniquePtr(std::unique_ptr<{}> u) {{ return std::unique_ptr<{}>(u.release()); }}",
superclass.to_cpp_name(), subclass.cpp(), super_name, subclass.cpp(), superclass.to_cpp_name(),
)),
..Default::default()
});
// And now constructors
let mut constructor_decls: Vec<String> = Vec::new();
for constructor in constructors {
Expand All @@ -700,7 +707,7 @@ impl<'a> CppCodeGenerator<'a> {
}
self.additional_functions.push(ExtraCpp {
type_definition: Some(format!(
"class {} : {}\n{{\npublic:\n{}\n{}\nvoid {}() const;\nprivate:rust::Box<{}> obs;\nvoid really_remove_ownership();\n\n}};",
"class {} : public {}\n{{\npublic:\n{}\n{}\nvoid {}() const;\nprivate:rust::Box<{}> obs;\nvoid really_remove_ownership();\n\n}};",
subclass.cpp(),
superclass.to_cpp_name(),
constructor_decls.join("\n"),
Expand Down
12 changes: 12 additions & 0 deletions engine/src/conversion/codegen_rs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -723,6 +723,10 @@ impl<'a> RsCodeGenerator<'a> {
extern_c_mod_items.push(parse_quote! {
fn #as_mut_id(self: Pin<&mut #cpp_id>) -> Pin<&mut #super_cxxxbridge_id>;
});
let as_unique_ptr_id = make_ident(format!("{}_As_{}_UniquePtr", cpp_id, super_name));
extern_c_mod_items.push(parse_quote! {
fn #as_unique_ptr_id(u: UniquePtr<#cpp_id>) -> UniquePtr<#super_cxxxbridge_id>;
});
bindgen_mod_items.push(parse_quote! {
impl AsRef<#super_path> for super::super::super::#id {
fn as_ref(&self) -> &cxxbridge::#super_cxxxbridge_id {
Expand All @@ -740,6 +744,14 @@ impl<'a> RsCodeGenerator<'a> {
}
}
});
let rs_as_unique_ptr_id = make_ident(format!("as_{}_unique_ptr", super_name));
bindgen_mod_items.push(parse_quote! {
impl super::super::super::#id {
pub fn #rs_as_unique_ptr_id(u: cxx::UniquePtr<#cpp_id>) -> cxx::UniquePtr<cxxbridge::#super_cxxxbridge_id> {
cxxbridge::#as_unique_ptr_id(u)
}
}
});
let remove_ownership = sub.remove_ownership();
global_items.push(parse_quote! {
#[allow(non_snake_case)]
Expand Down
68 changes: 68 additions & 0 deletions integration-tests/tests/integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8278,6 +8278,74 @@ fn test_pv_subclass_calls() {
);
}

#[test]
fn test_pv_subclass_as_superclass() {
let hdr = indoc! {"
#include <cstdint>
#include <memory>
class TestObserver {
public:
TestObserver() {}
virtual void a() const = 0;
virtual ~TestObserver() {}
};
inline void call_observer(std::unique_ptr<TestObserver> obs) { obs->a(); }
"};
run_test_ex(
"",
hdr,
quote! {
use autocxx::subclass::CppSubclass;
let obs = MyTestObserver::new_cpp_owned(
MyTestObserver::default()
);
let obs = MyTestObserver::as_TestObserver_unique_ptr(obs);
assert!(!Lazy::force(&STATUS).lock().unwrap().dropped);
ffi::call_observer(obs);
assert!(Lazy::force(&STATUS).lock().unwrap().sub_a_called);
assert!(Lazy::force(&STATUS).lock().unwrap().dropped);
*Lazy::force(&STATUS).lock().unwrap() = Default::default();
},
quote! {
generate!("call_observer")
subclass!("TestObserver",MyTestObserver)
},
None,
None,
Some(quote! {
use once_cell::sync::Lazy;
use std::sync::Mutex;

use ffi::TestObserver_methods;
#[autocxx::subclass::subclass]
#[derive(Default)]
pub struct MyTestObserver {
}
impl TestObserver_methods for MyTestObserver {
fn a(&self) {
assert!(!Lazy::force(&STATUS).lock().unwrap().dropped);
Lazy::force(&STATUS).lock().unwrap().sub_a_called = true;
}
}
impl Drop for MyTestObserver {
fn drop(&mut self) {
Lazy::force(&STATUS).lock().unwrap().dropped = true;
}
}

#[derive(Default)]
struct Status {
sub_a_called: bool,
dropped: bool,
}

static STATUS: Lazy<Mutex<Status>> = Lazy::new(|| Mutex::new(Status::default()));
}),
);
}

#[test]
fn test_cycle_nonpod_simple() {
let hdr = indoc! {"
Expand Down

0 comments on commit 7bf667b

Please sign in to comment.