diff --git a/binding-generator/src/class.rs b/binding-generator/src/class.rs index 6bb5b732c..42d335a13 100644 --- a/binding-generator/src/class.rs +++ b/binding-generator/src/class.rs @@ -407,10 +407,8 @@ impl<'tu, 'ge> Class<'tu, 'ge> { out.extend(fields.flat_map(|fld| { iter::from_fn({ let doc_comment = Rc::from(fld.doc_comment()); - let fld_type_ref = fld - .type_ref() - .into_owned() - .with_type_hint(TypeRefTypeHint::PrimitiveRefAsPointer); + let mut fld_type_ref = fld.type_ref().into_owned(); + fld_type_ref.set_type_hint(TypeRefTypeHint::PrimitiveRefAsPointer); let fld_type_ref_return_as_naked = fld_type_ref.return_as_naked(); let fld_const = fld.constness(); let passed_by_ref = fld_type_ref.can_return_as_direct_reference(); @@ -502,7 +500,6 @@ impl<'tu, 'ge> Class<'tu, 'ge> { arguments: Rc::new([Field::new_desc(FieldDesc { cpp_fullname: "val".into(), type_ref: fld_type_ref.with_inherent_constness(Constness::Const), - type_ref_type_hint: TypeRefTypeHint::None, default_value: fld.default_value().map(|v| v.into()), })]), return_kind: ReturnKind::InfallibleNaked, diff --git a/binding-generator/src/field.rs b/binding-generator/src/field.rs index 91d348828..d28742ae1 100644 --- a/binding-generator/src/field.rs +++ b/binding-generator/src/field.rs @@ -43,10 +43,21 @@ impl<'tu, 'ge> Field<'tu, 'ge> { Self::Desc(Rc::new(desc)) } + pub(crate) fn to_desc(&self) -> Rc> { + match self { + Self::Clang { type_ref_type_hint, .. } => Rc::new(FieldDesc { + cpp_fullname: self.cpp_name(CppNameStyle::Reference).into(), + type_ref: self.type_ref().into_owned().with_type_hint(type_ref_type_hint.clone()), // fixme, add an option to avoid inheriting type_ref + default_value: self.default_value().map(Rc::from), + }), + Self::Desc(desc) => Rc::clone(desc), + } + } + pub fn type_ref_type_hint(&self) -> &TypeRefTypeHint { match self { Self::Clang { type_ref_type_hint, .. } => type_ref_type_hint, - Self::Desc(desc) => &desc.type_ref_type_hint, + Self::Desc(desc) => desc.type_ref.type_hint(), } } @@ -56,7 +67,7 @@ impl<'tu, 'ge> Field<'tu, 'ge> { type_ref_type_hint: old_type_ref_type_hint, .. } => *old_type_ref_type_hint = type_ref_type_hint, - Self::Desc(desc) => Rc::make_mut(desc).type_ref_type_hint = type_ref_type_hint, + Self::Desc(desc) => Rc::make_mut(desc).type_ref.set_type_hint(type_ref_type_hint), } } @@ -92,6 +103,12 @@ impl<'tu, 'ge> Field<'tu, 'ge> { } } + pub fn with_type_ref(&self, type_ref: TypeRef<'tu, 'ge>) -> Self { + let mut desc = self.to_desc(); + Rc::make_mut(&mut desc).type_ref = type_ref; + Self::Desc(desc) + } + pub fn constness(&self) -> Constness { let type_ref = self.type_ref(); match type_ref.canonical().kind().as_ref() { @@ -254,7 +271,6 @@ impl fmt::Debug for Field<'_, '_> { pub struct FieldDesc<'tu, 'ge> { pub cpp_fullname: Rc, pub type_ref: TypeRef<'tu, 'ge>, - pub type_ref_type_hint: TypeRefTypeHint, pub default_value: Option>, } @@ -263,7 +279,6 @@ impl<'tu, 'ge> FieldDesc<'tu, 'ge> { Self { cpp_fullname: name.into(), type_ref, - type_ref_type_hint: TypeRefTypeHint::None, default_value: None, } } diff --git a/binding-generator/src/func.rs b/binding-generator/src/func.rs index c38430873..8f6d85764 100644 --- a/binding-generator/src/func.rs +++ b/binding-generator/src/func.rs @@ -108,7 +108,6 @@ impl<'tu, 'ge> Func<'tu, 'ge> { Field::new_desc(FieldDesc { cpp_fullname: arg.cpp_name(CppNameStyle::Reference).into(), type_ref, - type_ref_type_hint: TypeRefTypeHint::None, default_value: arg.default_value().map(|v| v.into()), }) }, @@ -126,15 +125,16 @@ impl<'tu, 'ge> Func<'tu, 'ge> { .map(|s| s().cpp_name(CppNameStyle::Reference).into_owned()) .join(", "); let mut desc = self.to_desc(InheritConfig::empty().kind().arguments().return_type_ref()); - desc.kind = kind; - desc.type_hint = FuncTypeHint::Specialized; - desc.arguments = arguments; - desc.return_type_ref = specialized(&return_type_ref).unwrap_or(return_type_ref); - desc.cpp_body = FuncCppBody::ManualCall(format!("{{{{name}}}}<{generic}>({{{{args}}}})").into()); - Self::new_desc(desc) + let desc_mut = Rc::make_mut(&mut desc); + desc_mut.kind = kind; + desc_mut.type_hint = FuncTypeHint::Specialized; + desc_mut.arguments = arguments; + desc_mut.return_type_ref = specialized(&return_type_ref).unwrap_or(return_type_ref); + desc_mut.cpp_body = FuncCppBody::ManualCall(format!("{{{{name}}}}<{generic}>({{{{args}}}})").into()); + Self::Desc(desc) } - pub(crate) fn to_desc(&self, skip_config: InheritConfig) -> FuncDesc<'tu, 'ge> { + pub(crate) fn to_desc(&self, skip_config: InheritConfig) -> Rc> { match self { Func::Clang { .. } => { let kind = if skip_config.kind { @@ -167,7 +167,7 @@ impl<'tu, 'ge> Func<'tu, 'ge> { } else { self.file_line_name().location }; - FuncDesc { + Rc::new(FuncDesc { kind, type_hint: FuncTypeHint::None, constness: self.constness(), @@ -183,7 +183,7 @@ impl<'tu, 'ge> Func<'tu, 'ge> { cpp_body: FuncCppBody::Auto, rust_body: FuncRustBody::Auto, rust_extern_definition: FuncRustExtern::Auto, - } + }) } Func::Desc(desc) => { let kind = if skip_config.kind { @@ -201,23 +201,12 @@ impl<'tu, 'ge> Func<'tu, 'ge> { } else { desc.def_loc.clone() }; - FuncDesc { - kind, - type_hint: FuncTypeHint::None, - constness: desc.constness, - return_kind: desc.return_kind, - cpp_name: Rc::clone(&desc.cpp_name), - rust_custom_leafname: desc.rust_custom_leafname.as_ref().map(Rc::clone), - rust_module: Rc::clone(&desc.rust_module), - doc_comment: Rc::clone(&desc.doc_comment), - def_loc, - rust_generic_decls: Rc::new([]), - arguments: Rc::clone(&desc.arguments), - return_type_ref, - cpp_body: FuncCppBody::Auto, - rust_body: FuncRustBody::Auto, - rust_extern_definition: FuncRustExtern::Auto, - } + let mut desc = Rc::clone(desc); + let desc_mut = Rc::make_mut(&mut desc); + desc_mut.kind = kind; + desc_mut.return_type_ref = return_type_ref; + desc_mut.def_loc = def_loc; + desc } } } @@ -249,8 +238,8 @@ impl<'tu, 'ge> Func<'tu, 'ge> { Func::Clang { .. } => { if inherit_config.any_enabled() { let mut desc = self.to_desc(inherit_config); - transfer(&mut desc, ancestor, inherit_config); - Self::new_desc(desc); + transfer(Rc::make_mut(&mut desc), ancestor, inherit_config); + *self = Func::Desc(desc); } } Func::Desc(desc) => transfer(Rc::make_mut(desc), ancestor, inherit_config), @@ -434,38 +423,47 @@ impl<'tu, 'ge> Func<'tu, 'ge> { pub fn return_type_ref(&self) -> TypeRef<'tu, 'ge> { match self { - &Self::Clang { entity, gen_env, .. } => match self.kind().as_ref() { - FuncKind::Constructor(cls) => cls.type_ref(), - // `operator =` returns a reference to the `self` value and it's quite cumbersome to handle correctly - FuncKind::InstanceOperator(_, OperatorKind::Set) => TypeRefDesc::void(), - FuncKind::Function - | FuncKind::InstanceMethod(..) - | FuncKind::StaticMethod(..) - | FuncKind::FieldAccessor(..) - | FuncKind::ConversionMethod(..) - | FuncKind::GenericInstanceMethod(..) - | FuncKind::GenericFunction - | FuncKind::FunctionOperator(..) - | FuncKind::InstanceOperator(..) => { - let mut out = TypeRef::new_ext( - entity.get_result_type().expect("Can't get return type"), - TypeRefTypeHint::PrimitiveRefAsPointer, - None, - gen_env, - ); - if let Some(type_hint) = settings::ARGUMENT_OVERRIDE - .get(&self.func_id()) - .and_then(|x| x.get(RETURN_HINT)) - { - out = out.with_type_hint(type_hint.clone()) + &Self::Clang { entity, gen_env, .. } => { + let mut out = match self.kind().as_ref() { + FuncKind::Constructor(cls) => cls.type_ref(), + // `operator =` returns a reference to the `self` value and it's quite cumbersome to handle correctly + FuncKind::InstanceOperator(_, OperatorKind::Set) => TypeRefDesc::void(), + FuncKind::Function + | FuncKind::InstanceMethod(..) + | FuncKind::StaticMethod(..) + | FuncKind::FieldAccessor(..) + | FuncKind::ConversionMethod(..) + | FuncKind::GenericInstanceMethod(..) + | FuncKind::GenericFunction + | FuncKind::FunctionOperator(..) + | FuncKind::InstanceOperator(..) => { + let out = TypeRef::new_ext( + entity.get_result_type().expect("Can't get return type"), + TypeRefTypeHint::PrimitiveRefAsPointer, + None, + gen_env, + ); + out.as_reference().unwrap_or(out) } - if let Some(type_ref) = out.as_reference() { - type_ref - } else { - out + }; + if let Some(type_hint) = settings::ARGUMENT_OVERRIDE + .get(&self.func_id()) + .and_then(|x| x.get(RETURN_HINT)) + { + out.set_type_hint(type_hint.clone()); + // if we're returning a BoxedRef then assign its mutability to the mutability of the borrowed argument + if let TypeRefTypeHint::BoxedAsRef(borrow_arg_name, _) = type_hint { + let args = self.arguments(); + let borrow_arg = args + .iter() + .find(|arg| arg.cpp_name(CppNameStyle::Declaration) == *borrow_arg_name); + if let Some(borrow_arg) = borrow_arg { + out.set_inherent_constness(borrow_arg.type_ref().constness()); + } } } - }, + out + } Self::Desc(desc) => desc.return_type_ref.clone(), } } diff --git a/binding-generator/src/renderer.rs b/binding-generator/src/renderer.rs index 6811ed22d..986ea36c6 100644 --- a/binding-generator/src/renderer.rs +++ b/binding-generator/src/renderer.rs @@ -7,7 +7,8 @@ pub trait TypeRefRenderer<'a> { type Recursed: TypeRefRenderer<'a> + Sized; fn render<'t>(self, type_ref: &'t TypeRef) -> Cow<'t, str>; - fn recurse(&self) -> Self::Recursed; + fn recurse(self) -> Self::Recursed; + fn recursed(&self) -> Self::Recursed; } #[derive(Debug)] @@ -47,11 +48,8 @@ impl<'a> TypeRefRenderer<'a> for CppRenderer<'_> { if self.name.is_empty() { format!("{cnst}{typ}**", typ = inner.render(self.recurse())) } else { - format!( - "{cnst}{typ}(*{name})[{size}]", - typ = inner.render(self.recurse()), - name = self.name - ) + let name = self.name; + format!("{cnst}{typ}(*{name})[{size}]", typ = inner.render(self.recurse())) } } else { format!("{typ}*{space_name}", typ = inner.render(self.recurse())) @@ -72,7 +70,7 @@ impl<'a> TypeRefRenderer<'a> for CppRenderer<'_> { // fixme: hack to keep backwards compatible behavior after tuple rendering changes // ideal fix would be to use CppName::Reference in the recurse() function globally // but it changes the function identifier generation - let mut renderer = self.recurse(); + let mut renderer = self.recursed(); renderer.name_style = CppNameStyle::Reference; tref.render(renderer) }) @@ -138,7 +136,11 @@ impl<'a> TypeRefRenderer<'a> for CppRenderer<'_> { .into() } - fn recurse(&self) -> Self { + fn recurse(self) -> Self { + self.recursed() + } + + fn recursed(&self) -> Self::Recursed { Self { name_style: self.name_style, name: "", @@ -164,7 +166,11 @@ impl<'a> TypeRefRenderer<'a> for CppExternReturnRenderer { self.recurse().render(over.as_ref().unwrap_or(type_ref)).into_owned().into() } - fn recurse(&self) -> Self::Recursed { + fn recurse(self) -> Self::Recursed { + self.recursed() + } + + fn recursed(&self) -> Self::Recursed { CppRenderer::new(CppNameStyle::Reference, "", true) } } @@ -175,7 +181,7 @@ fn render_cpp_tpl<'a>(renderer: impl TypeRefRenderer<'a>, type_ref: &TypeRef) -> let generic_types = generic_types .iter() .filter_map(|t| match t { - TemplateArg::Typename(type_ref) => Some(type_ref.render(renderer.recurse())), + TemplateArg::Typename(type_ref) => Some(type_ref.render(renderer.recursed())), TemplateArg::Constant(literal) => Some(literal.into()), TemplateArg::Unknown => None, }) diff --git a/binding-generator/src/settings/argument_override.rs b/binding-generator/src/settings/argument_override.rs index 7bae78ddd..131f527b6 100644 --- a/binding-generator/src/settings/argument_override.rs +++ b/binding-generator/src/settings/argument_override.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use once_cell::sync::Lazy; use crate::type_ref::TypeRefTypeHint; +use crate::writer::rust_native::type_ref::Lifetime; use crate::FuncId; pub const RETURN_HINT: &str = "return"; @@ -37,6 +38,18 @@ pub static ARGUMENT_OVERRIDE: Lazy> = Lazy::new(|| { "cv_setUseCollection_bool", "cv_useCollection", "cv_vconcat_const_MatX_size_t_const__OutputArrayR", // duplicate of cv_vconcat_VectorOfMat_Mat, but with pointers + "cv_Mat_Mat_MatRR", // Mat::copy that takes arg by value // ### cudaimgproc ### "cv_cuda_histEven_const__InputArrayR_GpuMatXX_intXX_intXX_intXX_StreamR", // slice of boxed objects "cv_cuda_histRange_const__InputArrayR_GpuMatXX_const_GpuMatXX_StreamR", // slice of boxed objects diff --git a/binding-generator/src/type_ref.rs b/binding-generator/src/type_ref.rs index a4fc1175f..d785c6798 100644 --- a/binding-generator/src/type_ref.rs +++ b/binding-generator/src/type_ref.rs @@ -11,6 +11,7 @@ use crate::class::{ClassDesc, TemplateKind}; use crate::element::ExcludeKind; use crate::renderer::{CppExternReturnRenderer, CppRenderer, TypeRefRenderer}; use crate::vector::VectorDesc; +use crate::writer::rust_native::type_ref::Lifetime; use crate::{settings, AbstractRefWrapper, ClassSimplicity, ExportConfig}; use crate::{Class, Element, Enum, Function, GeneratedType, GeneratorEnv, SmartPtr, StringExt, Tuple, Typedef, Vector}; @@ -56,6 +57,9 @@ pub enum TypeRefTypeHint { PrimitiveRefAsPointer, /// Adds a length to an unsized array AddArrayLength(usize), + /// Return boxed class as a wrapped reference to maintain lifetime connection to the source boxed object + /// (cpp_name(Declaration), lifetime) + BoxedAsRef(&'static str, Lifetime), } #[derive(Clone)] @@ -161,51 +165,22 @@ impl<'tu, 'ge> TypeRef<'tu, 'ge> { } pub fn with_type_hint(self, type_hint: TypeRefTypeHint) -> Self { - if *self.type_hint() != type_hint { - match self { - Self::Clang { - type_ref, - parent_entity, - gen_env, - .. - } => Self::Clang { - type_ref, - type_hint, - parent_entity, - gen_env, - }, - Self::Desc(mut desc) => { - Rc::make_mut(&mut desc).type_hint = type_hint; - Self::Desc(desc) - } - } - } else { - self - } + let mut out = self; + out.set_type_hint(type_hint); + out } - pub fn with_inherent_constness(&self, constness: Constness) -> Self { - if self.inherent_constness() != constness { + pub fn set_type_hint(&mut self, type_hint: TypeRefTypeHint) { + if *self.type_hint() != type_hint { match self { Self::Clang { - type_ref, - type_hint, - parent_entity, - gen_env, - } => Self::new_desc(TypeRefDesc { - kind: type_ref.kind(type_hint.clone(), *parent_entity, gen_env), - inherent_constness: constness, - type_hint: type_hint.clone(), - template_specialization_args: type_ref.template_specialization_args(gen_env).into(), - }), - Self::Desc(desc) => { - let mut desc = (**desc).clone(); - desc.inherent_constness = constness; - Self::Desc(Rc::new(desc)) + type_hint: old_type_hint, + .. + } => *old_type_hint = type_hint, + Self::Desc(ref mut desc) => { + Rc::make_mut(desc).type_hint = type_hint; } } - } else { - self.clone() } } @@ -277,6 +252,15 @@ impl<'tu, 'ge> TypeRef<'tu, 'ge> { } } + /// Map the contained TypeRef inside `Pointer` and `Reference` variants, useful for changing constness + pub fn map_ptr_ref<'otu, 'oge>(&self, f: impl FnOnce(&TypeRef<'tu, 'ge>) -> TypeRef<'otu, 'oge>) -> TypeRef<'otu, 'oge> { + match self.kind().as_ref() { + TypeRefKind::Pointer(inner) => TypeRef::new_pointer(inner.map(f)), + TypeRefKind::Reference(inner) => TypeRef::new_reference(inner.map(f)), + _ => f(self), + } + } + /// Map the contained TypeRef inside `Vector` variant pub fn map_vector<'otu, 'oge>(&self, f: impl FnOnce(&TypeRef<'tu, 'ge>) -> TypeRef<'otu, 'oge>) -> TypeRef<'otu, 'oge> { match self.kind().as_ref() { @@ -343,6 +327,35 @@ impl<'tu, 'ge> TypeRef<'tu, 'ge> { } } + pub fn set_inherent_constness(&mut self, constness: Constness) { + if self.inherent_constness() != constness { + match self { + Self::Clang { + type_ref, + type_hint, + parent_entity, + gen_env, + } => { + *self = Self::new_desc(TypeRefDesc { + kind: type_ref.kind(type_hint.clone(), *parent_entity, gen_env), + inherent_constness: constness, + type_hint: type_hint.clone(), + template_specialization_args: type_ref.template_specialization_args(gen_env).into(), + }) + } + Self::Desc(desc) => { + Rc::make_mut(desc).inherent_constness = constness; + } + } + } + } + + pub fn with_inherent_constness(&self, constness: Constness) -> Self { + let mut out = self.clone(); + out.set_inherent_constness(constness); + out + } + /// Returns Some((rust_name, cpp_name)) if canonical kind is primitive, None otherwise pub fn as_primitive(&self) -> Option<(&'static str, &'static str)> { match self.canonical().kind().as_ref() { @@ -490,6 +503,16 @@ impl<'tu, 'ge> TypeRef<'tu, 'ge> { } } + pub fn as_ptr_or_ref(&self) -> Option> { + if let TypeRefKind::Pointer(out) | TypeRefKind::Reference(out) | TypeRefKind::RValueReference(out) = + self.canonical().kind().into_owned() + { + Some(out) + } else { + None + } + } + /// True for types whose values are moved as per C++ function specification pub fn is_by_move(&self) -> bool { matches!(self.canonical().kind().as_ref(), TypeRefKind::RValueReference(_)) diff --git a/binding-generator/src/writer/rust_native/func.rs b/binding-generator/src/writer/rust_native/func.rs index 9e7ee1f93..f76e67472 100644 --- a/binding-generator/src/writer/rust_native/func.rs +++ b/binding-generator/src/writer/rust_native/func.rs @@ -13,83 +13,21 @@ use crate::{reserved_rename, settings, Class, CompiledInterpolation, Element, Fu use super::comment::{render_ref, RenderComment}; use super::element::{DefaultRustNativeElement, RustElement}; -use super::type_ref::TypeRefExt; +use super::type_ref::{Lifetime, TypeRefExt}; use super::{comment, disambiguate_single_name, rust_disambiguate_names, RustNativeGeneratedElement}; pub trait FuncExt<'tu, 'ge> { - fn companion_func_default_args(&self) -> Option>; fn companion_functions(&self) -> Vec>; } impl<'tu, 'ge> FuncExt<'tu, 'ge> for Func<'tu, 'ge> { - /// Companion function with all optional arguments as defaults - fn companion_func_default_args(&self) -> Option> { - fn viable_default_arg(arg: &Field) -> bool { - arg.default_value().is_some() && !arg.is_user_data() && { - let type_ref = arg.type_ref(); - !type_ref.as_function().is_some() - // don't remove the arguments that are used to pass the slice or its length - && !matches!(type_ref.type_hint(), TypeRefTypeHint::Slice | TypeRefTypeHint::LenForSlice(..)) - } - } - - if self.kind().as_field_accessor().is_some() { - return None; - } - - let args = self.arguments(); - // default args are usually in the end - if args.iter().rev().any(viable_default_arg) { - let first_non_default_arg_idx = args.iter().rposition(|arg| !viable_default_arg(arg)); - let (args_without_def, args_with_def) = if let Some(first_non_default_arg_idx) = first_non_default_arg_idx { - if first_non_default_arg_idx + 1 == args.len() { - return None; - } - (&args[..=first_non_default_arg_idx], &args[first_non_default_arg_idx..]) - } else { - ([].as_slice(), args.as_ref()) - }; - let original_rust_leafname = self.rust_leafname(FishStyle::No); - let mut doc_comment = self.doc_comment().into_owned(); - let rust_leafname = format!("{}_def", original_rust_leafname); - let default_args = comment::render_cpp_default_args(args_with_def); - if !doc_comment.is_empty() { - doc_comment.push_str("\n\n"); - } - write!( - &mut doc_comment, - "## Note\nThis alternative version of [{refr}] function uses the following default values for its arguments:\n{default_args}", - refr = render_ref(self, Some(&original_rust_leafname)) - ) - .expect("Impossible"); - let out = match self.clone() { - Func::Clang { .. } => { - let mut desc = self.to_desc(InheritConfig::empty().doc_comment().arguments()); - desc.rust_custom_leafname = Some(rust_leafname.into()); - desc.arguments = args_without_def.into(); - desc.doc_comment = doc_comment.into(); - Self::new_desc(desc) - } - Func::Desc(mut desc) => { - let desc_ref = Rc::make_mut(&mut desc); - desc_ref.arguments = args_without_def.into(); - desc_ref.rust_custom_leafname = Some(rust_leafname.into()); - Self::Desc(desc) - } - }; - if out.exclude_kind().is_included() { - Some(out) - } else { - None - } - } else { - None - } - } - fn companion_functions(&self) -> Vec> { let mut out = vec![]; - out.extend(self.companion_func_default_args()); + if let Some(default_func) = companion_func_default_args(self) { + out.extend(default_func.companion_functions()); + out.push(default_func); + } + out.extend(companion_func_boxref_mut(self)); out } } @@ -278,6 +216,11 @@ impl RustNativeGeneratedElement for Func<'_, '_> { decl_args.push(cls_type_ref.rust_self_func_decl(constness)); } let mut callback_arg_name: Option<&str> = None; + let boxed_ref_arg = if let TypeRefTypeHint::BoxedAsRef(name, lt) = return_type_ref.type_hint() { + Some((name, lt)) + } else { + None + }; for (name, arg) in &args { let arg_type_ref = arg.type_ref(); if arg.is_user_data() { @@ -293,7 +236,10 @@ impl RustNativeGeneratedElement for Func<'_, '_> { callback_arg_name = Some(name); } if !arg.as_slice_len().is_some() { - decl_args.push(arg_type_ref.rust_arg_func_decl(name)); + let lt = boxed_ref_arg + .filter(|(&boxed_arg_name, _)| boxed_arg_name == name) + .map_or(Lifetime::Elided, |(_, lt)| *lt); + decl_args.push(arg_type_ref.rust_arg_func_decl(name, lt)); } pre_post_arg_handle( arg_type_ref.rust_arg_pre_call(name, return_kind.is_infallible()), @@ -348,7 +294,7 @@ impl RustNativeGeneratedElement for Func<'_, '_> { }, ), ("name", name.as_ref()), - ("generic_decl", &rust_generic_decl(self)), + ("generic_decl", &rust_generic_decl(self, &return_type_ref)), ("decl_args", &decl_args.join(", ")), ("rv_rust_full", &return_type_func_decl), ("pre_call_args", &pre_call_args.join("\n")), @@ -881,20 +827,133 @@ where NamePool::with_capacity(size_hint.1.unwrap_or(size_hint.0)).into_disambiguator(args, |f| f.cpp_name(CppNameStyle::Declaration)) } -fn rust_generic_decl<'f>(f: &'f Func) -> Cow<'f, str> { +fn rust_generic_decl<'f>(f: &'f Func, return_type_ref: &TypeRef) -> Cow<'f, str> { + let mut decls = vec![]; + if let TypeRefTypeHint::BoxedAsRef(_, lt) = return_type_ref.type_hint() { + decls.push(lt.to_string()); + } match f { - Func::Clang { .. } => "".into(), + Func::Clang { .. } => {} Func::Desc(desc) => { - let mut decls = Vec::with_capacity(desc.rust_generic_decls.len()); + decls.reserve(desc.rust_generic_decls.len()); for (typ, constraint) in desc.rust_generic_decls.as_ref() { decls.push(format!("{typ}: {constraint}")); } - let decls = decls.join(", "); - if decls.is_empty() { - "".into() - } else { - format!("<{decls}>").into() + } + } + let decls = decls.join(", "); + if decls.is_empty() { + "".into() + } else { + format!("<{decls}>").into() + } +} + +/// Companion function with all optional arguments as defaults +fn companion_func_default_args<'tu, 'ge>(f: &Func<'tu, 'ge>) -> Option> { + fn viable_default_arg(arg: &Field) -> bool { + arg.default_value().is_some() && !arg.is_user_data() && { + let type_ref = arg.type_ref(); + !type_ref.as_function().is_some() + // don't remove the arguments that are used to pass the slice or its length + && !matches!(type_ref.type_hint(), TypeRefTypeHint::Slice | TypeRefTypeHint::LenForSlice(..)) + } + } + + if f.kind().as_field_accessor().is_some() { + return None; + } + + let args = f.arguments(); + // default args are usually in the end + if args.iter().rev().any(viable_default_arg) { + let first_non_default_arg_idx = args.iter().rposition(|arg| !viable_default_arg(arg)); + let (args_without_def, args_with_def) = if let Some(first_non_default_arg_idx) = first_non_default_arg_idx { + if first_non_default_arg_idx + 1 == args.len() { + return None; + } + (&args[..=first_non_default_arg_idx], &args[first_non_default_arg_idx..]) + } else { + ([].as_slice(), args.as_ref()) + }; + let original_rust_leafname = f.rust_leafname(FishStyle::No); + let mut doc_comment = f.doc_comment().into_owned(); + let rust_leafname = format!("{}_def", original_rust_leafname); + let default_args = comment::render_cpp_default_args(args_with_def); + if !doc_comment.is_empty() { + doc_comment.push_str("\n\n"); + } + write!( + &mut doc_comment, + "## Note\nThis alternative version of [{refr}] function uses the following default values for its arguments:\n{default_args}", + refr = render_ref(f, Some(&original_rust_leafname)) + ) + .expect("Impossible"); + let out = match f.clone() { + Func::Clang { .. } => { + let mut desc = f.to_desc(InheritConfig::empty().doc_comment().arguments()); + let desc_mut = Rc::make_mut(&mut desc); + desc_mut.rust_custom_leafname = Some(rust_leafname.into()); + desc_mut.arguments = args_without_def.into(); + desc_mut.doc_comment = doc_comment.into(); + Func::Desc(desc) + } + Func::Desc(mut desc) => { + let desc_ref = Rc::make_mut(&mut desc); + desc_ref.arguments = args_without_def.into(); + desc_ref.rust_custom_leafname = Some(rust_leafname.into()); + Func::Desc(desc) } + }; + if out.exclude_kind().is_included() { + Some(out) + } else { + None } + } else { + None + } +} + +/// Companion function returning `BoxRefMut` for a corresponding function returning `BoxRef` +fn companion_func_boxref_mut<'tu, 'ge>(f: &Func<'tu, 'ge>) -> Option> { + let ret_type_ref = f.return_type_ref(); + if let TypeRefTypeHint::BoxedAsRef(borrow_arg_name, _) = ret_type_ref.type_hint() { + let mut desc = f.to_desc(InheritConfig::empty()); + let desc_mut = Rc::make_mut(&mut desc); + let mut cloned_args = None; + let args = if let Some(args) = Rc::get_mut(&mut desc_mut.arguments) { + args + } else { + cloned_args = Some(desc_mut.arguments.to_vec()); + cloned_args.as_mut().unwrap() + }; + let const_borrow_arg = args + .iter_mut() + .find(|arg| arg.cpp_name(CppNameStyle::Declaration) == *borrow_arg_name) + .filter(|arg| { + let type_ref = arg.type_ref(); + type_ref.constness().is_const() + && type_ref + .as_ptr_or_ref() + .map_or(false, |ptr_or_ref| ptr_or_ref.as_class().is_some()) + }); + if let Some(const_borrow_arg) = const_borrow_arg { + *const_borrow_arg = const_borrow_arg.with_type_ref( + const_borrow_arg + .type_ref() + .map_ptr_ref(|inner| inner.with_inherent_constness(Constness::Mut)), + ); + if let Some(args) = cloned_args { + desc_mut.arguments = args.into(); + } + desc_mut.rust_custom_leafname = Some(format!("{}_mut", f.rust_leafname(FishStyle::No)).into()); + desc_mut.return_type_ref = desc_mut.return_type_ref.with_inherent_constness(Constness::Mut); + Some(Func::Desc(desc)) + } else { + None + } + } else { + None } } diff --git a/binding-generator/src/writer/rust_native/lifetime.rs b/binding-generator/src/writer/rust_native/lifetime.rs new file mode 100644 index 000000000..e58f6c7c5 --- /dev/null +++ b/binding-generator/src/writer/rust_native/lifetime.rs @@ -0,0 +1,107 @@ +use std::fmt; +use std::fmt::Write; + +/// Represents a Rust lifetime. Call `next()` to get the next lifetime (supported for Automatic and Elided lifetimes). +/// +/// Also implements `Display` to render the lifetime as a string. The implementation supports `align` and `fill` formatting options. +/// The `align` option can have any direction, and `fill` can meaningfully be only "," or " ". The former additionally renders ", " +/// after the lifetime, and the latter renders " ". +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Lifetime { + /// Lifetime is determined by Rust automatically + Elided, + /// Custom lifetime name (without the apostrophe) + Custom(&'static str), + /// Automatic lifetime, starting with `'a` and incrementing for each `next()` lifetime + Automatic(u8), +} + +impl Lifetime { + pub fn automatic() -> Self { + Self::Automatic(0) + } + + pub fn statik() -> Self { + Self::Custom("static") + } + + pub fn is_elided(&self) -> bool { + match self { + Lifetime::Elided => true, + Lifetime::Custom(_) | Lifetime::Automatic(_) => false, + } + } + + pub fn is_explicit(&self) -> bool { + match self { + Lifetime::Elided => false, + Lifetime::Custom(_) | Lifetime::Automatic(_) => true, + } + } + + pub fn next(self) -> Option { + match self { + Self::Elided => Some(Self::Elided), + Self::Custom(_) => None, + Self::Automatic(n) if n >= 25 => None, + Self::Automatic(n) => Some(Self::Automatic(n + 1)), + } + } +} + +impl IntoIterator for Lifetime { + type Item = Lifetime; + type IntoIter = LifetimeIterator; + + fn into_iter(self) -> LifetimeIterator { + LifetimeIterator { + cur_lifetime: Some(self), + } + } +} + +impl fmt::Display for Lifetime { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + #[inline] + fn write_align(f: &mut fmt::Formatter) -> fmt::Result { + if f.align().is_some() { + match f.fill() { + ',' => f.write_str(", ")?, + ' ' => f.write_char(' ')?, + _ => {} + } + } + Ok(()) + } + match *self { + Self::Elided => Ok(()), + Self::Custom(name) => { + f.write_char('\'')?; + f.write_str(name)?; + write_align(f) + } + Self::Automatic(n) if n > 25 => { + panic!("Too many lifetimes") + } + Self::Automatic(n) => { + f.write_char('\'')?; + f.write_char(char::from(b'a' + n))?; + write_align(f) + } + } + } +} + +pub struct LifetimeIterator { + cur_lifetime: Option, +} + +impl Iterator for LifetimeIterator { + type Item = Lifetime; + + fn next(&mut self) -> Option { + let out = self.cur_lifetime; + self.cur_lifetime = self.cur_lifetime.and_then(|l| l.next()); + out + } +} diff --git a/binding-generator/src/writer/rust_native/mod.rs b/binding-generator/src/writer/rust_native/mod.rs index e2fd95cd4..31716df5d 100644 --- a/binding-generator/src/writer/rust_native/mod.rs +++ b/binding-generator/src/writer/rust_native/mod.rs @@ -32,6 +32,7 @@ mod enumeration; mod field; mod func; mod function; +mod lifetime; pub mod renderer; mod smart_ptr; mod string_ext; diff --git a/binding-generator/src/writer/rust_native/renderer.rs b/binding-generator/src/writer/rust_native/renderer.rs index 0760da403..8b9f29bc2 100644 --- a/binding-generator/src/writer/rust_native/renderer.rs +++ b/binding-generator/src/writer/rust_native/renderer.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use std::fmt::Write; use crate::renderer::TypeRefRenderer; -use crate::type_ref::{CppNameStyle, FishStyle, NameStyle, TemplateArg, TypeRef, TypeRefKind}; +use crate::type_ref::{CppNameStyle, FishStyle, NameStyle, TemplateArg, TypeRef, TypeRefKind, TypeRefTypeHint}; use crate::writer::rust_native::element::RustElement; use crate::writer::rust_native::type_ref::TypeRefExt; use crate::{settings, Element}; @@ -19,7 +19,7 @@ fn render_rust_tpl<'a>(renderer: impl TypeRefRenderer<'a>, type_ref: &TypeRef, f let generic_types = generic_types .iter() .filter_map(|t| match t { - TemplateArg::Typename(type_ref) => Some(type_ref.render(renderer.recurse())), + TemplateArg::Typename(type_ref) => Some(type_ref.render(renderer.recursed())), TemplateArg::Constant(literal) => { if const_generics_implemented { Some(literal.into()) @@ -47,20 +47,22 @@ pub struct RustRenderer { pub name_style: NameStyle, pub lifetime: Lifetime, pub rust_by_ptr: bool, + pub type_ref_type_hint: TypeRefTypeHint, } impl RustRenderer { - pub fn new(name_style: NameStyle, lifetime: Lifetime, rust_by_ptr: bool) -> Self { + pub fn new(name_style: NameStyle, lifetime: Lifetime, rust_by_ptr: bool, type_ref_type_hint: TypeRefTypeHint) -> Self { Self { name_style, lifetime, rust_by_ptr, + type_ref_type_hint, } } - fn wrap_nullable<'a>(&self, type_ref: &TypeRef, typ: Cow<'a, str>) -> Cow<'a, str> { + fn wrap_nullable<'a>(type_ref: &TypeRef, typ: Cow<'a, str>, name_style: NameStyle) -> Cow<'a, str> { if type_ref.is_nullable() { - format!("Option{fish}<{typ}>", fish = self.name_style.rust_turbo_fish_qual()).into() + format!("Option{fish}<{typ}>", fish = name_style.rust_turbo_fish_qual()).into() } else { typ } @@ -86,8 +88,9 @@ impl TypeRefRenderer<'_> for RustRenderer { match type_ref.kind().into_owned() { TypeRefKind::Primitive(rust, _) => rust.into(), TypeRefKind::Array(elem, size) => { + let name_style = self.name_style; let typ = type_ref.format_as_array(&elem.render(self.recurse()), size); - self.wrap_nullable(type_ref, typ.into()) + Self::wrap_nullable(type_ref, typ.into(), name_style) } TypeRefKind::StdVector(vec) => vec.rust_name(self.name_style).into_owned().into(), TypeRefKind::StdTuple(tuple) => tuple.rust_name(self.name_style).into_owned().into(), @@ -108,27 +111,41 @@ impl TypeRefRenderer<'_> for RustRenderer { format!("*{cnst}{typ}", cnst = type_ref.constness().rust_qual_ptr()).into() } TypeRefKind::Pointer(inner) | TypeRefKind::Reference(inner) => { + let lt = self.lifetime; + let name_style = self.name_style; let typ = format!( "&{lt: <}{cnst}{typ}", cnst = type_ref.constness().rust_qual(), - lt = self.lifetime, typ = inner.render(self.recurse()) ); - self.wrap_nullable(type_ref, typ.into()) + Self::wrap_nullable(type_ref, typ.into(), name_style) } TypeRefKind::RValueReference(inner) => inner.render(self.recurse()).into_owned().into(), TypeRefKind::SmartPtr(ptr) => { let typ = ptr.rust_name(self.name_style).into_owned(); - self.wrap_nullable(type_ref, typ.into()) + Self::wrap_nullable(type_ref, typ.into(), self.name_style) } TypeRefKind::Class(cls) => { let fish_style = self.name_style.turbo_fish_style(); - format!( - "{name}{generic}", - name = cls.rust_name(self.name_style), - generic = render_rust_tpl(self, type_ref, fish_style), - ) - .into() + let name = if let TypeRefTypeHint::BoxedAsRef(_, lt) = self.type_ref_type_hint { + if cls.kind().is_boxed() { + Some( + format!( + "BoxedRef{mut_suf}{fish}<{lt:,<}{name}>", + mut_suf = type_ref.constness().rust_name_qual(), + fish = fish_style.rust_qual(), + name = cls.rust_name(NameStyle::ref_()) + ) + .into(), + ) + } else { + None + } + } else { + None + } + .unwrap_or_else(|| cls.rust_name(self.name_style)); + format!("{name}{generic}", generic = render_rust_tpl(self, type_ref, fish_style)).into() } TypeRefKind::Enum(enm) => enm.rust_name(self.name_style).into_owned().into(), TypeRefKind::Typedef(decl) => { @@ -145,9 +162,17 @@ impl TypeRefRenderer<'_> for RustRenderer { } } - fn recurse(&self) -> Self { + fn recurse(self) -> Self::Recursed { + Self { + lifetime: Lifetime::Elided, + ..self + } + } + + fn recursed(&self) -> Self::Recursed { Self { - lifetime: self.lifetime.next().expect("Too many lifetimes"), + lifetime: Lifetime::Elided, + type_ref_type_hint: self.type_ref_type_hint.clone(), ..*self } } diff --git a/binding-generator/src/writer/rust_native/type_ref.rs b/binding-generator/src/writer/rust_native/type_ref.rs index 642bc039a..9d3f4966f 100644 --- a/binding-generator/src/writer/rust_native/type_ref.rs +++ b/binding-generator/src/writer/rust_native/type_ref.rs @@ -1,11 +1,10 @@ use std::borrow::Cow; -use std::fmt; -use std::fmt::Write; use crate::type_ref::{ Constness, Dir, ExternDir, FishStyle, NameStyle, StrEnc, StrType, TypeRef, TypeRefDesc, TypeRefKind, TypeRefTypeHint, }; use crate::writer::rust_native::class::ClassExt; +pub use crate::writer::rust_native::lifetime::{Lifetime, LifetimeIterator}; use crate::{IteratorExt, StringExt}; use super::element::RustElement; @@ -26,7 +25,7 @@ pub trait TypeRefExt { fn rust_name_ext(&self, name_style: NameStyle, lifetime: Lifetime) -> Cow; fn rust_self_func_decl(&self, method_constness: Constness) -> String; - fn rust_arg_func_decl(&self, name: &str) -> String; + fn rust_arg_func_decl(&self, name: &str, lifetime: Lifetime) -> String; fn rust_extern_self_func_decl(&self, method_constness: Constness) -> String; fn rust_extern_arg_func_decl(&self, name: &str) -> String; fn rust_arg_pre_call(&self, name: &str, is_function_infallible: bool) -> String; @@ -123,11 +122,16 @@ impl TypeRefExt for TypeRef<'_, '_> { } fn rust_name(&self, name_style: NameStyle) -> Cow { - self.rust_name_ext(name_style, Lifetime::elided()) + self.rust_name_ext(name_style, Lifetime::Elided) } fn rust_name_ext(&self, name_style: NameStyle, lifetime: Lifetime) -> Cow { - self.render(RustRenderer::new(name_style, lifetime, self.is_rust_by_ptr())) + self.render(RustRenderer::new( + name_style, + lifetime, + self.is_rust_by_ptr(), + self.type_hint().clone(), + )) } fn rust_self_func_decl(&self, method_constness: Constness) -> String { @@ -142,7 +146,7 @@ impl TypeRefExt for TypeRef<'_, '_> { } } - fn rust_arg_func_decl(&self, name: &str) -> String { + fn rust_arg_func_decl(&self, name: &str, lifetime: Lifetime) -> String { let typ = if let Some(dir) = self.as_string() { match dir { Dir::In(StrType::StdString(StrEnc::Text) | StrType::CvString(StrEnc::Text) | StrType::CharPtr) => "&str".into(), @@ -177,7 +181,7 @@ impl TypeRefExt for TypeRef<'_, '_> { } else if self.is_rust_char() { "char".into() } else { - self.rust_name(NameStyle::ref_()) + self.rust_name_ext(NameStyle::ref_(), lifetime) }; let cnst = Constness::from_is_mut( self.is_by_move() @@ -446,6 +450,17 @@ impl TypeRefExt for TypeRef<'_, '_> { 0 } } + TypeRefKind::Class(cls) if cls.kind().is_boxed() => { + if let TypeRefTypeHint::BoxedAsRef(_, lt) = self.type_hint() { + if lt.is_explicit() { + 1 + } else { + 0 + } + } else { + 0 + } + } TypeRefKind::Typedef(tdef) => tdef.underlying_type_ref().rust_lifetime_count(), _ => 0, } @@ -550,97 +565,3 @@ impl TypeRefExt for TypeRef<'_, '_> { "".to_string() } } - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum Lifetime { - Elided, - Static, - Explicit(u8), -} - -impl Lifetime { - pub fn elided() -> Self { - Self::Elided - } - - pub fn statik() -> Self { - Self::Static - } - - pub fn explicit() -> Self { - Self::Explicit(0) - } - - pub fn is_elided(&self) -> bool { - matches!(self, Self::Elided) - } - - pub fn is_explicit(&self) -> bool { - matches!(self, Self::Static | Self::Explicit(_)) - } - - pub fn next(self) -> Option { - match self { - Self::Elided => Some(Self::Elided), - Self::Static => Some(Self::Static), - Self::Explicit(n) if n >= 25 => None, - Self::Explicit(n) => Some(Self::Explicit(n + 1)), - } - } -} - -impl IntoIterator for Lifetime { - type Item = Lifetime; - type IntoIter = LifetimeIterator; - - fn into_iter(self) -> LifetimeIterator { - LifetimeIterator { - cur_lifetime: Some(self), - } - } -} - -impl fmt::Display for Lifetime { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - #[inline] - fn write_align(f: &mut fmt::Formatter) -> fmt::Result { - if f.align().is_some() { - match f.fill() { - ',' => f.write_str(", ")?, - ' ' => f.write_char(' ')?, - _ => {} - } - } - Ok(()) - } - match *self { - Self::Elided => Ok(()), - Self::Static => { - f.write_str("'static")?; - write_align(f) - } - Self::Explicit(n) if n > 25 => { - panic!("Too many lifetimes") - } - Self::Explicit(n) => { - f.write_char('\'')?; - f.write_char(char::from(b'a' + n))?; - write_align(f) - } - } - } -} - -pub struct LifetimeIterator { - cur_lifetime: Option, -} - -impl Iterator for LifetimeIterator { - type Item = Lifetime; - - fn next(&mut self) -> Option { - let out = self.cur_lifetime; - self.cur_lifetime = self.cur_lifetime.and_then(|l| l.next()); - out - } -} diff --git a/binding-generator/src/writer/rust_native/typedef.rs b/binding-generator/src/writer/rust_native/typedef.rs index c5cf963bc..88e247c55 100644 --- a/binding-generator/src/writer/rust_native/typedef.rs +++ b/binding-generator/src/writer/rust_native/typedef.rs @@ -44,7 +44,7 @@ impl RustNativeGeneratedElement for Typedef<'_, '_> { fn gen_rust(&self, opencv_version: &str) -> String { static TPL: Lazy = Lazy::new(|| include_str!("tpl/typedef/tpl.rs").compile_interpolation()); let underlying_type = self.underlying_type_ref(); - let lifetimes = Lifetime::explicit() + let lifetimes = Lifetime::automatic() .into_iter() .take(underlying_type.rust_lifetime_count()) .map(|l| l.to_string()) @@ -62,7 +62,7 @@ impl RustNativeGeneratedElement for Typedef<'_, '_> { ("generic_args", generic_args.into()), ( "definition", - underlying_type.rust_name_ext(NameStyle::ref_(), Lifetime::explicit()), + underlying_type.rust_name_ext(NameStyle::ref_(), Lifetime::automatic()), ), ])) } diff --git a/examples/discrete_fourier_transform.rs b/examples/discrete_fourier_transform.rs index 143f432c1..a2a3f07ef 100644 --- a/examples/discrete_fourier_transform.rs +++ b/examples/discrete_fourier_transform.rs @@ -38,20 +38,27 @@ fn main() -> opencv::Result<()> { magI = magI_tmp; let mut magI_log = Mat::default(); core::log(&magI, &mut magI_log)?; - magI = Mat::roi(&magI_log, Rect::new(0, 0, magI_log.cols() & -2, magI_log.rows() & -2))?; + let magI_log_rect = Rect::new(0, 0, magI_log.cols() & -2, magI_log.rows() & -2); + let mut magI = Mat::roi_mut(&mut magI_log, magI_log_rect)?; let cx = magI.cols() / 2; let cy = magI.rows() / 2; - let mut q0 = Mat::roi(&magI, Rect::new(0, 0, cx, cy))?; - let mut q1 = Mat::roi(&magI, Rect::new(cx, 0, cx, cy))?; - let mut q2 = Mat::roi(&magI, Rect::new(0, cy, cx, cy))?; - let mut q3 = Mat::roi(&magI, Rect::new(cx, cy, cx, cy))?; - let mut tmp = Mat::default(); - q0.copy_to(&mut tmp)?; - q3.copy_to(&mut q0)?; - tmp.copy_to(&mut q3)?; - q1.copy_to(&mut tmp)?; - q2.copy_to(&mut q1)?; - tmp.copy_to(&mut q2)?; + let q0_rect = Rect::new(0, 0, cx, cy); + let q3_rect = Rect::new(cx, cy, cx, cy); + let mut tmp_0 = Mat::default(); + let mut tmp_1 = Mat::default(); + + Mat::roi(&magI, q0_rect)?.copy_to(&mut tmp_0)?; + Mat::roi(&magI, q3_rect)?.copy_to(&mut tmp_1)?; + tmp_0.copy_to(&mut Mat::roi_mut(&mut magI, q3_rect)?)?; + tmp_1.copy_to(&mut Mat::roi_mut(&mut magI, q0_rect)?)?; + + let q1_rect = Rect::new(cx, 0, cx, cy); + let q2_rect = Rect::new(0, cy, cx, cy); + Mat::roi(&magI, q1_rect)?.copy_to(&mut tmp_0)?; + Mat::roi(&magI, q2_rect)?.copy_to(&mut tmp_1)?; + tmp_0.copy_to(&mut Mat::roi_mut(&mut magI, q2_rect)?)?; + tmp_1.copy_to(&mut Mat::roi_mut(&mut magI, q1_rect)?)?; + let mut magI_tmp = Mat::default(); core::normalize(&magI, &mut magI_tmp, 0., 1., core::NORM_MINMAX, -1, &core::no_array())?; let magI = magI_tmp; diff --git a/src/lib.rs b/src/lib.rs index 7b3ca2977..bcb971879 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,15 +38,13 @@ pub(crate) mod mod_prelude_sys { /// Prelude for generated modules and types pub(crate) mod mod_prelude { + pub use crate::core::{BoxedRef, BoxedRefMut, CV_MAKETYPE, CV_MAKE_TYPE}; + pub use crate::hub_prelude::*; + pub use crate::mod_prelude_sys::*; pub use crate::{ - boxed_cast_base, boxed_cast_descendant, - core::{CV_MAKETYPE, CV_MAKE_TYPE}, - extern_arg_send, extern_container_send, extern_receive, extern_send, - hub_prelude::*, - input_array_ref_forward, - mod_prelude_sys::*, - opencv_type_boxed, opencv_type_enum, opencv_type_simple, output_array_ref_forward, ptr_cast_base, ptr_extern, - ptr_extern_ctor, tuple_extern, vector_copy_non_bool, vector_extern, vector_non_copy_or_bool, Error, Result, + boxed_cast_base, boxed_cast_descendant, extern_arg_send, extern_container_send, extern_receive, extern_send, + input_array_ref_forward, opencv_type_boxed, opencv_type_enum, opencv_type_simple, output_array_ref_forward, ptr_cast_base, + ptr_extern, ptr_extern_ctor, tuple_extern, vector_copy_non_bool, vector_extern, vector_non_copy_or_bool, Error, Result, }; } diff --git a/src/manual/core.rs b/src/manual/core.rs index 8e0a50df6..3a322c1a8 100644 --- a/src/manual/core.rs +++ b/src/manual/core.rs @@ -1,4 +1,5 @@ pub use affine3::*; +pub use boxed_ref::*; pub use data_type::*; pub use gpumat::*; pub use input_output_array::*; @@ -18,6 +19,7 @@ pub use vector::*; pub use CV_MAKETYPE as CV_MAKE_TYPE; mod affine3; +mod boxed_ref; mod data_type; mod gpumat; mod input_output_array; diff --git a/src/manual/core/boxed_ref.rs b/src/manual/core/boxed_ref.rs new file mode 100644 index 000000000..df79d5e5f --- /dev/null +++ b/src/manual/core/boxed_ref.rs @@ -0,0 +1,147 @@ +use std::fmt; +use std::marker::PhantomData; +use std::ops::{Deref, DerefMut}; + +use crate::core::{ToInputArray, ToInputOutputArray, ToOutputArray, _InputArray, _InputOutputArray, _OutputArray}; +use crate::traits::{Boxed, OpenCVType, OpenCVTypeArg}; +use crate::Result; + +pub struct BoxedRef<'r, T: Boxed> { + reference: T, + // can make it &'r T, but then it will just take up unnecessary space + referenced_object: PhantomData<&'r T>, +} + +impl<'r, T: Boxed> BoxedRef<'r, T> { + /// Create a new wrapped reference to a `Boxed` type + #[inline] + pub fn new(referenced_object: &'r T, reference: T) -> Self { + // don't expose the fact that we don't actually use the `referenced_object` argument, only a lifetime from it + let _referenced_object = referenced_object; + Self { + reference, + referenced_object: PhantomData, + } + } +} + +impl Deref for BoxedRef<'_, T> { + type Target = T; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.reference + } +} + +impl fmt::Debug for BoxedRef<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&self.reference, f) + } +} + +impl<'t, T: OpenCVTypeArg<'t> + Boxed> OpenCVTypeArg<'t> for BoxedRef<'_, T> { + type ExternContainer = T::ExternContainer; + + fn opencv_into_extern_container_nofail(self) -> Self::ExternContainer { + self.reference.opencv_into_extern_container_nofail() + } +} + +impl<'t, T: OpenCVType<'t> + Boxed> OpenCVType<'t> for BoxedRef<'_, T> { + type Arg = T::Arg; + type ExternReceive = T::ExternReceive; + + #[inline] + unsafe fn opencv_from_extern(s: Self::ExternReceive) -> Self { + Self { + reference: T::opencv_from_extern(s), + referenced_object: PhantomData, + } + } +} + +impl ToInputArray for BoxedRef<'_, T> { + fn input_array(&self) -> Result<_InputArray> { + self.reference.input_array() + } +} + +pub struct BoxedRefMut<'r, T: Boxed> { + reference: T, + referenced_object: PhantomData<&'r mut T>, +} + +impl Deref for BoxedRefMut<'_, T> { + type Target = T; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.reference + } +} + +impl DerefMut for BoxedRefMut<'_, T> { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.reference + } +} + +impl<'r, T: Boxed> BoxedRefMut<'r, T> { + /// Create a new wrapped mutable reference to a `Boxed` type + #[inline] + pub fn new(referenced_object: &'r mut T, reference: T) -> Self { + // don't expose the fact that we don't actually use the `referenced_object` argument, only a lifetime from it + let _referenced_object = referenced_object; + Self { + reference, + referenced_object: PhantomData, + } + } +} + +impl fmt::Debug for BoxedRefMut<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&self.reference, f) + } +} + +impl<'t, T: OpenCVTypeArg<'t> + Boxed> OpenCVTypeArg<'t> for BoxedRefMut<'_, T> { + type ExternContainer = T::ExternContainer; + + fn opencv_into_extern_container_nofail(self) -> Self::ExternContainer { + self.reference.opencv_into_extern_container_nofail() + } +} + +impl<'t, T: OpenCVType<'t> + Boxed> OpenCVType<'t> for BoxedRefMut<'_, T> { + type Arg = T::Arg; + type ExternReceive = T::ExternReceive; + + #[inline] + unsafe fn opencv_from_extern(s: Self::ExternReceive) -> Self { + Self { + reference: T::opencv_from_extern(s), + referenced_object: PhantomData, + } + } +} + +impl ToInputArray for BoxedRefMut<'_, T> { + fn input_array(&self) -> Result<_InputArray> { + self.reference.input_array() + } +} + +impl ToInputOutputArray for BoxedRefMut<'_, T> { + fn input_output_array(&mut self) -> Result<_InputOutputArray> { + self.reference.input_output_array() + } +} + +impl ToOutputArray for BoxedRefMut<'_, T> { + fn output_array(&mut self) -> Result<_OutputArray> { + self.reference.output_array() + } +} diff --git a/src/manual/core/mat.rs b/src/manual/core/mat.rs index 9a0477584..23be59c75 100644 --- a/src/manual/core/mat.rs +++ b/src/manual/core/mat.rs @@ -6,7 +6,7 @@ use std::{fmt, ptr, slice}; pub use mat_::*; -use crate::core::{MatConstIterator, MatExpr, MatSize, Point, Scalar, UMat}; +use crate::core::{BoxedRefMut, MatConstIterator, MatExpr, MatSize, Point, Rect, Scalar, UMat}; use crate::prelude::*; use crate::{core, input_output_array, Error, Result}; @@ -538,6 +538,21 @@ pub trait MatTraitManual: MatTraitConstManual + MatTrait { } } +impl Mat { + /// Returns 2 mutable ROIs into a single `Mat` as long as they do not intersect + pub fn roi_2_mut(m: &mut Mat, roi1: Rect, roi2: Rect) -> Result<(BoxedRefMut, BoxedRefMut)> { + if (roi1 & roi2).empty() { + // safe because we made sure that the interest areas do not intersect + let m2 = unsafe { (m as *mut Mat).as_mut().expect("Can't fail") }; + let out1 = Mat::roi_mut(m, roi1)?; + let out2 = Mat::roi_mut(m2, roi2)?; + Ok((out1, out2)) + } else { + return Err(Error::new(core::StsBadArg, "ROIs must not intersect")); + } + } +} + impl MatTraitConstManual for T {} impl MatTraitManual for T {} diff --git a/tests/mat.rs b/tests/mat.rs index 8ba21620d..81889cf0d 100644 --- a/tests/mat.rs +++ b/tests/mat.rs @@ -1,4 +1,5 @@ use std::ffi::c_void; +use std::mem; use matches::assert_matches; @@ -469,7 +470,7 @@ fn mat_vec() -> Result<()> { fn mat_continuous() -> Result<()> { let s: Vec> = vec![vec![1., 2., 3.], vec![4., 5., 6.], vec![7., 8., 9.]]; - let mat = Mat::from_slice_2d(&s)?; + let mut mat = Mat::from_slice_2d(&s)?; { let sub_mat_non_cont = Mat::roi(&mat, Rect::new(1, 1, 2, 2))?; @@ -521,7 +522,7 @@ fn mat_continuous() -> Result<()> { } { - let mut sub_mat_non_cont = Mat::roi(&mat, Rect::new(1, 1, 1, 2))?; + let mut sub_mat_non_cont = Mat::roi_mut(&mut mat, Rect::new(1, 1, 1, 2))?; assert!(!sub_mat_non_cont.is_continuous()); assert_matches!( sub_mat_non_cont.data_typed::(), @@ -735,6 +736,33 @@ fn mat_locate_roi() -> Result<()> { Ok(()) } +#[test] +fn mat_roi() -> Result<()> { + let mut mat = Mat::from_slice(&[1, 2, 3, 4])?; + let (mut roi1, mut roi2) = Mat::roi_2_mut(&mut mat, Rect::new(0, 0, 2, 1), Rect::new(2, 0, 2, 1))?; + assert_eq!(1, *roi1.at(0)?); + assert_eq!(2, *roi1.at(1)?); + assert_eq!(3, *roi2.at(0)?); + assert_eq!(4, *roi2.at(1)?); + + mem::swap(roi1.at_mut::(0)?, roi2.at_mut::(0)?); + mem::swap(roi1.at_mut::(1)?, roi2.at_mut::(1)?); + assert_eq!(3, *mat.at(0)?); + assert_eq!(4, *mat.at(1)?); + assert_eq!(1, *mat.at(2)?); + assert_eq!(2, *mat.at(3)?); + + assert_matches!( + Mat::roi_2_mut(&mut mat, Rect::new(0, 0, 3, 1), Rect::new(2, 0, 2, 1)), + Err(Error { + code: core::StsBadArg, + .. + }) + ); + + Ok(()) +} + #[test] fn mat_convert() -> Result<()> { let mat = Mat::from_slice(&[1, 2, 3, 4])?;