Update string handling logic
MODIFY
binding-generator/src/class.rs
binding-generator/src/class.rs
@@ -339,7 +339,7 @@ impl Element for Class<'_> {
}
fn rust_leafname(&self) -> Cow<str> {
- if self.type_ref().is_string() {
+ if self.type_ref().as_string().is_some() {
"String".into()
} else {
self.cpp_localname()

MODIFY
binding-generator/src/func.rs
binding-generator/src/func.rs
@@ -25,6 +25,7 @@ use crate::{
settings::{self, SliceHint},
StrExt,
StringExt,
+ type_ref::{Dir, StrType},
TypeRef,
};
@@ -248,7 +249,7 @@ impl<'tu> Func<'tu> {
else if let Some(fld) = self.as_field_accessor() {
self.type_hint != FunctionTypeHint::FieldSetter && {
let type_ref = fld.type_ref();
- type_ref.is_const() || type_ref.is_copy() || type_ref.is_cv_string() || type_ref.is_std_string()
+ type_ref.is_const() || type_ref.is_copy() || matches!(type_ref.as_string(), Some(Dir::In(StrType::CvString)) | Some(Dir::In(StrType::StdString)))
}
} else {
self.entity.is_const_method()

MODIFY
binding-generator/src/type_ref.rs
binding-generator/src/type_ref.rs
@@ -74,6 +74,19 @@ enum Form {
Reference(Option<Lifetime>, bool),
}
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum StrType {
+ StdString,
+ CvString,
+ CharPtr,
+}
+
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum Dir<T> {
+ In(T),
+ Out(T),
+}
+
impl Form {
pub fn is_full(self) -> bool {
match self {
@@ -566,55 +579,72 @@ impl<'tu> TypeRef<'tu> {
}
}
- pub fn is_std_string(&self) -> bool {
- match self.canonical().kind() {
- Kind::Class(cls) => {
+ pub fn as_string(&self) -> Option<Dir<StrType>> {
+ fn class_string_type(cls: Class) -> Option<StrType> {
let cpp_fullname = cls.cpp_fullname();
- cpp_fullname.starts_with("std::") && cpp_fullname.ends_with("::string")
- }
- Kind::Reference(inner) => {
- inner.is_std_string()
- }
- Kind::Pointer(inner) => {
- inner.is_std_string() && !inner.is_recursive_const()
- }
- _ => {
- false
- }
+ if cpp_fullname.starts_with("std::") && cpp_fullname.ends_with("::string") {
+ Some(StrType::StdString)
+ } else if cpp_fullname == "cv::String" {
+ Some(StrType::CvString)
+ } else {
+ None
}
}
- pub fn is_cv_string(&self) -> bool {
match self.canonical().kind() {
Kind::Class(cls) => {
- cls.cpp_fullname() == "cv::String"
+ if let Some(typ) = class_string_type(cls) {
+ return Some(Dir::In(typ))
+ }
}
Kind::Reference(inner) => {
- inner.is_cv_string()
+ if let Some(typ) = inner.as_class().and_then(class_string_type) {
+ return if inner.is_clang_const() {
+ Some(Dir::In(typ))
+ } else {
+ Some(Dir::Out(typ))
+ };
+ }
}
Kind::Pointer(inner) => {
- inner.is_cv_string() && !inner.is_recursive_const()
+ if let Some(typ) = inner.as_class().and_then(class_string_type) {
+ return if inner.is_clang_const() {
+ Some(Dir::In(typ))
+ } else {
+ Some(Dir::Out(typ))
}
- _ => {
- false
+ } else {
+ let inner_cpp_full = inner.cpp_full();
+ if inner_cpp_full == "char" || inner_cpp_full == "const char" {
+ return if inner.is_clang_const() {
+ Some(Dir::In(StrType::CharPtr))
+ } else {
+ Some(Dir::Out(StrType::CharPtr))
+ };
}
}
}
-
- pub fn is_char_ptr_string(&self) -> bool {
- match self.canonical().kind() {
- Kind::Pointer(inner) | Kind::Array(inner, ..) => {
+ Kind::Array(inner, ..) => {
let inner_cpp_full = inner.cpp_full();
- inner_cpp_full == "char" || inner_cpp_full == "const char"
+ if inner_cpp_full == "char" || inner_cpp_full == "const char" {
+ return Some(Dir::In(StrType::CharPtr))
}
- _ => {
- false
}
+ _ => {}
+ }
+ None
+ }
+
+ pub fn is_std_string(&self) -> bool {
+ matches!(self.as_string(), Some(Dir::In(StrType::StdString)))
}
+
+ pub fn is_cv_string(&self) -> bool {
+ matches!(self.as_string(), Some(Dir::In(StrType::CvString)))
}
- pub fn is_string(&self) -> bool {
- self.is_std_string() || self.is_cv_string() || self.is_char_ptr_string()
+ pub fn is_char_ptr_string(&self) -> bool {
+ matches!(self.as_string(), Some(Dir::In(StrType::CharPtr)))
}
pub fn is_input_array(&self) -> bool {
@@ -788,7 +818,7 @@ impl<'tu> TypeRef<'tu> {
pub fn as_string_array(&self) -> Option<(TypeRef<'tu>, Option<usize>)> {
if let Some((elem, size)) = self.as_array() {
- if elem.is_string() {
+ if elem.as_string().is_some() {
return Some((elem, size))
}
}
@@ -825,34 +855,6 @@ impl<'tu> TypeRef<'tu> {
}
}
-// pub fn as_entity(&self) -> Option<Box<dyn EntityElement<'tu> + 'tu>> where 'g: 'tu {
-// match self.kind() {
-// Kind::Primitive(..) | Kind::Array(..) | Kind::Pointer(..)
-// | Kind::Reference(..) => {
-// None
-// }
-// Kind::StdVector(out) => {
-// Some(Box::new(out))
-// }
-// Kind::SmartPtr(out) => {
-// Some(Box::new(out))
-// }
-// Kind::Class(out) => {
-// Some(Box::new(out))
-// }
-// Kind::Enum(out) => {
-// Some(Box::new(out))
-// }
-// Kind::Function => {
-// None
-// }
-// Kind::Typedef(out) => {
-// Some(Box::new(out))
-// }
-// Kind::Generic(..) => {
-// None
-// }
-// }
// }
pub fn is_by_ptr(&self) -> bool {
@@ -872,10 +874,6 @@ impl<'tu> TypeRef<'tu> {
}
}
- fn is_output(&self) -> bool {
- !self.is_recursive_const() && (self.as_reference().is_some() || self.as_pointer().map_or(false, |t| t.is_string()))
- }
-
pub fn is_data_type(&self) -> bool {
settings::DATA_TYPES.contains(self.cpp_full().as_ref())
}
@@ -1025,7 +1023,7 @@ impl<'tu> TypeRef<'tu> {
}
fn rust_type_ref(&self, form: Form) -> Cow<str> {
- if self.is_string() {
+ if self.as_string().is_some() {
#[allow(clippy::if_same_then_else)]
return if self.is_const() {
"String" // todo implement receiving const str's
@@ -1152,7 +1150,7 @@ impl<'tu> TypeRef<'tu> {
}
fn rust_lifetime_count(&self) -> usize {
- if self.is_string() {
+ if self.as_string().is_some() {
0
} else {
match self.kind() {
@@ -1198,14 +1196,19 @@ impl<'tu> TypeRef<'tu> {
pub fn rust_extern_with_const(&self, constness: Constness) -> Cow<str> {
#[allow(clippy::never_loop)] // fixme use named block when stable
'typ: loop {
- if self.is_string() {
+ if let Some(dir) = self.as_string() {
+ match dir {
+ Dir::In(_) => {
break 'typ if constness.is_const(self) {
"*const c_char".into()
- } else if self.is_output() {
- "*mut *mut c_void".into()
} else {
"*mut c_char".into()
};
+ },
+ Dir::Out(_) => {
+ break 'typ "*mut *mut c_void".into();
+ },
+ }
}
if self.is_by_ptr() {
break 'typ if constness.is_const(self) {
@@ -1219,7 +1222,7 @@ impl<'tu> TypeRef<'tu> {
out.write_fmt(format_args!("*{}", self.rust_const_qual(true))).expect("Impossible");
if inner.is_void() {
out += "c_void";
- } else if self.is_string() {
+ } else if self.as_string().is_some() {
out += "c_char";
} else {
out += &inner.rust_extern_with_const(constness)
@@ -1235,10 +1238,17 @@ impl<'tu> TypeRef<'tu> {
).into();
}
if let Some(elem) = self.as_variable_array() {
+ let typ = if matches!(elem.as_string(), Some(Dir::Out(StrType::CharPtr))) {
+ // kind of special casing for cv_startLoop_int__X__int__charXX__int_charXX, without that
+ // argv is treated as array of output arguments and it doesn't seem to be meant this way
+ format!("*{cnst}c_char", cnst=elem.rust_clang_const_qual(true)).into()
+ } else {
+ elem.rust_extern_with_const(constness)
+ };
break 'typ format!(
"*{cnst}{typ}",
cnst=self.rust_const_qual(true),
- typ=elem.rust_extern_with_const(constness),
+ typ=typ,
).into()
}
if let Some(func) = self.as_function() {
@@ -1263,12 +1273,11 @@ impl<'tu> TypeRef<'tu> {
pub fn rust_arg_func_decl(&self, name: &str) -> String {
#[allow(clippy::never_loop)] // fixme use named block when stable
let typ = 'decl_type: loop {
- if self.is_string() {
- if self.is_output() {
- break 'decl_type "&mut String".into();
- } else {
- break 'decl_type "&str".into();
- }
+ if let Some(dir) = self.as_string() {
+ break 'decl_type match dir {
+ Dir::In(_) => "&str".into(),
+ Dir::Out(_) => "&mut String".into(),
+ };
} else if self.is_input_array() {
break 'decl_type "&dyn core::ToInputArray".into();
} else if self.is_output_array() {
@@ -1306,7 +1315,7 @@ impl<'tu> TypeRef<'tu> {
pub fn rust_return_map(&self, is_safe_context: bool, is_static_func: bool) -> Cow<str> {
let unsafety_call = if is_safe_context { "unsafe " } else { "" };
- if self.is_string() || self.is_by_ptr() {
+ if self.as_string().is_some() || self.is_by_ptr() {
format!(
".map(|r| {unsafety_call}{{ {typ}::opencv_from_extern(r) }} )",
unsafety_call=unsafety_call,
@@ -1325,15 +1334,14 @@ impl<'tu> TypeRef<'tu> {
}
pub fn rust_arg_pre_call(&self, name: &str, is_function_infallible: bool) -> String {
- if self.is_string() {
- return if self.is_output() {
- format!("string_arg_output_send!(via {name}_via)", name=name)
- } else {
+ if let Some(dir) = self.as_string() {
+ return match dir {
+ Dir::In(_) => {
let mut flags = vec![];
if is_function_infallible {
flags.push("nofail")
}
- if !self.is_recursive_const() {
+ if !self.is_const() {
flags.push("mut")
}
let mut flags = flags.join(" ");
@@ -1341,7 +1349,11 @@ impl<'tu> TypeRef<'tu> {
flags.push(' ');
}
format!("extern_container_arg!({flags}{name})", flags=flags, name=name)
- }
+ },
+ Dir::Out(_) => {
+ format!("string_arg_output_send!(via {name}_via)", name=name)
+ },
+ };
} else if self.is_input_array() {
return format!("input_array_arg!({name})", name=name);
} else if self.is_output_array() {
@@ -1391,14 +1403,15 @@ impl<'tu> TypeRef<'tu> {
}
pub fn rust_arg_func_call(&self, name: &str, is_const: bool) -> String {
- if self.is_string() {
- return if self.is_output() {
- format!("&mut {name}_via", name=name)
- } else if self.is_recursive_const() || is_const {
+ if let Some(dir) = self.as_string() {
+ return match dir {
+ Dir::In(_) => if self.is_const() || is_const {
format!("{name}.opencv_as_extern()", name=name)
} else {
format!("{name}.opencv_as_extern_mut()", name=name)
- }
+ },
+ Dir::Out(_) => format!("&mut {name}_via", name=name),
+ };
}
if self.as_reference().map_or(false, |inner| (inner.as_simple_class().is_some() || inner.is_enum()) && (inner.is_const() || is_const)) {
return format!("&{name}", name=name);
@@ -1461,7 +1474,7 @@ impl<'tu> TypeRef<'tu> {
}
pub fn rust_extern_return(&self) -> Cow<str> {
- if self.is_string() {
+ if self.as_string().is_some() {
"*mut c_void".into()
} else {
self.rust_extern_with_const(Constness::Mut)
@@ -1477,7 +1490,7 @@ impl<'tu> TypeRef<'tu> {
}
pub fn rust_arg_post_call(&self, name: &str, _is_function_infallible: bool) -> String {
- if self.is_string() && self.is_output() {
+ if let Some(Dir::Out(_)) = self.as_string() {
format!("string_arg_output_receive!(out, {name}_via => {name})", name = name)
} else {
"".to_string()
@@ -1558,7 +1571,7 @@ impl<'tu> TypeRef<'tu> {
_ => {
if let DependentTypeMode::ForReturn(def_location) = mode {
if !self.is_generic() && !self.is_void() {
- if self.is_string() {
+ if self.as_string().is_some() {
let type_ref = TypeRef::new(
self.gen_env.resolve_type(&self.cpp_extern_return()).expect("Can't resolve string cpp_extern_return()"),
self.gen_env,
@@ -1777,18 +1790,17 @@ impl<'tu> TypeRef<'tu> {
pub fn cpp_extern_with_name(&self, name: &str) -> Cow<str> {
let space_name = if name.is_empty() { "".to_string() } else { format!(" {}", name) };
- if self.is_by_ptr() {
+ if let Some(dir) = self.as_string() {
+ match dir {
+ Dir::In(_) => format!("{cnst}char*{name}", cnst=self.cpp_const_qual(), name=space_name).into(),
+ Dir::Out(_) => format!("{cnst}void*{name}", cnst=self.cpp_const_qual(), name=space_name).into(),
+ }
+ } else if self.is_by_ptr() {
if self.as_pointer().is_some() || self.as_reference().is_some() {
format!("{typ}{name}", typ=self.cpp_full(), name=space_name).into()
} else {
format!("{typ}*{name}", typ=self.cpp_full(), name=space_name).into()
}
- } else if self.is_string() {
- if self.is_output() {
- format!("{cnst}void*{name}", cnst=self.cpp_recursive_const_qual(), name=space_name).into()
- } else {
- format!("{cnst}char*{name}", cnst=self.cpp_recursive_const_qual(), name=space_name).into()
- }
} else {
self.cpp_full_with_name(name)
}
@@ -1808,35 +1820,43 @@ impl<'tu> TypeRef<'tu> {
}
pub fn cpp_arg_func_decl(&self, name: &str) -> String {
- if self.is_output() && self.is_string() || self.as_simple_class().is_some() {
+ if matches!(self.as_string(), Some(Dir::Out(_))) || self.as_simple_class().is_some() {
return format!("{typ}* {name}", typ=self.cpp_extern(), name=name);
}
self.cpp_extern_with_name(name).into_owned()
}
pub fn cpp_arg_pre_call(&self, name: &str) -> String {
- if self.is_output() {
- if self.is_std_string() {
+ match self.as_string() {
+ Some(Dir::Out(StrType::StdString)) => {
return format!("std::string {name}_out", name=name);
- } else if self.is_cv_string() {
+ }
+ Some(Dir::Out(StrType::CvString)) => {
return format!("cv::String {name}_out", name=name);
}
+ Some(Dir::Out(StrType::CharPtr)) => {
+ return format!("char* {name}_out = new char[1024]()", name=name);
+ }
+ Some(_) | None => {}
}
"".to_string()
}
pub fn cpp_arg_func_call<'a>(&self, name: impl Into<Cow<'a, str>>) -> Cow<'a, str> {
let name = name.into();
- let is_cv_string = self.is_cv_string();
- if is_cv_string || self.is_std_string() {
- return if self.is_output() {
- let ptr = if self.as_pointer().is_some() { "&" } else { "" };
- format!("{ptr}{name}_out", ptr=ptr, name=name).into()
- } else if is_cv_string {
- format!("cv::String({name})", name=name).into()
- } else {
- format!("std::string({name})", name=name).into()
- };
+
+ match self.as_string() {
+ Some(Dir::Out(str_type)) => {
+ let ptr = if str_type != StrType::CharPtr && self.as_pointer().is_some() { "&" } else { "" };
+ return format!("{ptr}{name}_out", ptr=ptr, name=name).into();
+ }
+ Some(Dir::In(StrType::StdString)) => {
+ return format!("std::string({name})", name=name).into();
+ },
+ Some(Dir::In(StrType::CvString)) => {
+ return format!("cv::String({name})", name=name).into();
+ },
+ Some(Dir::In(StrType::CharPtr)) | None => {}
}
if self.is_by_ptr() {
return if self.as_pointer().is_some() {
@@ -1852,7 +1872,7 @@ impl<'tu> TypeRef<'tu> {
}
pub fn cpp_extern_return(&self) -> Cow<str> {
- if self.is_string() {
+ if self.as_string().is_some() {
"void*".into()
} else if self.is_by_ptr() && !self.as_abstract_class_ptr().is_some() {
format!("{typ}*", typ=self.cpp_full()).into()
@@ -1870,9 +1890,28 @@ impl<'tu> TypeRef<'tu> {
}
pub fn cpp_arg_post_call(&self, name: &str) -> String {
- if self.is_output() && self.is_string() {
+ match self.as_string() {
+ Some(Dir::Out(StrType::StdString)) | Some(Dir::Out(StrType::CvString)) => {
return format!("*{name} = ocvrs_create_string({name}_out.c_str())", name=name);
}
+ Some(Dir::Out(StrType::CharPtr)) => {
+ return format!("*{name} = ocvrs_create_string({name}_out)", name=name);
+ }
+ _ => {}
+ }
+ "".to_string()
+ }
+
+ // we need cleanup as a separate step from post_call because in cv_ocl_convertTypeStr_int_int_int_charX the
+ // return value is actually one of the arguments and if we free it (in post_call phase) before converting
+ // to string (in return statement) it will result in UB
+ pub fn cpp_arg_cleanup(&self, name: &str) -> String {
+ match self.as_string() {
+ Some(Dir::Out(StrType::CharPtr)) => {
+ return format!("delete[] {name}_out", name=name);
+ }
+ _ => {}
+ }
"".to_string()
}
}
@@ -1907,17 +1946,28 @@ impl fmt::Debug for TypeRef<'_> {
if self.is_enum() {
props.push("enum");
}
- if self.is_std_string() {
+ if let Some(str_type) = self.as_string() {
+ props.push("string");
+ let str_type = match str_type {
+ Dir::In(str_type) => {
+ str_type
+ },
+ Dir::Out(str_type) => {
+ props.push("output_string");
+ str_type
+ },
+ };
+ match str_type {
+ StrType::StdString => {
props.push("std_string");
- }
- if self.is_cv_string() {
+ },
+ StrType::CvString => {
props.push("cv_string");
- }
- if self.is_char_ptr_string() {
+ },
+ StrType::CharPtr => {
props.push("char_ptr_string");
+ },
}
- if self.is_string() {
- props.push("string");
}
if self.is_void() {
props.push("void");
@@ -1934,9 +1984,6 @@ impl fmt::Debug for TypeRef<'_> {
if self.is_by_ptr() {
props.push("by_ptr");
}
- if self.is_output() {
- props.push("output_string");
- }
if self.is_generic() {
props.push("generic");
}

MODIFY
binding-generator/src/vector.rs
binding-generator/src/vector.rs
@@ -54,7 +54,7 @@ impl<'tu> Vector<'tu> {
let is_data_type = self.is_data_type(&element_type);
let mut out = element_type.dependent_types_with_mode(DependentTypeMode::ForReturn(DefinitionLocation::Type));
out.reserve(1 + if is_data_type { 3 } else { 0 });
- if element_type.is_string() {
+ if element_type.as_string().is_some() {
out.push(D::from_return_type_wrapper(ReturnTypeWrapper::new(
TypeRef::new(self.gen_env.resolve_type(&element_type.cpp_extern_return()).expect("Can't resolve string cpp_extern_return()"), self.gen_env),
DefinitionLocation::Custom(element_type.rust_module().into_owned()),

MODIFY
binding-generator/src/writer/rust_native/func.rs
binding-generator/src/writer/rust_native/func.rs
@@ -18,6 +18,7 @@ use crate::{
settings,
StrExt,
StringExt,
+ type_ref::{Dir, StrType},
};
use super::RustNativeGeneratedElement;
@@ -255,22 +256,27 @@ fn cpp_call_invoke(f: &Func) -> String {
fn cpp_method_return<'f>(f: &'f Func) -> Cow<'f, str> {
let return_type = f.return_type();
if return_type.is_void() {
- "return Ok();".into()
+ "Ok()".into()
} else if return_type.is_by_ptr() && !f.as_constructor().is_some() {
let out = return_type.source()
.as_class()
.and_then(|cls| if cls.is_abstract() {
- Some(Cow::Borrowed("return Ok(ret);"))
+ Some(Cow::Borrowed("Ok(ret)"))
} else {
None
});
- out.unwrap_or_else(|| format!("return Ok(new {typ}(ret));", typ=return_type.cpp_full()).into())
- } else if return_type.is_cv_string() || return_type.is_std_string() {
- "return Ok(ocvrs_create_string(ret.c_str()));".into()
- } else if return_type.is_char_ptr_string() {
- "return Ok(ocvrs_create_string(ret));".into()
+ out.unwrap_or_else(|| format!("Ok(new {typ}(ret))", typ=return_type.cpp_full()).into())
+ } else if let Some(Dir::In(string_type)) | Some(Dir::Out(string_type)) = return_type.as_string() {
+ match string_type {
+ StrType::StdString | StrType::CvString => {
+ "Ok(ocvrs_create_string(ret.c_str()))".into()
+ },
+ StrType::CharPtr => {
+ "Ok(ocvrs_create_string(ret))".into()
+ },
+ }
} else {
- format!("return Ok<{typ}>(ret);", typ=return_type.cpp_extern_return()).into()
+ format!("Ok<{typ}>(ret)", typ=return_type.cpp_extern_return()).into()
}
}
@@ -344,36 +350,39 @@ impl RustNativeGeneratedElement for Func<'_> {
let mut decl_args = Vec::with_capacity(args.len());
let mut pre_call_args = Vec::with_capacity(args.len());
let mut post_call_args = Vec::with_capacity(args.len());
+ let mut cleanup_args = Vec::with_capacity(args.len());
if let Some(cls) = self.as_instance_method() {
decl_args.push(cls.type_ref().cpp_self_func_decl(self.is_const()));
}
for (name, arg) in args {
let type_ref = arg.type_ref();
decl_args.push(type_ref.cpp_arg_func_decl(&name));
- let mut pre_call_arg = type_ref.cpp_arg_pre_call(&name);
- if !pre_call_arg.is_empty() {
- pre_call_arg.push(';');
- pre_call_args.push(pre_call_arg);
- }
- let mut post_call_arg = type_ref.cpp_arg_post_call(&name);
- if !post_call_arg.is_empty() {
- post_call_arg.push(';');
- post_call_args.push(post_call_arg);
- }
+ pre_post_arg_handle(type_ref.cpp_arg_pre_call(&name), &mut pre_call_args);
+ pre_post_arg_handle(type_ref.cpp_arg_post_call(&name), &mut post_call_args);
+ pre_post_arg_handle(type_ref.cpp_arg_cleanup(&name), &mut cleanup_args);
}
let return_type = self.return_type();
+ let return_wrapper_full = return_type.cpp_extern_return_wrapper_full();
+ let ret = cpp_method_return(self);
+ let ret = if cleanup_args.is_empty() {
+ format!("return {};", ret).into()
+ } else {
+ pre_post_arg_handle(format!("{typ} f_ret = {expr}", typ=return_wrapper_full, expr=ret), &mut post_call_args);
+ "return f_ret;".into()
+ };
TPL.interpolate(&hashmap! {
"attributes_begin" => attributes_begin.into(),
"debug" => get_debug(self).into(),
- "return_wrapper_type" => return_type.cpp_extern_return_wrapper_full(),
+ "return_wrapper_type" => return_wrapper_full,
"identifier" => identifier,
"decl_args" => decl_args.join(", ").into(),
"pre_call_args" => pre_call_args.join("\n").into(),
"call" => cpp_call_invoke(self).into(),
"post_call_args" => post_call_args.join("\n").into(),
- "return" => cpp_method_return(self),
+ "cleanup_args" => cleanup_args.join("\n").into(),
+ "return" => ret,
"attributes_end" => attributes_end.into(),
})
}

MODIFY
binding-generator/src/writer/rust_native/tpl/func/cpp.tpl.cpp
binding-generator/src/writer/rust_native/tpl/func/cpp.tpl.cpp
{{pre_call_args}}
{{call}}
{{post_call_args}}
+ {{cleanup_args}}
{{return}}
} OCVRS_CATCH(OCVRS_TYPE({{return_wrapper_type}}))
}

MODIFY
binding-generator/src/writer/rust_native/vector.rs
binding-generator/src/writer/rust_native/vector.rs
@@ -52,7 +52,7 @@ impl RustNativeGeneratedElement for Vector<'_> {
};
if element_type.as_typedef().is_some()
&& !element_type.is_data_type()
- && !element_type.is_string()
+ && !element_type.as_string().is_some()
&& !settings::FORCE_VECTOR_TYPEDEF_GENERATION.contains(element_type.cpp_full().as_ref())
{
&TYPE_ALIAS_TPL
@@ -111,7 +111,7 @@ impl RustNativeGeneratedElement for Vector<'_> {
if element_type.is_by_ptr() {
prefix = format!("new {inner_cpp_full}(", inner_cpp_full = element_type.cpp_full()).into();
suffix = ")".into();
- } else if element_type.is_string() {
+ } else if element_type.as_string().is_some() {
prefix = "ocvrs_create_string(".into();
suffix = ".c_str())".into();
}

ADD
tests/opencl.rs
+use opencv::{
+ Result,
+ core,
+};
+
+#[test]
+fn convert_type_str() -> Result<()> {
+ // this function writes to buf argument and returns it
+ let mut test = "test".to_string();
+ assert_eq!("noconvert", core::convert_type_str(1, 1, 1, &mut test)?);
+ assert_eq!("", test);
+ assert_eq!("convert_ushort_sat", core::convert_type_str(1, 2, 1, &mut test)?);
+ assert_eq!("convert_ushort_sat", test);
+ Ok(())
+}

Update string handling logic