diff --git a/engine/src/conversion/analysis/fun/mod.rs b/engine/src/conversion/analysis/fun/mod.rs index 359c6a2fa..91e7443aa 100644 --- a/engine/src/conversion/analysis/fun/mod.rs +++ b/engine/src/conversion/analysis/fun/mod.rs @@ -606,6 +606,7 @@ impl<'a> FnAnalyzer<'a> { TypeConversionSophistication::SimpleForSubclasses, Some(analysis.rust_name.clone()), ); + for sub in self.subclasses_by_superclass(sup) { // For each subclass, we need to create a plain-C++ method to call its superclass // and a Rust/C++ bridge API to call _that_. @@ -676,7 +677,7 @@ impl<'a> FnAnalyzer<'a> { } } } - + results.push(Api::Function { fun, analysis, @@ -1279,7 +1280,7 @@ impl<'a> FnAnalyzer<'a> { // C++ API and we need to create a C++ wrapper function which is more cxx-compliant. // That wrapper function is included in the cxx::bridge, and calls through to the // original function. - let wrapper_function_needed = match kind { + let mut wrapper_function_needed = match kind { FnKind::Method { method_kind: MethodKind::Static @@ -1303,6 +1304,9 @@ impl<'a> FnAnalyzer<'a> { _ if self.force_wrapper_generation => true, _ => false, }; + if param_details.iter_mut().filter(|p| p.has_lifetime).count() != 0 { + wrapper_function_needed = true; + } let cpp_wrapper = if wrapper_function_needed { // Generate a new layer of C++ code to wrap/unwrap parameters diff --git a/engine/src/conversion/analysis/fun/subclass.rs b/engine/src/conversion/analysis/fun/subclass.rs index 8698539c6..50d998146 100644 --- a/engine/src/conversion/analysis/fun/subclass.rs +++ b/engine/src/conversion/analysis/fun/subclass.rs @@ -38,6 +38,7 @@ pub(super) fn subclasses_by_superclass( let mut subclasses_per_superclass: HashMap> = HashMap::new(); for api in apis.iter() { + if let Api::Subclass { name, superclass } = api { subclasses_per_superclass .entry(superclass.clone()) diff --git a/engine/src/conversion/codegen_rs/fun_codegen.rs b/engine/src/conversion/codegen_rs/fun_codegen.rs index 1b68e8c2a..3fd6f181d 100644 --- a/engine/src/conversion/codegen_rs/fun_codegen.rs +++ b/engine/src/conversion/codegen_rs/fun_codegen.rs @@ -150,11 +150,12 @@ pub(super) fn gen_function( ref method_kind, .. } => { - // Method, or static method. impl_entry = Some(fn_generator.generate_method_impl( matches!(method_kind, MethodKind::Constructor { .. }), + matches!(method_kind, MethodKind::Virtual { .. }), impl_for, )); + } FnKind::TraitMethod { ref details, .. } => { trait_impl_entry = Some(fn_generator.generate_trait_impl(details)); @@ -216,6 +217,7 @@ pub(super) fn gen_function( #(#doc_attrs)* #vis #bridge_unsafety fn #cxxbridge_name #lifetime_tokens ( #params ) #ret_type; )); + RsCodegenResult { extern_c_mod_items: vec![extern_c_mod_item], bindgen_mod_items, @@ -387,6 +389,7 @@ impl<'a> FnGenerator<'a> { fn generate_method_impl( &self, avoid_self: bool, + virt: bool, impl_block_type_name: &QualifiedName, ) -> Box { let (lifetime_tokens, wrapper_params, ret_type, call_body) = @@ -427,6 +430,7 @@ impl<'a> FnGenerator<'a> { } }), ty, + virt }) } @@ -470,6 +474,7 @@ impl<'a> FnGenerator<'a> { Box::new(ImplBlockDetails { item: ImplItem::Fn(parse_quote! { #stuff }), ty: ImplBlockKey { ty, lifetime: None }, + virt: false, }) } diff --git a/engine/src/conversion/codegen_rs/mod.rs b/engine/src/conversion/codegen_rs/mod.rs index ee56169e3..a2ed24683 100644 --- a/engine/src/conversion/codegen_rs/mod.rs +++ b/engine/src/conversion/codegen_rs/mod.rs @@ -13,6 +13,7 @@ mod lifetime; mod namespace_organizer; mod non_pod_struct; pub(crate) mod unqualify; +mod polymorphism; use indexmap::map::IndexMap as HashMap; use indexmap::set::IndexSet as HashSet; @@ -20,11 +21,10 @@ use indexmap::set::IndexSet as HashSet; use autocxx_parser::{ExternCppType, IncludeCppConfig, RustFun, UnsafePolicy}; use itertools::Itertools; +use polymorphism::{conv_ref_args, polymorphise}; use proc_macro2::{Span, TokenStream}; use syn::{ - parse_quote, punctuated::Punctuated, token::Comma, Attribute, Expr, FnArg, ForeignItem, - ForeignItemFn, Ident, ImplItem, Item, ItemForeignMod, ItemMod, Lifetime, TraitItem, Type, - TypePath, + parse_quote, punctuated::Punctuated, token::Comma, Attribute, Expr, FnArg, ForeignItem, ForeignItemFn, Ident, ImplItem, Item, ItemForeignMod, ItemMod, Lifetime, TraitItem, Type, TypePath }; use crate::{ @@ -47,7 +47,7 @@ use super::{ fun::{FnPhase, PodAndDepAnalysis, ReceiverMutability}, pod::PodAnalysis, }, - api::{AnalysisPhase, Api, SubclassName, TypeKind, TypedefKind}, + api::{AnalysisPhase, Api, SubclassName, TypeKind, TypedefKind, Virtualness}, convert_error::ErrorContextType, doc_attr::get_doc_attrs, }; @@ -69,6 +69,7 @@ struct ImplBlockKey { struct ImplBlockDetails { item: ImplItem, ty: ImplBlockKey, + virt: bool, } struct TraitImplBlockDetails { @@ -167,7 +168,7 @@ impl<'a> RsCodeGenerator<'a> { c.rs_codegen(all_apis) } - fn rs_codegen(mut self, all_apis: ApiVec) -> Vec { + fn rs_codegen(mut self, mut all_apis: ApiVec) -> Vec { // ... and now let's start to generate the output code. // First off, when we generate structs we may need to add some methods // if they're superclasses. @@ -175,8 +176,9 @@ impl<'a> RsCodeGenerator<'a> { let subclasses_with_a_single_trivial_constructor = find_trivially_constructed_subclasses(&all_apis); let non_pod_types = find_non_pod_types(&all_apis); + // Now let's generate the Rust code. - let (rs_codegen_results_and_namespaces, additional_cpp_needs): (Vec<_>, Vec<_>) = all_apis + let (mut rs_codegen_results_and_namespaces, additional_cpp_needs): (Vec<_>, Vec<_>) = all_apis .into_iter() .map(|api| { let more_cpp_needed = api.needs_cpp_codegen(); @@ -190,6 +192,9 @@ impl<'a> RsCodeGenerator<'a> { ((name, gen), more_cpp_needed) }) .unzip(); + + polymorphise(&mut rs_codegen_results_and_namespaces); + // First, the hierarchy of mods containing lots of 'use' statements // which is the final API exposed as 'ffi'. let mut use_statements = @@ -220,7 +225,7 @@ impl<'a> RsCodeGenerator<'a> { // Items for the [cxx::bridge] mod... let mut bridge_items: Vec = bridge_items.into_iter().flatten().collect(); // Things to include in the "extern "C"" mod passed within the cxx::bridge - let mut extern_c_mod_items: Vec = + let mut extern_c_mod_items: Vec<_> = extern_c_mod_items.into_iter().flatten().collect(); // The same for extern "Rust" let mut extern_rust_mod_items = extern_rust_mod_items.into_iter().flatten().collect(); @@ -420,6 +425,7 @@ impl<'a> RsCodeGenerator<'a> { } })) } + let mut unique_types = HashSet::new(); for (key, entries) in trait_impl_entries_by_trait_and_ty.into_iter() { let unsafety = key.unsafety; let ty = key.ty; @@ -428,7 +434,17 @@ impl<'a> RsCodeGenerator<'a> { #unsafety impl #trt for #ty { #(#entries)* } - })) + })); + unique_types.insert(ty.clone()); + } + for ty in unique_types { + output_items.push(Item::Impl(parse_quote! { + impl AsRef< #ty > for #ty { + fn as_ref(self: & #ty) -> & #ty { + self + } + } + })); } for (child_name, child_ns_entries) in ns_entries.children() { let new_ns = ns.push((*child_name).clone()); @@ -682,7 +698,7 @@ impl<'a> RsCodeGenerator<'a> { } }, ]; - let mut extern_c_mod_items = vec![ + let mut extern_c_mod_items: Vec = vec![ self.generate_cxxbridge_type(&full_cpp, false, Vec::new()), parse_quote! { fn #relinquish_ownership_call(self: &#cpp_id); @@ -812,9 +828,17 @@ impl<'a> RsCodeGenerator<'a> { let ret = details.ret; let unsafe_token = details.requires_unsafe.wrapper_token(); let global_def = quote! { #unsafe_token fn #api_name(#params) #ret }; - let params = unqualify_params(minisynize_punctuated(params)); + let mut params = unqualify_params(minisynize_punctuated(params)); let ret = unqualify_ret_type(ret.into()); let method_name = details.method_name; + for p in &mut params { + match p { + FnArg::Typed(ref mut pt) => { + conv_ref_args(pt, true) + }, + _ => {}, + } + } let cxxbridge_decl: ForeignItemFn = parse_quote! { #unsafe_token fn #api_name(#params) #ret; }; let args: Punctuated = @@ -1099,6 +1123,7 @@ impl<'a> RsCodeGenerator<'a> { ty: parse_quote! { #self_ty }, lifetime: None, }, + virt: false, })), None, None, diff --git a/engine/src/conversion/codegen_rs/polymorphism.rs b/engine/src/conversion/codegen_rs/polymorphism.rs new file mode 100644 index 000000000..817900ae1 --- /dev/null +++ b/engine/src/conversion/codegen_rs/polymorphism.rs @@ -0,0 +1,395 @@ +use crate::conversion::api::Virtualness; +use crate::types::QualifiedName; +use itertools::Itertools; +use proc_macro2::Span; +use quote::quote; +use syn::ExprPath; +use syn::{ + parse_quote, punctuated::Punctuated, token::Mut, Expr, FnArg, ForeignItem, ForeignItemFn, + GenericArgument, Ident, ImplItem, ImplItemFn, Item, ItemTrait, Pat, PatType, PathArguments, + Receiver, ReturnType, Signature, Stmt, TraitItem, TraitItemFn, Type, TypePath, +}; + +use super::{RsCodegenResult, Use}; + +#[doc = "Goes trough the entire codegen and modifies it to support polymorphism for refences and methods."] +pub(crate) fn polymorphise(rs_codegen_results: &mut Vec<(QualifiedName, RsCodegenResult)>) { + let mut impls_to_trait = RequiredPolyMethods::default(); + + if let Some(first) = rs_codegen_results.first_mut() { + first.1.bindgen_mod_items.push(parse_quote!( + use autocxx::*; + )); + } + + for results in &mut *rs_codegen_results { + polymorphise_methods(&mut impls_to_trait, results); + polymorphise_functions(&mut impls_to_trait, results); + } + + make_method_traits(rs_codegen_results, &mut impls_to_trait); +} + +#[doc = "Builds the function signature for a trait method with or without body"] +fn make_trait_method( + group: &(TypePath, QualifiedName, syn::Signature, syn::ExprPath), + mutability: bool, + first_namespace: &mut Option, +) -> (TraitItem, syn::ExprPath) { + let mut sig = group.2.clone(); + *first_namespace = Some(group.1.clone()); + for i in &mut sig.inputs { + match i { + FnArg::Typed(t) => { + conv_ref_args(t, false); + } + FnArg::Receiver(r) => { + *r = if mutability { + parse_quote!(&mut self) + } else { + parse_quote!(&self) + }; + } + } + } + + (TraitItem::Fn(parse_quote! {#sig; }), group.3.clone()) +} + +#[doc = "Adds the trait definition for the methods to the global items of codegen"] +fn add_methods_trait( + ident: &Ident, + methods: &Vec<(syn::TraitItem, syn::ExprPath)>, + cgr: &mut RsCodegenResult, +) { + let mut ti: ItemTrait = parse_quote! { pub trait #ident {} }; + ti.items = methods.iter().map(|m| m.0.clone()).collect(); + + cgr.global_items.push(Item::Trait(ti)); +} + +#[doc = "Adds the implementations of the methods trait to the global items of codegen"] +fn add_methods_impl( + mutability: bool, + ty: &TypePath, + methods: &Vec<(syn::TraitItem, syn::ExprPath)>, + ident: &Ident, + cgr: &mut RsCodegenResult, +) { + let impl_methods = build_impl_methods(&methods, mutability); + + let target_class = if mutability { + quote!(std::pin::Pin) + } else { + quote!(Child) + }; + let target_deref = if mutability { + quote!(std::ops::DerefMut) + } else { + quote!(std::ops::Deref) + }; + + cgr.global_items.push(parse_quote! { + impl> #ident for #target_class { + #( #impl_methods )* + } + }); +} + +#[doc = "Generates the neccessary traits and impl blocks for polymorphic methods"] +fn make_method_traits( + rs_codegen_results: &mut Vec<(QualifiedName, RsCodegenResult)>, + impls_to_trait: &mut RequiredPolyMethods, +) { + let mut added_use = false; + + for (impls, mutability) in [ + (&mut impls_to_trait.const_methods, false), + (&mut impls_to_trait.mut_methods, true), + ] { + for (ty, group) in &impls.iter().group_by(|k| &k.0) { + let mut first_namespace = None; + let methods: Vec<_> = group + .map(|group| make_trait_method(group, mutability, &mut first_namespace)) + .collect(); + + let mut cgr = RsCodegenResult::default(); + + let ident = Ident::new( + &format!( + "Methods{}{}", + ty.path.segments.last().unwrap().ident, + if mutability { "Mut" } else { "Const" } + ), + Span::call_site(), + ); + add_methods_trait(&ident, &methods, &mut cgr); + add_methods_impl(mutability, ty, &methods, &ident, &mut cgr); + + if !added_use { + cgr.global_items.push(parse_quote!( + use autocxx::*; + )); + added_use = true; + } + + rs_codegen_results.push((first_namespace.unwrap(), cgr)) + } + } +} + +#[doc = "Goes trough the entire codegen and modifies it to support polymorphism for refences and methods."] +fn build_impl_method( + mutability: bool, + mut inputs: Punctuated, + ident: &Ident, + output: &ReturnType, + handler: &ExprPath, +) -> ImplItemFn { + let params_call = params_from_fnargs(&inputs); + + if mutability { + for p in &mut inputs { + if let FnArg::Typed(pat) = p + && let syn::Pat::Ident(id) = &mut *pat.pat + { + id.mutability = Some(Mut::default()); + } + } + } + + let body: Vec = if mutability { + parse_quote!( + let ptr = self.as_mut(); + let raw_ptr = unsafe { ptr.get_unchecked_mut() }; + #handler (raw_ptr.as_base_mut() #(, #params_call )* ) + ) + } else { + parse_quote!(#handler (self.deref().as_base() #(, #params_call )* )) + }; + + parse_quote! { + fn #ident <'a>(#inputs ) #output { + #( #body )* + } + } +} + +#[doc = "Adds a single method to the list of methods that require a polymorphic implementation in a trait."] +fn build_impl_methods( + methods: &Vec<(syn::TraitItem, syn::ExprPath)>, + mutability: bool, +) -> Vec { + methods + .iter() + .map(|m| { + if let (TraitItem::Fn(TraitItemFn { sig, .. }), handler) = m { + let Signature { + ident, + output, + inputs, + .. + } = sig.clone(); + + build_impl_method(mutability, inputs, &ident, &output, handler) + } else { + panic!() + } + }) + .collect() +} + +#[doc = "Adds the methods to the list of methods that require a polymorphic implementation in a trait."] +fn polymorphise_methods( + impls_to_trait: &mut RequiredPolyMethods, + results: &mut (QualifiedName, RsCodegenResult), +) { + let imp = results.1.impl_entry.take(); + if let Some(impll) = imp { + if let ImplItem::Fn(f) = impll.item.clone() + && let Some(FnArg::Receiver(r)) = f.sig.inputs.first() + && !impll.virt //ignore virtual methods for now because they get get generated multiple times when overridden + { + let handler = if let Stmt::Expr(e, _) = f.block.stmts.get(0).unwrap() + && let Expr::Call(c) = e + && let Expr::Path(pat) = &*c.func + { + pat.clone() + } else { + panic!(); + }; + polymorphise_receiver_func(r, impls_to_trait, results.0.clone(), &f.sig, handler); + } else { + results.1.impl_entry = Some(impll); //put back + } + } +} + +#[doc = "Given a method signature from a method adds it to the list of methods that require a polymorphic implementation in a trait. + Returns true if one is required."] +fn polymorphise_receiver_func( + r: &Receiver, + impls_to_trait: &mut RequiredPolyMethods, + qn: QualifiedName, + sig: &Signature, + fnname: syn::ExprPath, +) -> bool { + match &*r.ty { + Type::Reference(re) => { + if let Type::Path(path) = &*re.elem { + impls_to_trait + .const_methods + .push((path.clone(), qn, sig.clone(), fnname)); + } + true + } + Type::Path(pat) => { + if let Some(ty) = try_get_pin_type(pat) { + impls_to_trait + .mut_methods + .push((ty.clone(), qn, sig.clone(), fnname)); + true + } else { + false + } + } + _ => false, + } +} + +#[doc = "Returns true if the function requires a wrapper for the polymorphic parameters. Additionally if the function is a method it will be added to the polymorhic method trat"] +fn analyze_function( + f: &Signature, + fnname: &Ident, + impls_to_trait: &mut RequiredPolyMethods, + qn: QualifiedName, +) -> bool { + let func_name = f.ident.to_string(); + f.inputs + .iter() + .filter(|i| match i { + FnArg::Receiver(r) => { + let fnname: syn::ExprPath = parse_quote!( #fnname ); + polymorphise_receiver_func(r, impls_to_trait, qn.clone(), &f, fnname) + } + FnArg::Typed(t) => match &*t.ty { + Type::Path(p) => try_get_pin_type(p).is_some(), + Type::Reference(_) => true, + _ => false, + }, + }) + .count() + > 0 + && !func_name.contains("autocxx_make_string") + && !func_name.contains("synthetic_const_copy_ctor") + && !func_name.starts_with("cast_") +} + +#[derive(Default)] +struct RequiredPolyMethods { + const_methods: Vec<(TypePath, QualifiedName, Signature, syn::ExprPath)>, + mut_methods: Vec<(TypePath, QualifiedName, Signature, syn::ExprPath)>, +} + +#[doc = "Converts the functions parameters into polymorphic impl parameters for reference parameters"] +fn polymorphise_functions( + impls_to_trait: &mut RequiredPolyMethods, + results: &mut (QualifiedName, RsCodegenResult), +) { + for ie in &mut results.1.extern_c_mod_items { + if let ForeignItem::Fn(ForeignItemFn { vis, sig, .. }) = ie { + let fnname: Ident = Ident::new( + &format!("{}_poly", sig.ident.to_string()), + Span::call_site(), + ); + if analyze_function(&sig, &fnname, impls_to_trait, results.0.clone()) { + *vis = parse_quote!(); + let mut fnargs = sig.inputs.clone(); + for arg in &mut fnargs { + if let FnArg::Typed(t) = arg { + conv_ref_args(t, true) + } + } + let ret = &mut sig.output; + let params = params_from_fnargs(&fnargs); + let oldident = &sig.ident; + + results.1.bindgen_mod_items.push(Item::Fn(parse_quote!( + pub fn #fnname ( #fnargs ) #ret { + cxxbridge :: #oldident ( #( #params, )* ) + } ))); + if let Some(fi) = results.1.materializations.first() + && let Use::UsedFromCxxBridgeWithAlias(a) = fi + { + results.1.materializations = vec![Use::Custom(Box::new( + parse_quote!(pub use bindgen::root:: #fnname as #a; ), + ))]; + } + } + } + } +} + +#[doc = "Returns the function parameters for a call getting the baseclass for polymorphic parameters"] +fn params_from_fnargs(fnargs: &Punctuated) -> Vec { + fnargs + .clone() + .iter() + .filter_map(|pat| match pat { + FnArg::Receiver(_) => None, + FnArg::Typed(t) => { + let argname = match &*t.pat { + syn::Pat::Ident(patty) => &patty.ident, + _ => todo!(), + }; + Some(if let Type::Reference(r) = &*t.ty && let Type::ImplTrait(it) = &*r.elem { + if let Some(syn::TypeParamBound::Trait(t)) = it.bounds.first() { + if t.path.segments.last().unwrap().ident.to_string() == "CppConstRef" { + parse_quote!( #argname .as_cpp_ref() ) + } else { + parse_quote!( #argname .as_cpp_mut() ) + } + } else { + panic!() + } + } else { + parse_quote!( #argname ) + }) + } + }) + .collect() +} + +#[doc = "Tries to get the type inside a pinned refenece if it is one"] +fn try_get_pin_type(p: &TypePath) -> Option<&TypePath> { + if p.path.segments.last().unwrap().ident.to_string() == "Pin" + && let PathArguments::AngleBracketed(a) = &p.path.segments.last().unwrap().arguments + && let Some(ty) = a.args.last() + && let GenericArgument::Type(ty) = ty + && let Type::Reference(re) = ty + && let Type::Path(result) = &*re.elem + { + Some(result) + } else { + None + } +} + +#[doc = "Given a function parameter turns it into polymorphic impl parameter if it is a reference"] +pub(crate) fn conv_ref_args(t: &mut PatType, mutability: bool) { + if let Type::Path(p) = &mut *t.ty { + if let Some(ty) = try_get_pin_type(&p) { + *t.ty = parse_quote! { &mut impl crate::CppPinMutRef<#ty> }; + if mutability && let Pat::Ident(i) = &mut *t.pat { + i.mutability = Some(Mut::default()) + } + } + } + if let Type::Reference(r) = &mut *t.ty { + let ty = &*r.elem; + if let Type::ImplTrait(_)=ty { + } else { + *t.ty = parse_quote! { &impl crate::CppConstRef<#ty> }; + } + } +} diff --git a/engine/src/lib.rs b/engine/src/lib.rs index d67aa4ea8..a9790a571 100644 --- a/engine/src/lib.rs +++ b/engine/src/lib.rs @@ -13,6 +13,8 @@ #![forbid(unsafe_code)] #![cfg_attr(feature = "nightly", feature(doc_cfg))] +#![feature(let_chains)] + mod ast_discoverer; mod conversion; mod cxxbridge; diff --git a/integration-tests/src/lib.rs b/integration-tests/src/lib.rs index 02d672f92..42f8bfa78 100644 --- a/integration-tests/src/lib.rs +++ b/integration-tests/src/lib.rs @@ -382,6 +382,7 @@ pub fn do_run_test( #module_attributes use autocxx::prelude::*; + use crate::ffi::*; include_cpp!( #hexathorpe include "input.h" diff --git a/macro/src/lib.rs b/macro/src/lib.rs index 6cdb74a36..e418112f8 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -14,7 +14,7 @@ use proc_macro2::{Ident, Span}; use proc_macro_error::{abort, proc_macro_error}; use quote::quote; use syn::parse::Parser; -use syn::{parse_macro_input, parse_quote, Fields, Item, ItemStruct, Visibility}; +use syn::{parse_macro_input, parse_quote, Fields, Item, ItemEnum, ItemFn, ItemStruct, Visibility}; /// Implementation of the `include_cpp` macro. See documentation for `autocxx` crate. #[proc_macro_error] @@ -91,11 +91,34 @@ pub fn extern_rust_type(attr: TokenStream, input: TokenStream) -> TokenStream { } let i: Item = syn::parse(input.clone()).unwrap_or_else(|_| abort!(Span::call_site(), "Expected an item")); - match i { - Item::Struct(..) | Item::Enum(..) | Item::Fn(..) => {} + + let ty = match &i { + Item::Struct(ItemStruct { ident, .. }) => { + ident + }, + Item::Enum(ItemEnum { ident, .. }) => { + ident + }, + Item::Fn(ItemFn{ .. }) => { + return quote! { #i }.into(); + }, _ => abort!(Span::call_site(), "Expected a struct or enum"), - } - input + }; + + quote!( + #i + + impl CppConstRef<#ty> for #ty { + fn as_cpp_ref<'c>(&'c self) -> &'c #ty { + self + } + } + /* impl CppPinMutRef<#ty> for #ty { + fn as_cpp_mut<'c>(&'c mut self) -> std::pin::Pin<&'c mut #ty> { + self + } + } */ + ).into() } /// Attribute to state that a Rust function is to be exported to C++ diff --git a/src/lib.rs b/src/lib.rs index 3683e5ee6..e0873e967 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +#![feature(anonymous_lifetime_in_impl_trait)] + #![doc = include_str!("../README.md")] #![cfg_attr(nightly, feature(unsize))] #![cfg_attr(nightly, feature(dispatch_from_dyn))] @@ -407,6 +409,9 @@ macro_rules! usage { }; } +use std::mem::transmute; +use std::ops::Deref; +use std::ops::DerefMut; use std::pin::Pin; #[doc(hidden)] @@ -669,6 +674,28 @@ where Pin::new(Box::new(self)) } } +pub trait CppPinMutRef { + fn as_cpp_mut<'c>(&'c mut self) -> Pin<&'c mut Base>; +} +pub trait CppConstRef { + fn as_cpp_ref<'c>(&'c self) -> &'c Base; +} +impl, C: ToBaseClass> CppConstRef for Child { + fn as_cpp_ref<'c>(&'c self) -> &'c Base { + unsafe { transmute(self.deref().as_base()) } //TODO: Why safe? + } +} +impl> CppPinMutRef for Pin where Child: DerefMut { + fn as_cpp_mut<'c>(&'c mut self) -> Pin<&'c mut Base> { + let ptr = self.as_mut(); + let raw_ptr = unsafe { ptr.get_unchecked_mut() }; + unsafe { transmute(raw_ptr.as_base_mut()) } + } +} +pub trait ToBaseClass { + fn as_base(&self)-> &T; + fn as_base_mut(&mut self)-> Pin<&mut T>; +} use cxx::memory::UniquePtrTarget; use cxx::UniquePtr; @@ -715,8 +742,21 @@ pub mod prelude { pub use moveit::moveit; pub use moveit::new::New; pub use moveit::Emplace; + + //Polymorphism + pub use crate::CppPinMutRef; + pub use crate::CppConstRef; + pub use crate::ToBaseClass; } +impl ToBaseClass for Child where Child: AsRef{ + fn as_base(&self)-> &Base { + self.as_ref() + } + fn as_base_mut(&mut self)-> Pin<&mut Base> { + unsafe { transmute(self.as_ref()) } + } +} /// Re-export moveit for ease of consumers. pub use moveit;