diff --git a/c++_tests/c++/main.cpp b/c++_tests/c++/main.cpp index c6b894dc..1e4bce8f 100644 --- a/c++_tests/c++/main.cpp +++ b/c++_tests/c++/main.cpp @@ -353,8 +353,20 @@ TEST(TestOptional, smokeTest) ASSERT_FALSE(!!val2); } - EXPECT_NEAR(10., x.f4({5.}), std::numeric_limits::epsilon()); + EXPECT_NEAR(10., x.f4({ 5. }), std::numeric_limits::epsilon()); EXPECT_NEAR(-1., x.f4({}), std::numeric_limits::epsilon()); + + { + auto val = x.f5(true); + ASSERT_TRUE(!!val); + FooRef foo = std::move(*val); + EXPECT_EQ(5, foo.f(0, 0)); + EXPECT_EQ(std::string("aaa"), foo.getName().to_std_string()); + } + { + auto foo = x.f5(false); + EXPECT_FALSE(!!foo); + } } TEST(TestResult, smokeTest) diff --git a/c++_tests/src/lib.rs.in b/c++_tests/src/lib.rs.in index 287bd3a2..38b887ca 100644 --- a/c++_tests/src/lib.rs.in +++ b/c++_tests/src/lib.rs.in @@ -365,9 +365,17 @@ foreigner_class!(class TestPassPathAsParam { method TestPassPathAsParam::path(&self) -> &str; }); -#[derive(Default)] -pub struct TestOptional {} +pub struct TestOptional { + foo: Foo, +} +impl Default for TestOptional { + fn default() -> TestOptional { + TestOptional { + foo: Foo::new(5, "aaa"), + } + } +} impl TestOptional { fn f1(&self, ret_notnull: bool) -> Option { if ret_notnull { @@ -395,6 +403,13 @@ impl TestOptional { fn f4(&self, a: Option) -> f64 { a.map(|v| v * 2.).unwrap_or(-1.) } + fn f5(&self, x: bool) -> Option<&Foo> { + if x { + Some(&self.foo) + } else { + None + } + } } foreigner_class!(class TestOptional { @@ -404,6 +419,7 @@ foreigner_class!(class TestOptional { method TestOptional::f2(&self, ret_notnull: bool) -> Option; method TestOptional::f3(&self, ret_notnull: bool) -> Option; method TestOptional::f4(&self, _: Option) -> f64; + method TestOptional::f5(&self, x: bool) -> Option<&Foo>; }); #[derive(Default)] diff --git a/macroslib/src/cpp/map_type.rs b/macroslib/src/cpp/map_type.rs index 5ffbb789..10727186 100644 --- a/macroslib/src/cpp/map_type.rs +++ b/macroslib/src/cpp/map_type.rs @@ -8,7 +8,8 @@ use petgraph::Direction; use my_ast::{code_to_item, if_option_return_some_type, if_result_return_ok_err_types, if_vec_return_elem_type, normalized_ty_string, parse_ty, RustType}; use errors::fatal_error; -use types_conv_map::{ForeignTypeInfo, FROM_VAR_TEMPLATE}; +use types_conv_map::{make_unique_rust_typename, ForeignTypeInfo, FROM_VAR_TEMPLATE, + TO_VAR_TEMPLATE}; use {CppConfig, CppOptional, CppVariant, ForeignEnumInfo, ForeignerClassInfo, TypesConvMap}; use cpp::{CppConverter, CppForeignTypeInfo}; use cpp::cpp_code::c_class_type; @@ -571,6 +572,86 @@ fn handle_option_type_in_result<'a>( }), })); } + + //handle Option<&ForeignClass> case + if let ast::TyKind::Rptr( + _, + ast::MutTy { + ty: ref under_ref_ty, + mutbl: ast::Mutability::Immutable, + }, + ) = opt_ty.node + { + if let Some(fclass) = conv_map + .find_foreigner_class_with_such_self_type(under_ref_ty, false) + .map(|v| v.clone()) + { + let foreign_info = + foreign_class_foreign_name(sess, conv_map, &fclass, under_ref_ty.span, false)?; + let this_type_for_method = fclass.this_type_for_method.as_ref().ok_or_else(|| { + fatal_error( + sess, + fclass.span, + &format!( + "Class {} (namespace {}) return as reference, but there is no constructor", + fclass.name, cpp_cfg.namespace_name, + ), + ) + })?; + let this_type: RustType = this_type_for_method.clone().into(); + let void_ptr_typename = Symbol::intern("*mut ::std::os::raw::c_void"); + let my_void_ptr_ti = RustType::new( + parse_ty(sess, DUMMY_SP, void_ptr_typename)?, + make_unique_rust_typename(void_ptr_typename, this_type.normalized_name), + ); + let arg_rust_ty: RustType = arg_ty.clone().into(); + conv_map.add_type(arg_rust_ty.clone()); + conv_map.add_conversation_rule( + arg_rust_ty, + my_void_ptr_ti, + Symbol::intern(&format!( + r#" + let {to_var}: *mut ::std::os::raw::c_void = match {from_var} {{ + Some(x) => x as *const {self_type} as *mut ::std::os::raw::c_void, + None => ::std::ptr::null_mut(), + }}; +"#, + to_var = TO_VAR_TEMPLATE, + from_var = FROM_VAR_TEMPLATE, + self_type = this_type.normalized_name, + )).into(), + ); + + let (typename, output_converter) = match cpp_cfg.cpp_optional { + CppOptional::Std17 => ( + Symbol::intern(&format!("std::optional<{}Ref>", fclass.name)), + format!( + "{var} != nullptr ? {Type}Ref({var}) : std::optional<{Type}Ref>()", + Type = fclass.name, + var = FROM_VAR_TEMPLATE, + ), + ), + CppOptional::Boost => ( + Symbol::intern(&format!("boost::optional<{}Ref>", fclass.name)), + format!( + "{var} != nullptr ? {Type}Ref({var}) : boost::optional<{Type}Ref>()", + Type = fclass.name, + var = FROM_VAR_TEMPLATE, + ), + ), + }; + return Ok(Some(CppForeignTypeInfo { + base: foreign_info, + c_converter: String::new(), + cpp_converter: Some(CppConverter { + typename, + output_converter, + input_converter: "#error".to_string(), + }), + })); + } + } + let mut cpp_info_opt = map_ordinal_result_type(sess, conv_map, arg_ty)?; let cpp_info_ty = map_ordinal_result_type(sess, conv_map, opt_ty)?; let f_opt_ty = cpp_info_ty.base.name; diff --git a/macroslib/src/types_conv_map/mod.rs b/macroslib/src/types_conv_map/mod.rs index 33ec1192..86e72341 100644 --- a/macroslib/src/types_conv_map/mod.rs +++ b/macroslib/src/types_conv_map/mod.rs @@ -780,7 +780,7 @@ impl TypesConvMap { debug!("TypesConvMap::add_conversation_rule {} -> {}", from, to); let from = get_graph_node(&mut self.conv_graph, &mut self.rust_names_map, from); let to = get_graph_node(&mut self.conv_graph, &mut self.rust_names_map, to); - self.conv_graph.add_edge(from, to, rule); + self.conv_graph.update_edge(from, to, rule); } pub(crate) fn register_exported_enum(&mut self, enum_info: &ForeignEnumInfo) { diff --git a/macroslib/tests/test_complex_cases.rs b/macroslib/tests/test_complex_cases.rs index 99959b28..eef77102 100644 --- a/macroslib/tests/test_complex_cases.rs +++ b/macroslib/tests/test_complex_cases.rs @@ -651,6 +651,7 @@ foreigner_class!(class Foo { method Foo::f2(&self) -> Option; method Foo::f3(&self) -> Option; method Foo::f4(&self) -> Option; + method Foo::f5(&self) -> Option<&Boo>; }); "#, &[ForeignLang::Cpp], @@ -681,6 +682,11 @@ foreigner_class!(class Foo { .foreign_code .contains("std::optional f4()") ); + assert!( + cpp_code_pair + .foreign_code + .contains("std::optional f5()") + ); } #[test]