Skip to content

Commit 3629df7

Browse files
committed
Correctly handle TypeRefTypeHint of the references
1 parent 9841600 commit 3629df7

File tree

10 files changed

+131
-8
lines changed

10 files changed

+131
-8
lines changed

binding-generator/Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,8 @@ percent-encoding = { version = "2", default-features = false }
2020
regex = "1"
2121
shlex = { version = "1.3", default-features = false }
2222

23+
[dev-dependencies]
24+
tempfile = "3"
25+
2326
[features]
2427
clang-runtime = ["clang/runtime", "clang-sys/runtime"]

binding-generator/src/generator.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ pub trait GeneratorVisitor<'tu>: Sized {
7777
/// It takes [Entity]s supplied by the entity walker, extracts their export data (whether the entity should appear in bindings at
7878
/// all or is internal) and calls the corresponding method in [GeneratorVisitor] based on their type. This is the 2nd pass of the
7979
/// binding generation.
80-
struct OpenCvWalker<'tu, 'r, V> {
80+
pub struct OpenCvWalker<'tu, 'r, V> {
8181
module: &'r str,
8282
opencv_module_header_dir: &'r Path,
8383
visitor: V,

binding-generator/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ pub use entity::EntityExt;
3030
pub use enumeration::Enum;
3131
use field::Field;
3232
pub use func::{Func, FuncId, FuncTypeHint};
33-
pub use generator::{GeneratedType, Generator, GeneratorVisitor};
33+
pub use generator::{GeneratedType, Generator, GeneratorVisitor, OpenCvWalker};
3434
pub use generator_env::{ClassKindOverride, ExportConfig, GeneratorEnv};
3535
pub use iterator_ext::IteratorExt;
3636
use memoize::{MemoizeMap, MemoizeMapExt};

binding-generator/src/settings/argument_override.rs

+14
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,20 @@ pub static ARGUMENT_OVERRIDE: Lazy<HashMap<FuncId, HashMap<&str, TypeRefTypeHint
149149
FuncId::new_mut("cv::MatSize::MatSize", ["_p"]),
150150
HashMap::from([("_p", TypeRefTypeHint::PrimitivePtrAsRaw)]),
151151
),
152+
(
153+
FuncId::new_mut(
154+
"cv::findCirclesGrid",
155+
["image", "patternSize", "centers", "flags", "blobDetector", "parameters"],
156+
),
157+
HashMap::from([("blobDetector", TypeRefTypeHint::Nullable)]),
158+
),
159+
(
160+
FuncId::new_mut(
161+
"cv::findCirclesGrid",
162+
["image", "patternSize", "centers", "flags", "blobDetector"],
163+
),
164+
HashMap::from([("blobDetector", TypeRefTypeHint::Nullable)]),
165+
),
152166
])
153167
});
154168

binding-generator/src/type_ref/desc.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ impl<'tu> ClangTypeExt<'tu> for Type<'tu> {
379379
match kind {
380380
TypeKind::Pointer => {
381381
let pointee = self.get_pointee_type().expect("No pointee type for pointer");
382-
let pointee_typeref = TypeRef::new_ext(pointee, type_hint, parent_entity, gen_env);
382+
let pointee_typeref = TypeRef::new_ext(pointee, type_hint.recurse(), parent_entity, gen_env);
383383
let pointee_kind = pointee_typeref.kind();
384384
if pointee_kind.is_function() {
385385
pointee_kind.into_owned()
@@ -392,7 +392,7 @@ impl<'tu> ClangTypeExt<'tu> for Type<'tu> {
392392

393393
TypeKind::LValueReference => TypeRefKind::Reference(TypeRef::new_ext(
394394
self.get_pointee_type().expect("No pointee type for reference"),
395-
type_hint,
395+
type_hint.recurse(),
396396
parent_entity,
397397
gen_env,
398398
)),

binding-generator/src/type_ref/kind.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ impl<'tu, 'ge> TypeRefKind<'tu, 'ge> {
251251
.map(|(_, str_type)| (Dir::from_out_dir(inner.inherent_constness().is_mut()), str_type)),
252252
TypeRefKind::Array(inner, ..) => {
253253
if inner.kind().is_char() {
254-
Some((Dir::In, StrType::CharPtr(StrEnc::Text)))
254+
Some((Dir::from_out_dir(inner.constness().is_mut()), StrType::CharPtr(StrEnc::Text)))
255255
} else {
256256
None
257257
}
@@ -527,7 +527,7 @@ mod tests {
527527

528528
{
529529
let char_array = TypeRef::new_array(TypeRefDesc::char(), None);
530-
assert_eq!(Some((Dir::In, StrType::CharPtr(StrEnc::Text))), as_string(char_array));
530+
assert_eq!(Some((Dir::Out, StrType::CharPtr(StrEnc::Text))), as_string(char_array));
531531
}
532532
}
533533
}

binding-generator/src/type_ref/types.rs

+11
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,17 @@ impl TypeRefTypeHint {
4444
}
4545
}
4646

47+
/// Filters current TypeRef type hint to make it suitable for inner type e.g. for the pointee
48+
///
49+
/// Useful for example to strip the nullability from the inner type of a pointer
50+
pub fn recurse(self) -> Self {
51+
match self {
52+
Self::Nullable => Self::None,
53+
Self::NullableSlice => Self::Slice,
54+
recursable => recursable,
55+
}
56+
}
57+
4758
pub fn nullability(&self) -> Nullability {
4859
match self {
4960
TypeRefTypeHint::Nullable | TypeRefTypeHint::NullableSlice => Nullability::Nullable,

binding-generator/src/writer/rust_native/type_ref.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,14 @@ impl TypeRefExt for TypeRef<'_, '_> {
9393
}
9494
kind => {
9595
let (indirection, tref_kind, tref) = match kind {
96-
TypeRefKind::Pointer(pointee) => (Indirection::Pointer, pointee.kind().into_owned(), Owned(pointee)),
97-
TypeRefKind::Reference(pointee) => (Indirection::Reference, pointee.kind().into_owned(), Owned(pointee)),
96+
TypeRefKind::Pointer(pointee) => {
97+
let pointee = pointee.with_type_hint(self.type_hint().clone());
98+
(Indirection::Pointer, pointee.kind().into_owned(), Owned(pointee))
99+
}
100+
TypeRefKind::Reference(pointee) => {
101+
let pointee = pointee.with_type_hint(self.type_hint().clone());
102+
(Indirection::Reference, pointee.kind().into_owned(), Owned(pointee))
103+
}
98104
kind => (Indirection::None, kind, Borrowed(self)),
99105
};
100106
match tref_kind.canonical().into_owned() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#define CV_EXPORTS
2+
3+
namespace cv {
4+
5+
{{code}}
6+
7+
}

binding-generator/tests/generation.rs

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
use std::fs::File;
2+
use std::io::Write;
3+
use std::path::Path;
4+
5+
use clang::diagnostic::Severity;
6+
use clang::{Clang, Entity, Index};
7+
use opencv_binding_generator::writer::rust_native::element::RustNativeGeneratedElement;
8+
use opencv_binding_generator::{EntityWalkerExt, Func, GeneratorEnv, GeneratorVisitor, OpenCvWalker};
9+
use tempfile::TempDir;
10+
11+
fn clang_parse(code: &str, op: impl FnOnce(Entity)) {
12+
const CODE_TPL: &str = include_str!("code_template.cpp");
13+
const CODE_PAT: &str = "{{code}}";
14+
15+
let temp_dir = TempDir::new().expect("Can't create temp dir");
16+
let temp_file_path = temp_dir.path().join("temp.cpp");
17+
if let Some(start) = CODE_TPL.find(CODE_PAT) {
18+
let mut temp_cpp = File::create(&temp_file_path).expect("Can't create temp file");
19+
temp_cpp
20+
.write_all(CODE_TPL[..start].as_bytes())
21+
.expect("Can't write to temp file");
22+
temp_cpp.write_all(code.as_bytes()).expect("Can't write to temp file");
23+
temp_cpp
24+
.write_all(CODE_TPL[start + CODE_PAT.len()..].as_bytes())
25+
.expect("Can't write to temp file");
26+
}
27+
let clang = Clang::new().expect("Can't init clang");
28+
let index = Index::new(&clang, false, false);
29+
let root_tu = index
30+
.parser(&temp_file_path)
31+
.skip_function_bodies(true)
32+
.detailed_preprocessing_record(true)
33+
.parse()
34+
.expect("Can't parse");
35+
let diags = root_tu.get_diagnostics();
36+
if !diags.is_empty() {
37+
let mut has_error = false;
38+
eprintln!("WARNING: {} diagnostic messages", diags.len());
39+
for diag in diags {
40+
if !has_error && matches!(diag.get_severity(), Severity::Error | Severity::Fatal) {
41+
has_error = true;
42+
}
43+
eprintln!(" {diag}");
44+
}
45+
if has_error {
46+
panic!("Errors during header parsing");
47+
}
48+
}
49+
op(root_tu.get_entity());
50+
}
51+
52+
fn extract_functions(code: &str, cb: impl FnMut(Func)) {
53+
struct FunctionExtractor<F> {
54+
cb: F,
55+
}
56+
57+
impl<F: FnMut(Func)> GeneratorVisitor<'_> for FunctionExtractor<F> {
58+
fn visit_func(&mut self, func: Func) {
59+
(self.cb)(func);
60+
}
61+
}
62+
63+
clang_parse(code, |root_tu| {
64+
let gen_env = GeneratorEnv::empty();
65+
let visitor = FunctionExtractor { cb };
66+
let opencv_walker = OpenCvWalker::new("core", Path::new(""), visitor, gen_env);
67+
68+
root_tu.walk_opencv_entities(opencv_walker);
69+
});
70+
}
71+
72+
#[test]
73+
fn char_ptr_slice() {
74+
extract_functions("CV_EXPORTS int startLoop(int argc, char* argv[]);", |f| {
75+
assert_eq!(f.gen_rust("0.0.0").trim(), "#[inline]\npub fn start_loop(argv: &mut [&str]) -> Result<i32> {\n\tstring_array_arg_mut!(argv);\n\treturn_send!(via ocvrs_return);\n\tunsafe { sys::cv_startLoop_int_charXX(argv.len().try_into()?, argv.as_mut_ptr(), ocvrs_return.as_mut_ptr()) };\n\treturn_receive!(unsafe ocvrs_return => ret);\n\tlet ret = ret.into_result()?;\n\tOk(ret)\n}");
76+
assert_eq!(f.gen_cpp().trim(), "void cv_startLoop_int_charXX(int argc, char** argv, Result<int>* ocvrs_return) {\n\ttry {\n\t\tint ret = cv::startLoop(argc, argv);\n\t\tOk(ret, ocvrs_return);\n\t} OCVRS_CATCH(ocvrs_return);\n}");
77+
assert_eq!(
78+
f.gen_rust_externs().trim(),
79+
r#"pub fn cv_startLoop_int_charXX(argc: i32, argv: *mut *mut c_char, ocvrs_return: *mut Result<i32>);"#,
80+
);
81+
});
82+
}

0 commit comments

Comments
 (0)