Implement casting to base classes and back
MODIFY
binding-generator/src/class.rs
binding-generator/src/class.rs
@@ -26,6 +26,7 @@ use crate::{
StrExt,
TypeRef,
};
+use crate::return_type_wrapper::ReturnTypeWrapper;
#[derive(Clone, Copy, Debug, PartialOrd, PartialEq)]
pub enum Kind {
@@ -109,6 +110,10 @@ impl<'tu, 'ge> Class<'tu, 'ge> {
// self.entity.walk_methods_while(|child| !Func::new(child, self.gen_env).is_abstract())
}
+ pub fn is_polymorphic(&self) -> bool {
+ self.entity.walk_methods_while(|f| !f.is_virtual_method())
+ }
+
pub fn is_trait(&self) -> bool {
self.is_boxed()
// self.is_abstract() || self.has_descendants() || settings::FORCE_CLASS_TRAIT.contains(self.cpp_fullname().as_ref())
@@ -170,6 +175,13 @@ impl<'tu, 'ge> Class<'tu, 'ge> {
.collect()
}
+ pub fn descendants(&self) -> impl Iterator<Item=Class<'tu, 'ge>> {
+ let gen_env = self.gen_env;
+ gen_env.descendants.get(self.cpp_fullname().as_ref()).into_iter()
+ .map(move |desc| desc.iter().map(move |e| Class::new(*e, gen_env)))
+ .flatten()
+ }
+
pub fn has_methods(&self) -> bool {
self.entity.walk_methods_while(|_| false)
}
@@ -240,7 +252,7 @@ impl<'tu, 'ge> Class<'tu, 'ge> {
}
pub fn dependent_types(&self) -> Vec<DependentType<'tu, 'ge>> {
- self.fields().into_iter()
+ let dep_types = self.fields().into_iter()
.filter(|f| !f.is_excluded())
.map(|f| f.type_ref().dependent_types(DependentTypeMode::ForReturn(DefinitionLocation::Module)))
.flatten()
@@ -248,8 +260,20 @@ impl<'tu, 'ge> Class<'tu, 'ge> {
.filter(|m| !m.is_excluded())
.map(|m| m.dependent_types())
.flatten()
+ );
+ if !self.is_simple() {
+ dep_types.chain(self.descendants().into_iter()
+ .filter(|b| !b.is_excluded() && !b.is_simple() && !b.is_abstract())
+ .map(|b| DependentType::from_return_type_wrapper(ReturnTypeWrapper::new(
+ b.type_ref(),
+ DefinitionLocation::Type,
+ self.gen_env,
+ )))
)
.collect()
+ } else {
+ dep_types.collect()
+ }
}
pub fn field_methods<'f>(&self, fields: impl Iterator<Item=&'f Field<'tu, 'ge>>) -> Vec<Func<'tu, 'ge>> where 'tu: 'ge, 'ge: 'f {
@@ -391,6 +415,9 @@ impl fmt::Debug for Class<'_, '_> {
if self.is_trait() {
props.push("trait");
}
+ if self.is_polymorphic() {
+ props.push("polymorphic");
+ }
if self.is_by_ptr() {
props.push("by_ptr");
}

MODIFY
binding-generator/src/generator_env.rs
binding-generator/src/generator_env.rs
@@ -111,6 +111,12 @@ impl<'tu> EntityWalkerVisitor<'tu> for DbPopulator<'tu, '_> {
EntityKind::ClassDecl | EntityKind::StructDecl => {
entity.visit_children(|c, _| {
match c.get_kind() {
+ EntityKind::BaseSpecifier => {
+ let c_decl = c.get_definition().expect("Can't get base class definition");
+ self.gen_env.descendants.entry(c_decl.cpp_fullname().into_owned())
+ .or_insert_with(|| HashSet::with_capacity(4))
+ .insert(entity);
+ }
EntityKind::Constructor | EntityKind::Method | EntityKind::FunctionTemplate
| EntityKind::ConversionFunction => {
self.add_func_comment(c);
@@ -148,6 +154,7 @@ pub struct GeneratorEnv<'tu> {
class_constants: HashMap<String, Const<'tu>>,
type_resolve_cache: HashMap<String, Type<'tu>>,
used_in_smart_ptr: HashSet<Entity<'tu>>,
+ pub descendants: HashMap<String, HashSet<Entity<'tu>>>,
}
impl<'tu> GeneratorEnv<'tu> {
@@ -162,6 +169,7 @@ impl<'tu> GeneratorEnv<'tu> {
class_constants: HashMap::with_capacity(32),
type_resolve_cache: HashMap::with_capacity(32),
used_in_smart_ptr: HashSet::with_capacity(32),
+ descendants: HashMap::with_capacity(16),
};
let walker = EntityWalker::new(root_entity);
walker.walk_opencv_entities(DbPopulator { gen_env: &mut out });

MODIFY
binding-generator/src/writer/rust_native/class.rs
binding-generator/src/writer/rust_native/class.rs
@@ -44,6 +44,14 @@ fn gen_rust_class(c: &Class, opencv_version: &str) -> String {
|| include_str!("tpl/class/base.tpl.rs").compile_interpolation()
);
+ static BASE_CAST_TPL: Lazy<CompiledInterpolation> = Lazy::new(
+ || include_str!("tpl/class/base_cast.tpl.rs").compile_interpolation()
+ );
+
+ static DESCENDANT_CAST_TPL: Lazy<CompiledInterpolation> = Lazy::new(
+ || include_str!("tpl/class/descendant_cast.tpl.rs").compile_interpolation()
+ );
+
static SIMPLE_BASE_TPL: Lazy<CompiledInterpolation> = Lazy::new(
|| include_str!("tpl/class/simple_base.tpl.rs").compile_interpolation()
);
@@ -120,13 +128,51 @@ fn gen_rust_class(c: &Class, opencv_version: &str) -> String {
}
if !is_abstract {
+ let rust_local = c.rust_localname();
+ let mut impls = if methods.iter().any(|m| m.is_clone()) {
+ IMPL_CLONE_TPL.interpolate(&hashmap! {
+ "rust_local" => rust_local.as_ref(),
+ })
+ } else {
+ "".to_string()
+ };
+
let mut bases = c.all_bases().into_iter()
.filter(|b| !b.is_excluded() && !b.is_simple()) // todo, allow extension of simple classes for e.g. Elliptic_KeyPoint
.collect::<Vec<_>>();
+ bases.sort_unstable_by(|a, b| a.cpp_localname().cmp(&b.cpp_localname()));
+ if !is_simple {
+ if c.is_polymorphic() {
+ let mut descendants = c.descendants()
+ .filter(|d| !d.is_excluded() && !d.is_simple() && !d.is_abstract())
+ .collect::<Vec<_>>();
+ descendants.sort_unstable_by(|a, b| a.cpp_localname().cmp(&b.cpp_localname()));
+ for d in descendants {
+ let desc_local = d.rust_localname();
+ let desc_full = d.rust_fullname();
+ impls += &DESCENDANT_CAST_TPL.interpolate(&hashmap! {
+ "rust_local" => rust_local.as_ref(),
+ "descendant_rust_local" => desc_local.as_ref(),
+ "descendant_rust_full" => desc_full.as_ref(),
+ });
+ }
+ }
+ for b in &bases {
+ if !b.is_abstract() {
+ let base_local = b.rust_localname();
+ let base_full = b.rust_fullname();
+ impls += &BASE_CAST_TPL.interpolate(&hashmap! {
+ "rust_local" => rust_local.as_ref(),
+ "base_rust_local" => base_local.as_ref(),
+ "base_rust_full" => base_full.as_ref(),
+ });
+ }
+ }
+ }
+
if is_trait {
bases.push(c.clone());
}
- bases.sort_unstable_by(|a, b| a.cpp_localname().cmp(&b.cpp_localname()));
let bases = bases.into_iter()
.map(|base| {
let base_type_ref = base.type_ref();
@@ -201,15 +247,6 @@ fn gen_rust_class(c: &Class, opencv_version: &str) -> String {
.map(|c| c.gen_rust(opencv_version))
.join("");
- let rust_local = c.rust_localname();
- let impls = if methods.iter().any(|m| m.is_clone()) {
- IMPL_CLONE_TPL.interpolate(&hashmap! {
- "rust_local" => rust_local.clone(),
- })
- } else {
- "".to_string()
- };
-
out += &tpl.interpolate(&hashmap! {
"doc_comment" => Cow::Owned(c.rendered_doc_comment(opencv_version)),
"debug" => get_debug(c).into(),
@@ -243,7 +280,15 @@ fn gen_rust_exports_boxed(c: &Class) -> String {
fn gen_cpp_boxed(c: &Class) -> String {
static BOXED_CPP_TPL: Lazy<CompiledInterpolation> = Lazy::new(
- || include_str!("tpl/class/boxed_dtor.tpl.cpp").compile_interpolation()
+ || include_str!("tpl/class/boxed.tpl.cpp").compile_interpolation()
+ );
+
+ static DESCENDANT_CAST_TPL: Lazy<CompiledInterpolation> = Lazy::new(
+ || include_str!("tpl/class/descendant_cast.tpl.cpp").compile_interpolation()
+ );
+
+ static BASE_CAST_TPL: Lazy<CompiledInterpolation> = Lazy::new(
+ || include_str!("tpl/class/base_cast.tpl.cpp").compile_interpolation()
);
let fields = c.fields();
@@ -254,12 +299,49 @@ fn gen_cpp_boxed(c: &Class) -> String {
}
}
+ let mut casts = String::new();
if !c.is_abstract() {
+ let rust_local = c.rust_localname();
+ let mut bases = c.all_bases().into_iter()
+ .filter(|b| !b.is_excluded() && !b.is_simple() && !b.is_abstract())
+ .collect::<Vec<_>>();
+ bases.sort_unstable_by(|a, b| a.cpp_localname().cmp(&b.cpp_localname()));
+ if !c.is_simple() {
+ let cpp_decl = c.type_ref().cpp_arg_func_decl("instance");
+ if c.is_polymorphic() {
+ let mut descendants = c.descendants()
+ .filter(|d| !d.is_excluded() && !d.is_simple() && !d.is_abstract())
+ .collect::<Vec<_>>();
+ descendants.sort_unstable_by(|a, b| a.cpp_localname().cmp(&b.cpp_localname()));
+ for d in descendants {
+ let desc_rust_local = d.rust_localname();
+ let desc_cpp_full = d.cpp_fullname();
+ casts += &DESCENDANT_CAST_TPL.interpolate(&hashmap! {
+ "rust_local" => rust_local.as_ref(),
+ "descendant_rust_local" => desc_rust_local.as_ref(),
+ "descendant_cpp_full" => desc_cpp_full.as_ref(),
+ "cpp_decl" => &cpp_decl,
+ });
+ }
+ }
+ for b in bases {
+ let base_rust_local = b.rust_localname();
+ let base_cpp_full = b.cpp_fullname();
+ casts += &BASE_CAST_TPL.interpolate(&hashmap! {
+ "rust_local" => rust_local.as_ref(),
+ "base_rust_local" => base_rust_local.as_ref(),
+ "base_cpp_full" => base_cpp_full.as_ref(),
+ "cpp_decl" => &cpp_decl,
+ });
+ }
+ }
+
let type_ref = c.type_ref();
out += &BOXED_CPP_TPL.interpolate(&hashmap! {
"rust_local" => type_ref.rust_local(),
"cpp_full" => type_ref.cpp_full(),
"cpp_extern" => type_ref.cpp_extern(),
+ "casts" => casts.into(),
})
}
out

ADD
binding-generator/src/writer/rust_native/tpl/class/base_cast.tpl.cpp
+{{base_cpp_full}}* cv_{{rust_local}}_to_{{base_rust_local}}({{cpp_decl}}) {
+ return dynamic_cast<{{base_cpp_full}}*>(instance);
+}
+
+

ADD
binding-generator/src/writer/rust_native/tpl/class/base_cast.tpl.rs
+boxed_cast_base! { {{rust_local}}, {{base_rust_full}}, cv_{{rust_local}}_to_{{base_rust_local}} }
+
+

RENAME
binding-generator/src/writer/rust_native/tpl/class/boxed_dtor.tpl.cpp
binding-generator/src/writer/rust_native/tpl/class/boxed.tpl.cpp
+{{casts}}
void cv_{{rust_local}}_delete({{cpp_full}}* instance) {
delete instance;
}

ADD
binding-generator/src/writer/rust_native/tpl/class/descendant_cast.tpl.cpp
+ Result<{{descendant_cpp_full}}*> cv_{{rust_local}}_to_{{descendant_rust_local}}({{cpp_decl}}) {
+ try {
+ return Ok(dynamic_cast<{{descendant_cpp_full}}*>(instance));
+ } OCVRS_CATCH(Result<{{descendant_cpp_full}}*>)
+}
+
+

ADD
binding-generator/src/writer/rust_native/tpl/class/descendant_cast.tpl.rs
+boxed_cast_descendant! { {{rust_local}}, {{descendant_rust_full}}, cv_{{rust_local}}_to_{{descendant_rust_local}} }
+
+

MODIFY
src/lib.rs
src/lib.rs
@@ -42,6 +42,8 @@ pub(crate) mod mod_prelude {
Result,
core::{CV_MAKE_TYPE, CV_MAKETYPE},
mod_prelude_types::*,
+ boxed_cast_base,
+ boxed_cast_descendant,
opencv_type_boxed,
opencv_type_enum,
opencv_type_simple,

MODIFY
src/manual/core/ptr/ptr_extern.rs
src/manual/core/ptr/ptr_extern.rs
@@ -61,10 +61,10 @@ macro_rules! ptr_cast_base {
($type: ty, $base: ty, $extern_convert: ident $(,)?) => {
extern "C" { fn $extern_convert(val: *mut std::ffi::c_void) -> *mut std::ffi::c_void; }
- impl ::std::convert::Into<$base> for $type {
+ impl ::std::convert::From<$type> for $base {
#[inline]
- fn into(self) -> $base {
- unsafe { <$base>::from_raw($extern_convert(self.into_raw())) }
+ fn from(s: $type) -> Self {
+ unsafe { Self::from_raw($extern_convert(s.into_raw())) }
}
}
};

MODIFY
src/traits/boxed.rs
src/traits/boxed.rs
@@ -106,3 +106,33 @@ macro_rules! opencv_type_boxed {
}
};
}
+
+#[macro_export]
+macro_rules! boxed_cast_base {
+ ($type: ty, $base: ty, $extern_convert: ident $(,)?) => {
+ extern "C" { fn $extern_convert(val: *mut std::ffi::c_void) -> *mut std::ffi::c_void; }
+
+ impl ::std::convert::From<$type> for $base {
+ #[inline]
+ fn from(s: $type) -> Self {
+ unsafe { Self::from_raw($extern_convert(s.into_raw())) }
+ }
+ }
+ };
+}
+
+#[macro_export]
+macro_rules! boxed_cast_descendant {
+ ($type: ty, $descendant: ty, $extern_convert: ident $(,)?) => {
+ extern "C" { fn $extern_convert(val: *mut std::ffi::c_void) -> *mut std::ffi::c_void; }
+
+ impl ::std::convert::TryFrom<$type> for $descendant {
+ type Error = $crate::Error;
+
+ #[inline]
+ fn try_from(s: $type) -> $crate::Result<Self> {
+ Ok(unsafe { Self::from_raw($extern_convert(s.into_raw())) })
+ }
+ }
+ };
+}

MODIFY
tests/boxed.rs
tests/boxed.rs
@@ -4,11 +4,12 @@ use std::{
};
use opencv::{
- core::{Scalar, Vec4f},
+ core::{Algorithm, NORM_L2, Scalar, Vec4f},
+ features2d::BFMatcher,
flann::IndexParams,
prelude::*,
Result,
- types::{PtrOfIndexParams, VectorOfVec4f},
+ types::{PtrOfFeature2D, PtrOfIndexParams, VectorOfVec4f},
};
#[test]
@@ -100,3 +101,37 @@ fn into_raw_ptrofboxed() -> Result<()> {
assert_eq!("my string", b.get_string("string", "-")?);
Ok(())
}
+
+#[test]
+fn smart_ptr_cast_base() -> Result<()> {
+ #[cfg(ocvrs_opencv_branch_4)]
+ use opencv::features2d::{AKAZE_DescriptorType::DESCRIPTOR_MLDB, KAZE_DiffusivityType::DIFF_PM_G2};
+ #[cfg(not(ocvrs_opencv_branch_4))]
+ use opencv::features2d::{AKAZE_DESCRIPTOR_MLDB as DESCRIPTOR_MLDB, KAZE_DIFF_PM_G2 as DIFF_PM_G2};
+ let d = <dyn AKAZE>::create(DESCRIPTOR_MLDB, 0, 3, 0.001, 4, 4, DIFF_PM_G2)?;
+ assert_eq!(true, Feature2DTrait::empty(&d)?);
+ if !cfg!(ocvrs_opencv_branch_32) {
+ assert_eq!("Feature2D.AKAZE", Feature2DTrait::get_default_name(&d)?);
+ } else {
+ assert_eq!("my_object", Feature2DTrait::get_default_name(&d)?);
+ }
+ let a = PtrOfFeature2D::from(d);
+ assert_eq!(true, Feature2DTrait::empty(&a)?);
+ if !cfg!(ocvrs_opencv_branch_32) {
+ assert_eq!("Feature2D.AKAZE", Feature2DTrait::get_default_name(&a)?);
+ } else {
+ assert_eq!("my_object", Feature2DTrait::get_default_name(&a)?);
+ }
+ Ok(())
+}
+
+#[test]
+fn cast_base() -> Result<()> {
+ let m = BFMatcher::new(NORM_L2, false)?;
+ assert_eq!(true, AlgorithmTrait::empty(&m)?);
+ assert_eq!("my_object", &m.get_default_name()?);
+ let a = Algorithm::from(m);
+ assert_eq!(true, a.empty()?);
+ assert_eq!("my_object", &a.get_default_name()?);
+ Ok(())
+}

Implement casting to base classes and back