Allow casting smart pointers to their base classes if it's needed by the OpenCV API
MODIFY
binding-generator/src/generator_env.rs
binding-generator/src/generator_env.rs
use std::{
- collections::HashMap,
+ collections::{HashMap, HashSet},
fmt,
fs::File,
io::{Read, Seek, SeekFrom},
@@ -16,6 +16,7 @@ use crate::{
Element,
EntityWalker,
EntityWalkerVisitor,
+ Func,
is_ephemeral_header,
is_opencv_path,
memo_map,
@@ -68,6 +69,16 @@ impl<'tu> DBPopulator<'tu, '_> {
}
self.gen_env.class_constants.insert(full_name, cnst);
}
+
+ fn add_used_in_smart_ptr(&mut self, func: Entity<'tu>) {
+ let args = Func::new(func, self.gen_env).arguments().into_iter()
+ .filter_map(|arg| arg.type_ref().as_smart_ptr())
+ .filter_map(|smart_ptr| smart_ptr.pointee().clang_type().get_declaration())
+ .collect::<Vec<_>>();
+ for arg in args {
+ self.gen_env.used_in_smart_ptr.insert(arg);
+ }
+ }
}
impl<'tu> EntityWalkerVisitor<'tu> for DBPopulator<'tu, '_> {
@@ -92,6 +103,7 @@ impl<'tu> EntityWalkerVisitor<'tu> for DBPopulator<'tu, '_> {
EntityKind::Constructor | EntityKind::Method | EntityKind::FunctionTemplate
| EntityKind::ConversionFunction => {
self.add_func_comment(c);
+ self.add_used_in_smart_ptr(c);
}
EntityKind::VarDecl => {
if let Some(StorageClass::Static) = c.get_storage_class() {
@@ -107,6 +119,7 @@ impl<'tu> EntityWalkerVisitor<'tu> for DBPopulator<'tu, '_> {
}
EntityKind::FunctionDecl => {
self.add_func_comment(entity);
+ self.add_used_in_smart_ptr(entity);
}
_ => {}
}
@@ -122,6 +135,7 @@ pub struct GeneratorEnv<'tu> {
class_kind_cache: MemoizeMap<String, Option<ClassKind>>,
class_constants: HashMap<String, Const<'tu>>,
type_resolve_cache: HashMap<String, Type<'tu>>,
+ used_in_smart_ptr: HashSet<Entity<'tu>>,
}
impl<'tu> GeneratorEnv<'tu> {
@@ -134,6 +148,7 @@ impl<'tu> GeneratorEnv<'tu> {
class_kind_cache: MemoizeMap::new(HashMap::with_capacity(32)),
class_constants: HashMap::with_capacity(32),
type_resolve_cache: HashMap::with_capacity(32),
+ used_in_smart_ptr: HashSet::with_capacity(32),
};
let walker = EntityWalker::new(root_entity);
walker.walk_opencv_entities(DBPopulator { gen_env: &mut out });
@@ -248,6 +263,10 @@ impl<'tu> GeneratorEnv<'tu> {
pub fn resolve_type(&self, typ: &str) -> Option<Type<'tu>> {
self.type_resolve_cache.get(typ).copied()
}
+
+ pub fn is_used_in_smart_ptr(&self, entity: Entity) -> bool {
+ self.used_in_smart_ptr.contains(&entity)
+ }
}
impl fmt::Debug for GeneratorEnv<'_> {

MODIFY
binding-generator/src/smart_ptr.rs
binding-generator/src/smart_ptr.rs
@@ -18,7 +18,7 @@ use crate::{
#[derive(Clone)]
pub struct SmartPtr<'tu, 'ge> {
entity: Entity<'tu>,
- gen_env: &'ge GeneratorEnv<'tu>,
+ pub(crate) gen_env: &'ge GeneratorEnv<'tu>,
}
impl<'tu, 'ge> SmartPtr<'tu, 'ge> {

MODIFY
binding-generator/src/writer/rust_native/smart_ptr.rs
binding-generator/src/writer/rust_native/smart_ptr.rs
@@ -6,6 +6,7 @@ use crate::{
Constness,
ConstnessOverride,
Element,
+ EntityElement,
SmartPtr,
StrExt,
type_ref,
@@ -27,6 +28,10 @@ impl RustNativeGeneratedElement for SmartPtr<'_, '_> {
|| include_str!("tpl/smart_ptr/trait_raw.tpl.rs").compile_interpolation()
);
+ static BASE_CAST_TPL: Lazy<CompiledInterpolation> = Lazy::new(
+ || include_str!("tpl/smart_ptr/base_cast.tpl.rs").compile_interpolation()
+ );
+
static CTOR_TPL: Lazy<CompiledInterpolation> = Lazy::new(
|| include_str!("tpl/smart_ptr/ctor.tpl.rs").compile_interpolation()
);
@@ -47,16 +52,21 @@ impl RustNativeGeneratedElement for SmartPtr<'_, '_> {
if let Some(cls) = pointee_type.as_class() {
gen_ctor |= !cls.is_abstract();
if cls.is_trait() {
- let mut all_bases = cls.all_bases();
- all_bases.insert(cls);
- let mut all_bases = all_bases.into_iter()
+ inter_vars.insert("base_rust_local", cls.rust_localname().into_owned().into());
+ inter_vars.insert("base_rust_full", cls.rust_trait_fullname().into_owned().into());
+ impls += &TRAIT_RAW_TPL.interpolate(&inter_vars);
+ let mut all_bases = cls.all_bases().into_iter()
.filter(|b| !b.is_excluded())
.collect::<Vec<_>>();
all_bases.sort_unstable_by(|a, b| a.cpp_fullname().cmp(&b.cpp_fullname()));
for base in all_bases {
inter_vars.insert("base_rust_local", base.rust_localname().into_owned().into());
inter_vars.insert("base_rust_full", base.rust_trait_fullname().into_owned().into());
+ inter_vars.insert("base_rust_full_ref", base.type_ref().rust_full().into_owned().into());
impls += &TRAIT_RAW_TPL.interpolate(&inter_vars);
+ if self.gen_env.is_used_in_smart_ptr(base.entity()) {
+ impls += &BASE_CAST_TPL.interpolate(&inter_vars);
+ }
}
}
};
@@ -74,6 +84,10 @@ impl RustNativeGeneratedElement for SmartPtr<'_, '_> {
|| include_str!("tpl/smart_ptr/cpp.tpl.cpp").compile_interpolation()
);
+ static BASE_CAST_TPL: Lazy<CompiledInterpolation> = Lazy::new(
+ || include_str!("tpl/smart_ptr/base_cast.tpl.cpp").compile_interpolation()
+ );
+
static CTOR_TPL: Lazy<CompiledInterpolation> = Lazy::new(
|| include_str!("tpl/smart_ptr/ctor.tpl.cpp").compile_interpolation()
);
@@ -97,6 +111,7 @@ impl RustNativeGeneratedElement for SmartPtr<'_, '_> {
let mut inter_vars = hashmap! {
"rust_localalias" => self.rust_localalias(),
"cpp_extern" => type_ref.cpp_extern(),
+ "cpp_decl" => type_ref.cpp_arg_func_decl("instance").into(),
"cpp_full" => type_ref.cpp_full(),
"cpp_full_const" => cpp_full_const,
"inner_cpp_full" => pointee_type.cpp_full(),
@@ -104,8 +119,29 @@ impl RustNativeGeneratedElement for SmartPtr<'_, '_> {
"inner_cpp_extern_const" => inner_cpp_extern_const,
};
+ let mut base_cast = String::new();
let pointee_primitive = pointee_type.is_primitive();
- if pointee_primitive || pointee_type.as_class().map_or(false, |c| !c.is_abstract()) {
+ let mut gen_ctor = pointee_primitive;
+ if let Some(cls) = pointee_type.as_class() {
+ gen_ctor |= !cls.is_abstract();
+ if cls.is_trait() {
+ let mut all_bases = cls.all_bases().into_iter()
+ .filter(|b| !b.is_excluded() && self.gen_env.is_used_in_smart_ptr(b.entity()))
+ .collect::<Vec<_>>();
+ all_bases.sort_unstable_by(|a, b| a.cpp_fullname().cmp(&b.cpp_fullname()));
+ for base in all_bases {
+ inter_vars.insert("base_cpp_full", base.cpp_fullname().into_owned().into());
+ inter_vars.insert("base_rust_local", base.rust_localname().into_owned().into());
+ let new_base_cast = BASE_CAST_TPL.interpolate(&inter_vars);
+ if base_cast.is_empty() {
+ base_cast = new_base_cast;
+ } else {
+ base_cast += &new_base_cast;
+ }
+ }
+ }
+ }
+ if gen_ctor {
let inner_cpp_func_call = if pointee_primitive {
format!("new {typ}(val)", typ=pointee_type.cpp_full()).into()
} else {
@@ -121,6 +157,7 @@ impl RustNativeGeneratedElement for SmartPtr<'_, '_> {
} else {
inter_vars.insert("ctor", "".into());
};
+ inter_vars.insert("base_cast", base_cast.into());
TPL.interpolate(&inter_vars)
}

ADD
binding-generator/src/writer/rust_native/tpl/smart_ptr/base_cast.tpl.cpp
+
+
+cv::Ptr<{{base_cpp_full}}>* cv_{{rust_localalias}}_to_PtrOf{{base_rust_local}}({{cpp_decl}}) {
+ return new cv::Ptr<{{base_cpp_full}}>(instance->dynamicCast<{{base_cpp_full}}>());
+}
+

ADD
binding-generator/src/writer/rust_native/tpl/smart_ptr/base_cast.tpl.rs
+ptr_cast_base! { {{rust_localalias}}, core::Ptr<{{base_rust_full_ref}}>,
+ cv_{{rust_localalias}}_to_PtrOf{{base_rust_local}},
+}
+
+

MODIFY
binding-generator/src/writer/rust_native/tpl/smart_ptr/cpp.tpl.cpp
binding-generator/src/writer/rust_native/tpl/smart_ptr/cpp.tpl.cpp
extern "C" {
{{ctor}}
- void cv_{{rust_localalias}}_delete({{cpp_full}}* instance) {
+ void cv_{{rust_localalias}}_delete({{cpp_decl}}) {
delete instance;
}
@@ -8,9 +8,10 @@ extern "C" {
return instance->get();
}
- {{inner_cpp_extern}} cv_{{rust_localalias}}_get_inner_ptr_mut({{cpp_full}}* instance) {
+ {{inner_cpp_extern}} cv_{{rust_localalias}}_get_inner_ptr_mut({{cpp_decl}}) {
return instance->get();
}
+ {{base_cast}}
}

MODIFY
src/lib.rs
src/lib.rs
@@ -44,6 +44,7 @@ pub(crate) mod mod_prelude {
opencv_type_boxed,
opencv_type_enum,
opencv_type_simple,
+ ptr_cast_base,
ptr_extern,
ptr_extern_ctor,
vector_copy_non_bool,

MODIFY
src/manual/core/ptr/ptr_extern.rs
src/manual/core/ptr/ptr_extern.rs
@@ -55,3 +55,17 @@ macro_rules! ptr_extern_ctor {
}
};
}
+
+#[macro_export]
+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 {
+ #[inline]
+ fn into(self) -> $base {
+ unsafe { <$base>::from_raw($extern_convert(self.into_raw())) }
+ }
+ }
+ };
+}

ADD
tests/videostab.rs
+#![cfg(feature = "contrib")]
+
+use opencv::{
+ core::Ptr,
+ Result,
+ videostab::{KeypointBasedMotionEstimator, MotionEstimatorRansacL2, MotionModel},
+};
+
+#[test]
+fn motion_estimator() -> Result<()> {
+ // just test the Ptr creation, conversion to base class and linking for now
+ let est = MotionEstimatorRansacL2::new(MotionModel::MM_AFFINE).unwrap();
+ let est_ptr = Ptr::new(est);
+ let _estimator = KeypointBasedMotionEstimator::new(est_ptr.into()).unwrap();
+ Ok(())
+}

Allow casting smart pointers to their base classes if it's needed by the OpenCV API