已合并
docs(hicc): README与reference补充owned_ptr使用文档, 移除into_unique方案 #130
hunting创建于 9月10日
docs(hicc): README与reference补充owned_ptr使用文档, 移除into_unique方案 #130
已合并
共 13 个文件变更+622-32
| @@ -360,7 +360,10 @@ impl FuncInfo { | |||
| 360 | 360 | ||
| 361 | Some(Self { | 361 | Some(Self { |
| 362 | is_dynamic_cast: matches!(sig.marker, Some(cpp::Marker::DynamicCast)), | 362 | is_dynamic_cast: matches!(sig.marker, Some(cpp::Marker::DynamicCast)), |
| 363 | - is_make_proxy: matches!(sig.marker, Some(cpp::Marker::MakeProxy { leading: true, .. })), | 363 | + is_make_proxy: matches!( |
| 364 | + sig.marker, | ||
| 365 | + Some(cpp::Marker::MakeProxy { leading: true, .. }) | ||
| 366 | + ), | ||
| 364 | is_embedded_proxy: false, | 367 | is_embedded_proxy: false, |
| 365 | is_variadic: sig.variadic, | 368 | is_variadic: sig.variadic, |
| 366 | is_ref_self, | 369 | is_ref_self, |
| @@ -406,6 +409,15 @@ pub(super) struct Function<'a> { | |||
| 406 | 409 | ||
| 407 | impl<'a> Function<'a> { | 410 | impl<'a> Function<'a> { |
| 408 | pub fn try_from(f: &'a ImportFn) -> syn::Result<Self> { | 411 | pub fn try_from(f: &'a ImportFn) -> syn::Result<Self> { |
| 412 | + Self::try_from_with(f, None) | ||
| 413 | + } | ||
| 414 | + | ||
| 415 | + /// `self_alias`: impl/class 块内提升函数(带 member 属性)的 `Self` 占位符替身名。 | ||
| 416 | + /// 仅自由函数导出路径(ExportLib::export)传入; 类成员方法的 `Self` 由类适配 | ||
| 417 | + /// 作用域的 `typedef X Self;` 解析, 类管线经 `try_from` 传 None。 | ||
| 418 | + /// 替换必须在签名解析前完成, 使后续按字节偏移的切片(ret_str/限定名/参数名) | ||
| 419 | + /// 与替换后的新串一致。 | ||
| 420 | + pub fn try_from_with(f: &'a ImportFn, self_alias: Option<&str>) -> syn::Result<Self> { | ||
| 409 | let Some(attr) = Attr::get_attr("cpp", &f.attrs) else { | 421 | let Some(attr) = Attr::get_attr("cpp", &f.attrs) else { |
| 410 | return Err(syn::Error::new(f.ident.span(), "not found #[cpp(...)]")); | 422 | return Err(syn::Error::new(f.ident.span(), "not found #[cpp(...)]")); |
| 411 | }; | 423 | }; |
| @@ -435,6 +447,11 @@ impl<'a> Function<'a> { | |||
| 435 | }; | 447 | }; |
| 436 | let line = attr.span().start().line; | 448 | let line = attr.span().start().line; |
| 437 | let input = input.trim(); | 449 | let input = input.trim(); |
| 450 | + let input = match self_alias { | ||
| 451 | + Some(alias) => crate::utils::replace_ident_token(input, "Self", alias), | ||
| 452 | + None => input.to_string(), | ||
| 453 | + }; | ||
| 454 | + let input = input.as_str(); | ||
| 438 | // 嵌入形态: `@make_proxy<T>` 非首 token 时替换为 proxy 具体类型, 其余按普通函数导出 | 455 | // 嵌入形态: `@make_proxy<T>` 非首 token 时替换为 proxy 具体类型, 其余按普通函数导出 |
| 439 | let raw = input; | 456 | let raw = input; |
| 440 | let input = Self::substitute_embedded_proxy(input); | 457 | let input = Self::substitute_embedded_proxy(input); |
| @@ -481,7 +498,10 @@ impl<'a> Function<'a> { | |||
| 481 | 498 | ||
| 482 | /// `R T<..>::f(...)` 的 `T`(末段标识符, 去模板实参) | 499 | /// `R T<..>::f(...)` 的 `T`(末段标识符, 去模板实参) |
| 483 | pub fn cpp_qualifier(&self) -> Option<String> { | 500 | pub fn cpp_qualifier(&self) -> Option<String> { |
| 484 | - self.sig.name.qualifier_bare_name(&self.input).map(str::to_string) | 501 | + self.sig |
| 502 | + .name | ||
| 503 | + .qualifier_bare_name(&self.input) | ||
| 504 | + .map(str::to_string) | ||
| 485 | } | 505 | } |
| 486 | 506 | ||
| 487 | pub fn with_protected(mut self, protected: bool) -> Self { | 507 | pub fn with_protected(mut self, protected: bool) -> Self { |
| @@ -600,7 +620,11 @@ impl<'a> Function<'a> { | |||
| 600 | let scope = self.scope(); | 620 | let scope = self.scope(); |
| 601 | let line = self.line; | 621 | let line = self.line; |
| 602 | 622 | ||
| 603 | - let ptr_scope = if self.cpp_info.is_member { "Self::" } else { "" }; | 623 | + let ptr_scope = if self.cpp_info.is_member { |
| 624 | + "Self::" | ||
| 625 | + } else { | ||
| 626 | + "" | ||
| 627 | + }; | ||
| 604 | let name = self.qualified_name(); | 628 | let name = self.qualified_name(); |
| 605 | let after_name = &self.input[self.sig.name.span.end..]; | 629 | let after_name = &self.input[self.sig.name.span.end..]; |
| 606 | Ok(format!( | 630 | Ok(format!( |
| @@ -786,10 +810,10 @@ impl<'a> Function<'a> { | |||
| 786 | if sig.qualifiers_span.end != self.input.len() { | 810 | if sig.qualifiers_span.end != self.input.len() { |
| 787 | return Err(syn::Error::new(self.span, self.error_dynamic())); | 811 | return Err(syn::Error::new(self.span, self.error_dynamic())); |
| 788 | } | 812 | } |
| 789 | - let path_of = |ty: &cpp::Type| -> Option<String> { | 813 | + let path_of = |
| 790 | - Some(ty.class_path()?.to_string(&self.input)) | 814 | + |ty: &cpp::Type| -> Option<String> { Some(ty.class_path()?.to_string(&self.input)) }; |
| 791 | - }; | 815 | + let ret = |
| 792 | - let ret = path_of(&sig.ret).ok_or_else(|| syn::Error::new(self.span, self.error_dynamic()))?; | 816 | + path_of(&sig.ret).ok_or_else(|| syn::Error::new(self.span, self.error_dynamic()))?; |
| 793 | let input = sig | 817 | let input = sig |
| 794 | .params | 818 | .params |
| 795 | .first() | 819 | .first() |
| @@ -1,7 +1,7 @@ | |||
| 1 | use crate::{Attr, ImportClass, ImportFn, ImportLib}; | 1 | use crate::{Attr, ImportClass, ImportFn, ImportLib}; |
| 2 | 2 | ||
| 3 | -mod token; | ||
| 4 | pub(crate) mod cpp; | 3 | pub(crate) mod cpp; |
| 4 | +mod token; | ||
| 5 | pub(crate) use function::Arg; | 5 | pub(crate) use function::Arg; |
| 6 | use token::*; | 6 | use token::*; |
| 7 | 7 | ||
| @@ -74,7 +74,9 @@ impl ImportLib { | |||
| 74 | let Some(member) = Attr::get_attr("member", &f.attrs) else { | 74 | let Some(member) = Attr::get_attr("member", &f.attrs) else { |
| 75 | continue; | 75 | continue; |
| 76 | }; | 76 | }; |
| 77 | - let (Ok(Some(cls)), Ok(Some(method))) = (member.value("class"), member.value("method")) else { | 77 | + let (Ok(Some(cls)), Ok(Some(method))) = |
| 78 | + (member.value("class"), member.value("method")) | ||
| 79 | + else { | ||
| 78 | continue; | 80 | continue; |
| 79 | }; | 81 | }; |
| 80 | if cpp::strip_template_args(cls).trim() == class_ident { | 82 | if cpp::strip_template_args(cls).trim() == class_ident { |
| @@ -267,11 +269,33 @@ impl ExportLib { | |||
| 267 | "#line {}\nEXPORT_METHODS_BEG({}) {{\n", | 269 | "#line {}\nEXPORT_METHODS_BEG({}) {{\n", |
| 268 | self.line, self.link | 270 | self.line, self.link |
| 269 | )); | 271 | )); |
| 272 | + // impl/class 块内提升函数的 Self 替身 typedef: 注入导出表 struct 体内。 | ||
| 273 | + // 成员 typedef 非数据成员, 不影响 const void* 平铺布局(rust 侧 #[repr(C)] | ||
| 274 | + // 字段对位不变); NSDMI 条目初始化处于 complete-class context, 可见该 typedef。 | ||
| 275 | + codes.push_str(&self.self_typedefs()); | ||
| 270 | 276 | ||
| 271 | let statics = self.imported.collect_protected_statics()?; | 277 | let statics = self.imported.collect_protected_statics()?; |
| 272 | for f in self.imported.funcs.iter() { | 278 | for f in self.imported.funcs.iter() { |
| 273 | let is_protected = statics.iter().any(|(_, p)| p.ident == f.ident); | 279 | let is_protected = statics.iter().any(|(_, p)| p.ident == f.ident); |
| 274 | - let f = Function::try_from(f)?.with_protected(is_protected); | 280 | + // 泛型类实例的 Self 无法解析: 条目以空指针占位, 保持导出表 ABI 对位 |
| 281 | + // 与 build.rs 阶段 C++ 可编译性; Rust 侧由 generate() 宏级编译错误拦截。 | ||
| 282 | + // 非 member 函数的裸 Self 维持既有失败路径, 不在此占位。 | ||
| 283 | + let member_class = Attr::get_attr("member", &f.attrs) | ||
| 284 | + .and_then(|m| m.value("class").ok().flatten().map(str::to_string)); | ||
| 285 | + let unresolvable = ImportLib::cpp_sig_has_self(f) | ||
| 286 | + && member_class | ||
| 287 | + .as_deref() | ||
| 288 | + .map(|c| self.imported.cpp_type_of(c).is_none()) | ||
| 289 | + .unwrap_or(false); | ||
| 290 | + if unresolvable { | ||
| 291 | + codes.push_str("EXPORT_METHOD_IN(void, ExportMethods, (0));\n"); | ||
| 292 | + continue; | ||
| 293 | + } | ||
| 294 | + let self_alias = member_class | ||
| 295 | + .as_deref() | ||
| 296 | + .filter(|c| self.imported.cpp_type_of(c).is_some()) | ||
| 297 | + .map(crate::utils::self_alias_ident); | ||
| 298 | + let f = Function::try_from_with(f, self_alias.as_deref())?.with_protected(is_protected); | ||
| 275 | codes.push_str(&f.export_test()?); | 299 | codes.push_str(&f.export_test()?); |
| 276 | codes.push_str(&f.export()?); | 300 | codes.push_str(&f.export()?); |
| 277 | } | 301 | } |
| @@ -279,6 +303,35 @@ impl ExportLib { | |||
| 279 | codes.push_str(&format!("#line {}\n}} EXPORT_METHODS_END();\n", self.line)); | 303 | codes.push_str(&format!("#line {}\n}} EXPORT_METHODS_END();\n", self.line)); |
| 280 | Ok(codes) | 304 | Ok(codes) |
| 281 | } | 305 | } |
| 306 | + | ||
| 307 | + /// 收集 cpp 串实际含整词 `Self` 占位符的提升函数(member 属性), 按类去重后 | ||
| 308 | + /// 输出 `typedef {Cpp类型} {别名};` 行。无 Self 的 member 函数(如 hicc-std | ||
| 309 | + /// 类内提升)不发 typedef, 保证其生成物与既有输出一致。 | ||
| 310 | + fn self_typedefs(&self) -> String { | ||
| 311 | + let mut codes = String::new(); | ||
| 312 | + let mut seen: Vec<String> = vec![]; | ||
| 313 | + for f in &self.imported.funcs { | ||
| 314 | + if !ImportLib::cpp_sig_has_self(f) { | ||
| 315 | + continue; | ||
| 316 | + } | ||
| 317 | + let Some(member) = Attr::get_attr("member", &f.attrs) else { | ||
| 318 | + continue; | ||
| 319 | + }; | ||
| 320 | + let Ok(Some(class)) = member.value("class") else { | ||
| 321 | + continue; | ||
| 322 | + }; | ||
| 323 | + let alias = crate::utils::self_alias_ident(class); | ||
| 324 | + if seen.contains(&alias) { | ||
| 325 | + continue; | ||
| 326 | + } | ||
| 327 | + let Some(cpp_ty) = self.imported.cpp_type_of(class) else { | ||
| 328 | + continue; | ||
| 329 | + }; | ||
| 330 | + codes.push_str(&format!("typedef {} {};\n", cpp_ty, alias)); | ||
| 331 | + seen.push(alias); | ||
| 332 | + } | ||
| 333 | + codes | ||
| 334 | + } | ||
| 282 | } | 335 | } |
| 283 | 336 | ||
| 284 | 337 | ||
| @@ -970,3 +970,143 @@ fn make_proxy_embedded_multi_template_args_params_match_single() { | |||
| 970 | "模板实参数量不影响形参注入: {multi}" | 970 | "模板实参数量不影响形参注入: {multi}" |
| 971 | ); | 971 | ); |
| 972 | } | 972 | } |
| 973 | + | ||
| 974 | + | ||
| 975 | +fn impl_block_self_uses_unique_alias() { | ||
| 976 | + let lib = parse_lib( | ||
| 977 | + r#" | ||
| 978 | + | ||
| 979 | + | ||
| 980 | + class Foo {} | ||
| 981 | + impl Foo { | ||
| 982 | + | ||
| 983 | + pub fn make_clone(this: &Self) -> Self; | ||
| 984 | + } | ||
| 985 | + "#, | ||
| 986 | + ); | ||
| 987 | + let codes = lib.export().unwrap(); | ||
| 988 | + assert!( | ||
| 989 | + codes.contains("typedef Foo _hicc_self_Foo;"), | ||
| 990 | + "导出表 struct 体内应注入按类唯一的替身 typedef: {codes}" | ||
| 991 | + ); | ||
| 992 | + assert!( | ||
| 993 | + codes.contains( | ||
| 994 | + "(_hicc_self_Foo (*)(const _hicc_self_Foo&))&hicc::make_constructor<_hicc_self_Foo>" | ||
| 995 | + ), | ||
| 996 | + "条目 C++ 签名中的 Self 应整词替换为替身: {codes}" | ||
| 997 | + ); | ||
| 998 | + assert!( | ||
| 999 | + codes.contains("_hicc_self_Foo (* _"), | ||
| 1000 | + "取址测试函数同样使用替身: {codes}" | ||
| 1001 | + ); | ||
| 1002 | + assert!( | ||
| 1003 | + !codes.contains("make_constructor<Self>") && !codes.contains("(Self (*)(const Self&))"), | ||
| 1004 | + "导出表内不应残留裸 Self: {codes}" | ||
| 1005 | + ); | ||
| 1006 | + assert!( | ||
| 1007 | + codes.contains("typedef Foo Self;"), | ||
| 1008 | + "类适配区仍走类内 typedef Self 机制, 不受影响: {codes}" | ||
| 1009 | + ); | ||
| 1010 | + assert!( | ||
| 1011 | + codes.contains( | ||
| 1012 | + "((_hicc_self_Foo (*)(const _hicc_self_Foo&))&hicc::make_constructor<_hicc_self_Foo>)" | ||
| 1013 | + ), | ||
| 1014 | + "条目 C++ 签名中的 Self 应整词替换为替身: {codes}" | ||
| 1015 | + ); | ||
| 1016 | +} | ||
| 1017 | + | ||
| 1018 | + | ||
| 1019 | +fn impl_block_multiple_classes_get_distinct_aliases() { | ||
| 1020 | + let lib = parse_lib( | ||
| 1021 | + r#" | ||
| 1022 | + | ||
| 1023 | + | ||
| 1024 | + class Foo {} | ||
| 1025 | + | ||
| 1026 | + class Bar {} | ||
| 1027 | + impl Foo { | ||
| 1028 | + | ||
| 1029 | + pub fn make_foo(this: &Self) -> Self; | ||
| 1030 | + } | ||
| 1031 | + impl Bar { | ||
| 1032 | + | ||
| 1033 | + pub fn make_bar(this: &Self) -> Self; | ||
| 1034 | + } | ||
| 1035 | + "#, | ||
| 1036 | + ); | ||
| 1037 | + let codes = lib.export().unwrap(); | ||
| 1038 | + assert!(codes.contains("typedef Foo _hicc_self_Foo;"), "{codes}"); | ||
| 1039 | + assert!(codes.contains("typedef Bar _hicc_self_Bar;"), "{codes}"); | ||
| 1040 | + assert!( | ||
| 1041 | + !codes.contains("make_constructor<Self>"), | ||
| 1042 | + "多类并存时导出表内不得残留裸 Self: {codes}" | ||
| 1043 | + ); | ||
| 1044 | +} | ||
| 1045 | + | ||
| 1046 | + | ||
| 1047 | +fn member_fn_without_self_token_keeps_cpp_string() { | ||
| 1048 | + let lib = parse_lib( | ||
| 1049 | + r#" | ||
| 1050 | + | ||
| 1051 | + | ||
| 1052 | + class Foo {} | ||
| 1053 | + impl Foo { | ||
| 1054 | + | ||
| 1055 | + pub fn make(v: i32) -> Self; | ||
| 1056 | + } | ||
| 1057 | + "#, | ||
| 1058 | + ); | ||
| 1059 | + let codes = lib.export().unwrap(); | ||
| 1060 | + assert!( | ||
| 1061 | + codes.contains("(Foo (*)(int))&hicc::make_constructor<Foo>"), | ||
| 1062 | + "无 Self 占位符的 cpp 串应原样重构, 不受替身机制影响: {codes}" | ||
| 1063 | + ); | ||
| 1064 | +} | ||
| 1065 | + | ||
| 1066 | + | ||
| 1067 | +fn generic_impl_self_uses_placeholder_entry() { | ||
| 1068 | + let lib = parse_lib( | ||
| 1069 | + r#" | ||
| 1070 | + | ||
| 1071 | + | ||
| 1072 | + class Foo<T> {} | ||
| 1073 | + impl Foo<i32> { | ||
| 1074 | + | ||
| 1075 | + pub fn make_clone(this: &Self) -> Self; | ||
| 1076 | + } | ||
| 1077 | + "#, | ||
| 1078 | + ); | ||
| 1079 | + let codes = lib.export().unwrap(); | ||
| 1080 | + assert!( | ||
| 1081 | + codes.contains("EXPORT_METHOD_IN(void, ExportMethods, (0));"), | ||
| 1082 | + "泛型实例条目以空指针占位, 保持导出表 ABI 对位: {codes}" | ||
| 1083 | + ); | ||
| 1084 | + assert!( | ||
| 1085 | + !codes.contains("_hicc_self_"), | ||
| 1086 | + "泛型实例不发替身 typedef 也不替换别名(类适配区的 typedef Self 不受影响): {codes}" | ||
| 1087 | + ); | ||
| 1088 | + assert!( | ||
| 1089 | + !codes.contains("_hicc_test_6"), | ||
| 1090 | + "占位条目不生成取址测试函数: {codes}" | ||
| 1091 | + ); | ||
| 1092 | +} | ||
| 1093 | + | ||
| 1094 | + | ||
| 1095 | +fn generic_impl_with_concrete_cpp_ty_exports_normally() { | ||
| 1096 | + let lib = parse_lib( | ||
| 1097 | + r#" | ||
| 1098 | + | ||
| 1099 | + | ||
| 1100 | + class Foo<T> {} | ||
| 1101 | + impl Foo<i32> { | ||
| 1102 | + | ||
| 1103 | + pub fn make_clone(this: &Self) -> Self; | ||
| 1104 | + } | ||
| 1105 | + "#, | ||
| 1106 | + ); | ||
| 1107 | + let codes = lib.export().unwrap(); | ||
| 1108 | + assert!( | ||
| 1109 | + codes.contains("((Foo<int> (*)(const Foo<int>&))&hicc::make_constructor<Foo<int>>)"), | ||
| 1110 | + "直写具体类型的泛型 impl 条目正常发射: {codes}" | ||
| 1111 | + ); | ||
| 1112 | +} | ||
| @@ -116,6 +116,7 @@ impl ImportLib { | |||
| 116 | return Err(syn::Error::new(f.ident.span(), msg)); | 116 | return Err(syn::Error::new(f.ident.span(), msg)); |
| 117 | } | 117 | } |
| 118 | } | 118 | } |
| 119 | + self.check_self_usages()?; | ||
| 119 | let mut codes = self | 120 | let mut codes = self |
| 120 | .items | 121 | .items |
| 121 | .iter() | 122 | .iter() |
| @@ -441,6 +442,82 @@ impl ImportLib { | |||
| 441 | issues | 442 | issues |
| 442 | } | 443 | } |
| 443 | 444 | ||
| 445 | + /// 函数 cpp 串是否含整词 `Self` 占位符。 | ||
| 446 | + /// 提升函数(impl/class 块关联函数)的 cpp 属性恒为 `func` 键(method 仅用于 | ||
| 447 | + /// 带 receiver 的类成员方法, 不进入提升路径)。 | ||
| 448 | + pub(crate) fn cpp_sig_has_self(f: &ImportFn) -> bool { | ||
| 449 | + let Some(attr) = Attr::get_attr("cpp", &f.attrs) else { | ||
| 450 | + return false; | ||
| 451 | + }; | ||
| 452 | + attr.value("func") | ||
| 453 | + .ok() | ||
| 454 | + .flatten() | ||
| 455 | + .is_some_and(|v| crate::utils::replace_ident_token(v, "Self", "") != v) | ||
| 456 | + } | ||
| 457 | + | ||
| 458 | + /// member(class = ...) 字符串对应的 C++ 类型名: base 类名(去模板实参)命中 | ||
| 459 | + /// DSL 声明类时取其 `#[cpp(class = ...)]` 值; 模板模式(以 template 开头) | ||
| 460 | + /// 无具体实例可 typedef, 返回 None 跳过; 未在 DSL 声明的类型按 member 原文 | ||
| 461 | + /// 使用(由用户保证 C++ 可见性, 与 cpp 串直写具体类型的既有契约一致)。 | ||
| 462 | + pub(crate) fn cpp_type_of(&self, class_str: &str) -> Option<String> { | ||
| 463 | + let base = class_str.split('<').next().unwrap_or(class_str); | ||
| 464 | + for c in &self.classes { | ||
| 465 | + if c.ident == base { | ||
| 466 | + let v = Attr::get_attr("cpp", &c.attrs) | ||
| 467 | + .and_then(|a| a.value("class").ok().flatten().map(|v| v.to_string())) | ||
| 468 | + .unwrap_or_else(|| c.ident.to_string()); | ||
| 469 | + return if v.trim_start().starts_with("template") { | ||
| 470 | + None | ||
| 471 | + } else { | ||
| 472 | + Some(v) | ||
| 473 | + }; | ||
| 474 | + } | ||
| 475 | + } | ||
| 476 | + Some(class_str.to_string()) | ||
| 477 | + } | ||
| 478 | + | ||
| 479 | + /// 泛型类(模板模式声明)实例的 impl 块函数在 cpp 串使用 `Self` 占位符: | ||
| 480 | + /// 导出表作用域无法解析出具体模板实例, 宏展开期即报 Rust 编译错误 | ||
| 481 | + /// (指向 cpp 属性所在行, 先于 build.rs 的 C++ 编译), 指引在 cpp 串中 | ||
| 482 | + /// 直写具体 C++ 类型(如 `Foo<int>`)。多个违规函数聚合为一个错误。 | ||
| 483 | + pub(crate) fn check_self_usages(&self) -> parse::Result<()> { | ||
| 484 | + let mut errs: Option<syn::Error> = None; | ||
| 485 | + for f in &self.funcs { | ||
| 486 | + if !Self::cpp_sig_has_self(f) { | ||
| 487 | + continue; | ||
| 488 | + } | ||
| 489 | + let Some(member) = Attr::get_attr("member", &f.attrs) else { | ||
| 490 | + continue; | ||
| 491 | + }; | ||
| 492 | + let Ok(Some(class)) = member.value("class") else { | ||
| 493 | + continue; | ||
| 494 | + }; | ||
| 495 | + if self.cpp_type_of(class).is_some() { | ||
| 496 | + continue; | ||
| 497 | + } | ||
| 498 | + let span = Attr::get_attr("cpp", &f.attrs) | ||
| 499 | + .map(|a| a.span()) | ||
| 500 | + .unwrap_or_else(|| f.ident.span()); | ||
| 501 | + let e = syn::Error::new( | ||
| 502 | + span, | ||
| 503 | + format!( | ||
| 504 | + "`Self` placeholder in cpp signature is not supported for generic class `{class}`: write the concrete C++ type (e.g. `Foo<int>`) instead" | ||
| 505 | + ), | ||
| 506 | + ); | ||
| 507 | + errs = Some(match errs { | ||
| 508 | + Some(mut prev) => { | ||
| 509 | + prev.combine(e); | ||
| 510 | + prev | ||
| 511 | + } | ||
| 512 | + None => e, | ||
| 513 | + }); | ||
| 514 | + } | ||
| 515 | + match errs { | ||
| 516 | + Some(e) => Err(e), | ||
| 517 | + None => Ok(()), | ||
| 518 | + } | ||
| 519 | + } | ||
| 520 | + | ||
| 444 | /// 在已解析的 DSL 类声明(classes)中, 按类名查找直接 trait。 | 521 | /// 在已解析的 DSL 类声明(classes)中, 按类名查找直接 trait。 |
| 445 | fn derive_direct_intf(&self, class: &syn::Path) -> Option<&syn::Ident> { | 522 | fn derive_direct_intf(&self, class: &syn::Path) -> Option<&syn::Ident> { |
| 446 | let name = class.segments.last()?.ident.clone(); | 523 | let name = class.segments.last()?.ident.clone(); |
| @@ -1379,3 +1379,62 @@ fn lib_make_proxy_embedded_multi_template_args_bad_bound_errs() { | |||
| 1379 | }; | 1379 | }; |
| 1380 | assert!(err.contains("not a direct or super trait"), "{err}"); | 1380 | assert!(err.contains("not a direct or super trait"), "{err}"); |
| 1381 | } | 1381 | } |
| 1382 | + | ||
| 1383 | + | ||
| 1384 | +fn generic_impl_self_placeholder_is_compile_error() { | ||
| 1385 | + let lib: ImportLib = syn::parse_str( | ||
| 1386 | + r#" | ||
| 1387 | + | ||
| 1388 | + | ||
| 1389 | + class Foo<T> {} | ||
| 1390 | + impl Foo<i32> { | ||
| 1391 | + | ||
| 1392 | + pub fn make_clone(this: &Self) -> Self; | ||
| 1393 | + } | ||
| 1394 | + "#, | ||
| 1395 | + ) | ||
| 1396 | + .unwrap(); | ||
| 1397 | + let err = lib.generate().unwrap_err().to_string(); | ||
| 1398 | + assert!( | ||
| 1399 | + err.contains("make_clone") || err.contains("Foo<i32>"), | ||
| 1400 | + "错误应含指引信息: {err}" | ||
| 1401 | + ); | ||
| 1402 | + assert!( | ||
| 1403 | + err.contains("write the concrete C++ type"), | ||
| 1404 | + "错误应指引直写具体 C++ 类型: {err}" | ||
| 1405 | + ); | ||
| 1406 | +} | ||
| 1407 | + | ||
| 1408 | + | ||
| 1409 | +fn generic_impl_concrete_cpp_ty_no_error() { | ||
| 1410 | + let lib: ImportLib = syn::parse_str( | ||
| 1411 | + r#" | ||
| 1412 | + | ||
| 1413 | + | ||
| 1414 | + class Foo<T> {} | ||
| 1415 | + impl Foo<i32> { | ||
| 1416 | + | ||
| 1417 | + pub fn make_clone(this: &Self) -> Self; | ||
| 1418 | + } | ||
| 1419 | + "#, | ||
| 1420 | + ) | ||
| 1421 | + .unwrap(); | ||
| 1422 | + lib.generate().unwrap(); | ||
| 1423 | +} | ||
| 1424 | + | ||
| 1425 | + | ||
| 1426 | +fn non_generic_impl_self_no_error() { | ||
| 1427 | + let lib: ImportLib = syn::parse_str( | ||
| 1428 | + r#" | ||
| 1429 | + | ||
| 1430 | + | ||
| 1431 | + class Foo {} | ||
| 1432 | + impl Foo { | ||
| 1433 | + | ||
| 1434 | + pub fn make_clone(this: &Self) -> Self; | ||
| 1435 | + } | ||
| 1436 | + "#, | ||
| 1437 | + ) | ||
| 1438 | + .unwrap(); | ||
| 1439 | + lib.generate().unwrap(); | ||
| 1440 | +} | ||
| @@ -82,6 +82,54 @@ pub fn mangle_member_ident(class: &str, instance: Option<&str>, name: &str) -> s | |||
| 82 | } | 82 | } |
| 83 | } | 83 | } |
| 84 | 84 | ||
| 85 | +/// impl/class 块内提升为自由函数的关联函数, 其 `#[cpp(func/method)]` 串中的 `Self` | ||
| 86 | +/// 占位符在导出表(自由函数)作用域的替身名: `_hicc_self_{class}`。 | ||
| 87 | +/// class 为 `#[member(class = ...)]` 字符串(已去空白); 非标识符字符净化为 `_`, | ||
| 88 | +/// 保证不同泛型实例(`Foo<i32,i32>` 与 `Foo<i32,f64>`)互不冲突。 | ||
| 89 | +/// 配对机制: ExportLib::export 在导出表 struct 体内注入 `typedef {Cpp类型} {别名};`, | ||
| 90 | +/// 类型本身(可能含逗号)只出现在 typedef 定义处, 不经过 C++ 预处理器宏参数切分。 | ||
| 91 | +pub fn self_alias_ident(class_str: &str) -> String { | ||
| 92 | + let sanitized: String = class_str | ||
| 93 | + .chars() | ||
| 94 | + .map(|c| { | ||
| 95 | + if c.is_alphanumeric() || c == '_' { | ||
| 96 | + c | ||
| 97 | + } else { | ||
| 98 | + '_' | ||
| 99 | + } | ||
| 100 | + }) | ||
| 101 | + .collect(); | ||
| 102 | + format!("_hicc_self_{}", sanitized) | ||
| 103 | +} | ||
| 104 | + | ||
| 105 | +/// 整词替换标识符 `word`(前后均非 [A-Za-z0-9_]), 不误伤 `MySelf`/`SelfX` 等子串。 | ||
| 106 | +/// 多字节 UTF-8 序列整体拷贝, 不破坏非 ASCII 内容。 | ||
| 107 | +pub fn replace_ident_token(s: &str, word: &str, alias: &str) -> String { | ||
| 108 | + let b = s.as_bytes(); | ||
| 109 | + let wb = word.as_bytes(); | ||
| 110 | + let is_ident_ch = |c: u8| c.is_ascii_alphanumeric() || c == b'_'; | ||
| 111 | + let mut out = String::with_capacity(s.len() + alias.len()); | ||
| 112 | + let mut i = 0; | ||
| 113 | + while i < b.len() { | ||
| 114 | + if b[i..].starts_with(wb) | ||
| 115 | + && (i == 0 || !is_ident_ch(b[i - 1])) | ||
| 116 | + && (i + wb.len() == b.len() || !is_ident_ch(b[i + wb.len()])) | ||
| 117 | + { | ||
| 118 | + out.push_str(alias); | ||
| 119 | + i += wb.len(); | ||
| 120 | + } else { | ||
| 121 | + let start = i; | ||
| 122 | + i += 1; | ||
| 123 | + // 连续拷贝 UTF-8 续字节, 保持多字节字符完整 | ||
| 124 | + while i < b.len() && (b[i] & 0b1100_0000) == 0b1000_0000 { | ||
| 125 | + i += 1; | ||
| 126 | + } | ||
| 127 | + out.push_str(&s[start..i]); | ||
| 128 | + } | ||
| 129 | + } | ||
| 130 | + out | ||
| 131 | +} | ||
| 132 | + | ||
| 85 | pub fn last_seg(ty: &syn::Type) -> Option<&syn::PathSegment> { | 133 | pub fn last_seg(ty: &syn::Type) -> Option<&syn::PathSegment> { |
| 86 | match ty { | 134 | match ty { |
| 87 | syn::Type::Path(tp) => tp.path.segments.last(), | 135 | syn::Type::Path(tp) => tp.path.segments.last(), |
| @@ -119,3 +167,40 @@ pub fn stable_hex(input: &str) -> String { | |||
| 119 | h.write(input.as_bytes()); | 167 | h.write(input.as_bytes()); |
| 120 | format!("{:016x}", h.finish()) | 168 | format!("{:016x}", h.finish()) |
| 121 | } | 169 | } |
| 170 | + | ||
| 171 | + | ||
| 172 | +mod tests { | ||
| 173 | + use super::*; | ||
| 174 | + | ||
| 175 | + | ||
| 176 | + fn self_alias_ident_sanitizes_generic_args() { | ||
| 177 | + assert_eq!(self_alias_ident("Foo"), "_hicc_self_Foo"); | ||
| 178 | + assert_eq!(self_alias_ident("Foo<i32,i32>"), "_hicc_self_Foo_i32_i32_"); | ||
| 179 | + assert_ne!( | ||
| 180 | + self_alias_ident("Foo<i32,i32>"), | ||
| 181 | + self_alias_ident("Foo<i32,f64>"), | ||
| 182 | + "不同泛型实例的别名必须互异" | ||
| 183 | + ); | ||
| 184 | + } | ||
| 185 | + | ||
| 186 | + | ||
| 187 | + fn replace_ident_token_whole_word_only() { | ||
| 188 | + assert_eq!(replace_ident_token("Self", "Self", "X"), "X"); | ||
| 189 | + assert_eq!(replace_ident_token("const Self&", "Self", "X"), "const X&"); | ||
| 190 | + assert_eq!( | ||
| 191 | + replace_ident_token("make_constructor<Self>", "Self", "X"), | ||
| 192 | + "make_constructor<X>" | ||
| 193 | + ); | ||
| 194 | + assert_eq!( | ||
| 195 | + replace_ident_token("MySelf SelfX Self", "Self", "X"), | ||
| 196 | + "MySelf SelfX X", | ||
| 197 | + "标识符子串不得误伤" | ||
| 198 | + ); | ||
| 199 | + assert_eq!(replace_ident_token("SelfSelf", "Self", "X"), "SelfSelf"); | ||
| 200 | + assert_eq!( | ||
| 201 | + replace_ident_token("/* Self 中文 */ Self", "Self", "X"), | ||
| 202 | + "/* X 中文 */ X", | ||
| 203 | + "多字节 UTF-8 内容不受影响" | ||
| 204 | + ); | ||
| 205 | + } | ||
| 206 | +} | ||
| @@ -1,5 +1,3 @@ | |||
| 1 | -use hicc::AbiClass; | ||
| 2 | - | ||
| 3 | hicc::cpp! { | 1 | hicc::cpp! { |
| 4 | 2 | ||
| 5 | class Foo { | 3 | class Foo { |
| @@ -31,7 +29,13 @@ hicc::import_class! { | |||
| 31 | hicc::import_lib! { | 29 | hicc::import_lib! { |
| 32 | 30 | ||
| 33 | 31 | ||
| 34 | - #[cpp(func = "Foo* Foo::new_instance()")] | 32 | + // 将返回类型包装为hicc::owned_ptr<T>, Rust侧获得C++资源的所有权. |
| 33 | + cpp! { | ||
| 34 | + static hicc::owned_ptr<Foo> new_foo() { | ||
| 35 | + return Foo::new_instance(); | ||
| 36 | + } | ||
| 37 | + } | ||
| 38 | + | ||
| 35 | fn foo_new() -> Foo; | 39 | fn foo_new() -> Foo; |
| 36 | } | 40 | } |
| 37 | 41 | ||
| @@ -40,9 +44,7 @@ fn main() { | |||
| 40 | 44 | ||
| 41 | let mut foo = Foo::new(); | 45 | let mut foo = Foo::new(); |
| 42 | foo.bar(); | 46 | foo.bar(); |
| 43 | - let mut foo = unsafe { foo.into_unique() }; | ||
| 44 | foo.bar(); | 47 | foo.bar(); |
| 45 | - std::mem::drop(foo); | ||
| 46 | println!("exit"); | 48 | println!("exit"); |
| 47 | 49 | ||
| 48 | println!("=== [destroy] DONE ==="); | 50 | println!("=== [destroy] DONE ==="); |
| @@ -30,7 +30,8 @@ hicc::cpp! { | |||
| 30 | // protected 构造 + protected 析构: | 30 | // protected 构造 + protected 析构: |
| 31 | // - 构造: 只能经子类构造, @make_proxy 的 proxy 即子类, 天然可见; | 31 | // - 构造: 只能经子类构造, @make_proxy 的 proxy 即子类, 天然可见; |
| 32 | // - 析构: 首 token 形态 @make_proxy 经 std::unique_ptr<T> 返回, 其 default_delete | 32 | // - 析构: 首 token 形态 @make_proxy 经 std::unique_ptr<T> 返回, 其 default_delete |
| 33 | - // 要求 ~T 可访问, 不兼容; 改用嵌入形态返回裸指针 + destroy 指定 protected 释放函数. | 33 | + // 要求 ~T 可访问, 不兼容; 改用嵌入形态返回 hicc::owned_ptr<Guarded> 传递资源 |
| 34 | + // 所有权 + destroy 指定 protected 释放函数. | ||
| 34 | struct Guarded { | 35 | struct Guarded { |
| 35 | int tag; | 36 | int tag; |
| 36 | virtual int vfn(int v) const { | 37 | virtual int vfn(int v) const { |
| @@ -46,7 +47,7 @@ hicc::cpp! { | |||
| 46 | } | 47 | } |
| 47 | }; | 48 | }; |
| 48 | template <class P, class... A> | 49 | template <class P, class... A> |
| 49 | - static Guarded* new_guarded_raw(A&&... args) { return new P(std::forward<A>(args)...); } | 50 | + static hicc::owned_ptr<Guarded> new_guarded(A&&... args) { return new P(std::forward<A>(args)...); } |
| 50 | 51 | ||
| 51 | // 模板类: 同三种 protected 成员, 类型含模板参数 | 52 | // 模板类: 同三种 protected 成员, 类型含模板参数 |
| 52 | template <typename T> | 53 | template <typename T> |
| @@ -110,8 +111,9 @@ hicc::import_lib! { | |||
| 110 | 111 | ||
| 111 | struct Guarded: GuardedTrait {} | 112 | struct Guarded: GuardedTrait {} |
| 112 | 113 | ||
| 113 | - // 嵌入形态返回裸指针, 绕开 std::unique_ptr 对 ~T 的可访问性要求 | 114 | + // 嵌入形态返回 hicc::owned_ptr<Guarded>, C++ 侧适配并传递资源所有权, |
| 114 | - #[cpp(func = "Guarded* new_guarded_raw<@make_proxy<Guarded>>(int)")] | 115 | + // 绕开 std::unique_ptr 对 ~T 的可访问性要求 |
| 116 | + | ||
| 115 | fn new_guarded<I: GuardedTrait>(v: I, tag: i32) -> Guarded; | 117 | fn new_guarded<I: GuardedTrait>(v: I, tag: i32) -> Guarded; |
| 116 | 118 | ||
| 117 | 119 | ||
| @@ -178,14 +180,12 @@ fn main() { | |||
| 178 | assert_eq!(p.nfn(1), 1); | 180 | assert_eq!(p.nfn(1), 1); |
| 179 | assert!(p.as_rust::<RustBase>().is_some()); | 181 | assert!(p.as_rust::<RustBase>().is_some()); |
| 180 | 182 | ||
| 181 | - // protected 构造/析构: 经 proxy 构造; 裸指针返回为借用语义, into_unique 接管 | 183 | + // protected 构造/析构: 经 proxy 构造; 工厂返回 owned_ptr 传递所有权, |
| 182 | - // 所有权后 drop 经 Deleter<Guarded> → 桥 → protected free 释放 | 184 | + // drop 经 destroy 桥 → protected free 释放 |
| 183 | { | 185 | { |
| 184 | let g = new_guarded(RustGuarded, 7); | 186 | let g = new_guarded(RustGuarded, 7); |
| 185 | assert_eq!(g.vfn(3), -3); | 187 | assert_eq!(g.vfn(3), -3); |
| 186 | assert_eq!(g.as_super().vfn(3), 10); | 188 | assert_eq!(g.as_super().vfn(3), 10); |
| 187 | - let owned = unsafe { g.into_unique() }; | ||
| 188 | - drop(owned); | ||
| 189 | } | 189 | } |
| 190 | 190 | ||
| 191 | // 模板类: protected = "*" 全量 | 191 | // 模板类: protected = "*" 全量 |
| @@ -123,6 +123,7 @@ fn main() { | |||
| 123 | - **方法(有 `self`)**:以 `obj.fn_name(...)` 调用 | 123 | - **方法(有 `self`)**:以 `obj.fn_name(...)` 调用 |
| 124 | - **泛型类**:支持 `class Generic<T> { ... }` 语法 | 124 | - **泛型类**:支持 `class Generic<T> { ... }` 语法 |
| 125 | - **同名冲突**:不同 `class` 中的关联函数可同名,互不影响 | 125 | - **同名冲突**:不同 `class` 中的关联函数可同名,互不影响 |
| 126 | +- **`Self` 占位符**:cpp 串中的 `Self` 仅**非泛型类**支持;泛型类实例(如 `impl Generic<int>`)的 cpp 串须直写具体 C++ 类型(如 `Generic<int>*`),使用 `Self` 会导致 Rust 编译错误(宏展开期报错,指向 cpp 属性所在行) | ||
| 126 | 127 | ||
| 127 | ```rust | 128 | ```rust |
| 128 | hicc::cpp! { | 129 | hicc::cpp! { |
| @@ -492,14 +493,18 @@ impl Base { | |||
| 492 | 493 | ||
| 493 | 名字在解析期校验:未知名字、类自身方法误入 `pure`、静态函数不在 `impl` 块内、`T::` 前缀不匹配均报错。模板类同样支持。 | 494 | 名字在解析期校验:未知名字、类自身方法误入 `pure`、静态函数不在 `impl` 块内、`T::` 前缀不匹配均报错。模板类同样支持。 |
| 494 | 495 | ||
| 495 | -**构造与析构**:protected 构造函数对 `@make_proxy` 直接可用。protected 析构函数须经 `destroy = "Foo::free"` 释放(`free` 可列入 `protected`);此时首 token 形态 `T @make_proxy<T>(..)` 不适用(其 `std::unique_ptr<T>` 要求 `~T` 可访问),改用嵌入形态裸指针工厂 + `into_unique()` 接管所有权: | 496 | +**构造与析构**:protected 构造函数对 `@make_proxy` 直接可用。protected 析构函数须经 `destroy = "Foo::free"` 释放(`free` 可列入 `protected`);此时首 token 形态 `T @make_proxy<T>(..)` 不适用(其 `std::unique_ptr<T>` 要求 `~T` 可访问),改用嵌入形态工厂,并在 C++ 侧返回 `hicc::owned_ptr<Foo>` 传递资源所有权(推荐做法,参见上文「传递 C++ 资源所有权」): |
| 496 | 497 | ||
| 497 | ```rust | 498 | ```rust |
| 498 | #[cpp(class = "Foo", ctor = "Foo(int)", destroy = "Foo::free", protected = "free")] | 499 | #[cpp(class = "Foo", ctor = "Foo(int)", destroy = "Foo::free", protected = "free")] |
| 499 | struct Foo: FooTrait {} | 500 | struct Foo: FooTrait {} |
| 500 | -#[cpp(func = "Foo* my_new<@make_proxy<Foo>>(int)")] | 501 | + |
| 502 | +// C++ 侧适配函数返回 owned_ptr 传递所有权: | ||
| 503 | +// template <class P, class... A> | ||
| 504 | +// static hicc::owned_ptr<Foo> my_new(A&&... args) { return new P(std::forward<A>(args)...); } | ||
| 505 | +#[cpp(func = "hicc::owned_ptr<Foo> my_new<@make_proxy<Foo>>(int)")] | ||
| 501 | fn new_foo<I: FooTrait>(v: I, tag: i32) -> Foo; | 506 | fn new_foo<I: FooTrait>(v: I, tag: i32) -> Foo; |
| 502 | -// let owned = unsafe { new_foo(impl, 7).into_unique() }; // drop → Foo::free → ~Foo | 507 | +// let foo = new_foo(impl, 7); // 拥有所有权, drop → Foo::free → ~Foo |
| 503 | ``` | 508 | ``` |
| 504 | 509 | ||
| 505 | 完整示例见 `hicc-examples/protected`(具体类 + 模板类,虚/非虚/静态 + protected 构造/析构)。 | 510 | 完整示例见 `hicc-examples/protected`(具体类 + 模板类,虚/非虚/静态 + protected 构造/析构)。 |
| @@ -535,6 +540,65 @@ hicc::import_lib! { | |||
| 535 | 540 | ||
| 536 | 工厂泛型 `bound` 可为直接接口或任一祖先(以类视角实参书写)。 | 541 | 工厂泛型 `bound` 可为直接接口或任一祖先(以类视角实参书写)。 |
| 537 | 542 | ||
| 543 | +## 传递 C++ 资源所有权:`hicc::owned_ptr<T>` | ||
| 544 | + | ||
| 545 | +当 C++ 类的析构函数非公开(private/protected),必须经独立的静态函数释放资源时,用 `#[cpp(class = ..., destroy = ...)]` 声明释放函数;此时 C++ 侧资源创建函数的返回值应包装为 `hicc::owned_ptr<T>`(定义于 `<hicc/std/memory.hpp>`),标记返回的指针**拥有资源所有权**。Rust 侧接口的返回类型直接声明为映射的类类型,Rust 对象析构时自动调用 `destroy` 释放函数,避免资源泄露: | ||
| 546 | + | ||
| 547 | +```rust | ||
| 548 | +hicc::cpp! { | ||
| 549 | + class Foo { | ||
| 550 | + ~Foo() { | ||
| 551 | + std::cout << "Foo::~Foo" << std::endl; | ||
| 552 | + } | ||
| 553 | + public: | ||
| 554 | + static Foo* new_instance() { return new Foo; } | ||
| 555 | + static void free_instance(Foo* foo) { delete foo; } | ||
| 556 | + void bar() const { | ||
| 557 | + std::cout << "Foo::bar" << std::endl; | ||
| 558 | + } | ||
| 559 | + }; | ||
| 560 | +} | ||
| 561 | + | ||
| 562 | +hicc::import_class! { | ||
| 563 | + #[cpp(class = "Foo", destroy = "Foo::free_instance")] | ||
| 564 | + class Foo { | ||
| 565 | + #[cpp(method = "void bar() const")] | ||
| 566 | + fn bar(&mut self); | ||
| 567 | + fn new() -> Self { | ||
| 568 | + foo_new() | ||
| 569 | + } | ||
| 570 | + } | ||
| 571 | +} | ||
| 572 | + | ||
| 573 | +hicc::import_lib! { | ||
| 574 | + #![link_name = "example"] | ||
| 575 | + | ||
| 576 | + // 将返回类型包装为hicc::owned_ptr<T>, Rust侧获得C++资源的所有权. | ||
| 577 | + cpp! { | ||
| 578 | + static hicc::owned_ptr<Foo> new_foo() { | ||
| 579 | + return Foo::new_instance(); | ||
| 580 | + } | ||
| 581 | + } | ||
| 582 | + #[cpp(func = "hicc::owned_ptr<Foo> new_foo()")] | ||
| 583 | + fn foo_new() -> Foo; | ||
| 584 | +} | ||
| 585 | + | ||
| 586 | +fn main() { | ||
| 587 | + let mut foo = Foo::new(); // C++ 侧 Foo::new_instance() 创建 | ||
| 588 | + foo.bar(); | ||
| 589 | +} // foo 析构 → Foo::free_instance → ~Foo | ||
| 590 | +``` | ||
| 591 | + | ||
| 592 | +规则: | ||
| 593 | + | ||
| 594 | +| 位置 | 写法 | | ||
| 595 | +|---|---| | ||
| 596 | +| C++ 适配函数返回值 | `hicc::owned_ptr<T>`,标记所有权 | | ||
| 597 | +| Rust 接口返回类型 | 映射的类类型(如 `Foo`),析构时经 `destroy` 自动释放 | | ||
| 598 | +| C++ 适配函数参数 | **禁止**使用 `hicc::owned_ptr<T>`(仅用于返回值) | | ||
| 599 | + | ||
| 600 | +> `@make_proxy` 的 protected 析构场景同样推荐在 C++ 侧返回 `hicc::owned_ptr<T>` 传递所有权,参见上文「调用 C++ protected 成员」。完整示例参见 `examples/destroy`。 | ||
| 601 | + | ||
| 538 | ## 数据类型支持 | 602 | ## 数据类型支持 |
| 539 | 603 | ||
| 540 | | 数据类型 | 支持 | 备注 | | 604 | | 数据类型 | 支持 | 备注 | |
| @@ -548,6 +612,7 @@ hicc::import_lib! { | |||
| 548 | | `const T**` 多重指针 | ✅ | 程序员管理生命周期 | | 612 | | `const T**` 多重指针 | ✅ | 程序员管理生命周期 | |
| 549 | | `T**` 多重指针 | ✅ | 程序员管理生命周期 | | 613 | | `T**` 多重指针 | ✅ | 程序员管理生命周期 | |
| 550 | | `std::function<R(ArgTypes...)>` | ✅ | | | 614 | | `std::function<R(ArgTypes...)>` | ✅ | | |
| 615 | +| `hicc::owned_ptr<T>` | ✅ | 仅用于返回值,Rust 侧获得资源所有权,析构经 `destroy` 释放 | | ||
| 551 | 616 | ||
| 552 | ## 函数类型支持 | 617 | ## 函数类型支持 |
| 553 | 618 | ||
| @@ -638,6 +703,7 @@ build.flag("/GR-"); | |||
| 638 | | `examples/template-interface` | 泛型接口:参数少/多/命名不同/同形参多次绑定/部分实例化/`string` 同名别名/祖先链/工厂绑定祖先 | | 703 | | `examples/template-interface` | 泛型接口:参数少/多/命名不同/同形参多次绑定/部分实例化/`string` 同名别名/祖先链/工厂绑定祖先 | |
| 639 | | `examples/scratch-super` | super 视图最小示例 | | 704 | | `examples/scratch-super` | super 视图最小示例 | |
| 640 | | `examples/protected` | 调用 C++ protected 成员:虚/非虚/静态,具体类与模板类 | | 705 | | `examples/protected` | 调用 C++ protected 成员:虚/非虚/静态,具体类与模板类 | |
| 706 | +| `examples/destroy` | `hicc::owned_ptr<T>` 传递资源所有权:私有析构类经 `destroy` 释放 | | ||
| 641 | | `examples/no-exceptions` | `-fno-exceptions` 下编译:`hicc::Exception<T>` 退化直通、`import_class` 方法包装、`@make_proxy` 回调 | | 707 | | `examples/no-exceptions` | `-fno-exceptions` 下编译:`hicc::Exception<T>` 退化直通、`import_class` 方法包装、`@make_proxy` 回调 | |
| 642 | | `examples/no-rtti` | `-fno-rtti -fno-exceptions` 下编译:首 token `@make_proxy` 六种形态 `as_super`/`as_rust` 有效、派生引用保留标记、嵌入形态/C++ 重包装/普通对象视为非 proxy、无泄漏 | | 708 | | `examples/no-rtti` | `-fno-rtti -fno-exceptions` 下编译:首 token `@make_proxy` 六种形态 `as_super`/`as_rust` 有效、派生引用保留标记、嵌入形态/C++ 重包装/普通对象视为非 proxy、无泄漏 | |
| 643 | | `examples/placement_new` | 在 Rust 内存空间中构造 C++ 对象 | | 709 | | `examples/placement_new` | 在 Rust 内存空间中构造 C++ 对象 | |
| @@ -113,6 +113,48 @@ struct AbiValue<std::unique_ptr<T>> { | |||
| 113 | static output_type into_with(std::function<std::unique_ptr<T>()> fun) { return into(fun()); } | 113 | static output_type into_with(std::function<std::unique_ptr<T>()> fun) { return into(fun()); } |
| 114 | }; | 114 | }; |
| 115 | 115 | ||
| 116 | +// 对于部分类型其析构是非pub或者限制不能直接调用delete T,他们定义了Delete<T>的特化版本 | ||
| 117 | +// 但是资源创建函数返回的始终是T*,为了区分这个指针是否具有资源所有权,特意定义如下辅助类型 | ||
| 118 | +// | ||
| 119 | +// 使用方式(样例参见hicc-examples/destroy): | ||
| 120 | +// 1. C++类的析构函数非public时, 通过`#[cpp(class = ..., destroy = "T::free")]`声明释放函数; | ||
| 121 | +// 2. C++侧资源创建函数将返回类型定义为owned_ptr<T>, 标记返回的指针拥有资源所有权; | ||
| 122 | +// 3. Rust侧对应接口的返回类型直接声明为映射的类类型(如`fn new_foo() -> Foo`), | ||
| 123 | +// Rust对象析构时自动调用destroy函数释放资源, 避免泄露. | ||
| 124 | +// | ||
| 125 | +// 对于使用者,只需要将C++返回类型定义为owned_ptr<T>即可保证Rust侧可正确释放其资源,避免泄露. | ||
| 126 | +// 注: 它应该仅用于返回值,不应该用于任何函数的参数. | ||
| 127 | +template<typename T> | ||
| 128 | +struct owned_ptr { | ||
| 129 | + const T* ptr; | ||
| 130 | + owned_ptr(const T* p): ptr(p) {} | ||
| 131 | +}; | ||
| 132 | + | ||
| 133 | +template<typename T> | ||
| 134 | +struct MethodsType<owned_ptr<T>> { | ||
| 135 | + typedef typename MethodsType<T>::type type; | ||
| 136 | +}; | ||
| 137 | + | ||
| 138 | +template<typename T> | ||
| 139 | +struct AbiValue<owned_ptr<T>> { | ||
| 140 | + typedef AbiClass<T> input_type; | ||
| 141 | + typedef AbiClass<T> output_type; | ||
| 142 | + static owned_ptr<T> from(input_type val) { | ||
| 143 | + // 绝对不应该用于参数类型. | ||
| 144 | + return (const T*)val.get_obj(); | ||
| 145 | + } | ||
| 146 | + static owned_ptr<T> from_with(std::function<input_type()> fun) { | ||
| 147 | + return from(fun()); | ||
| 148 | + } | ||
| 149 | + static output_type into(owned_ptr<T> val) { | ||
| 150 | + return output_type(AbiClass<T>::destroy_methods(), (T*)val.ptr); | ||
| 151 | + } | ||
| 152 | + static output_type into_with(std::function<owned_ptr<T>()> fun) { | ||
| 153 | + return into(fun()); | ||
| 154 | + } | ||
| 155 | +}; | ||
| 156 | + | ||
| 157 | + | ||
| 116 | } // namespace hicc | 158 | } // namespace hicc |
| 117 | 159 | ||
| 118 | 160 | ||
| @@ -556,6 +556,8 @@ fn main() { | |||
| 556 | **说明**: | 556 | **说明**: |
| 557 | 557 | ||
| 558 | 1. C++类的私有析构函数需要利用`#[cpp(class = ..., destroy = ...)]`定义. | 558 | 1. C++类的私有析构函数需要利用`#[cpp(class = ..., destroy = ...)]`定义. |
| 559 | +1. C++侧资源创建函数的返回值应包装为`hicc::owned_ptr<T>`(定义于`hicc/std/memory.hpp`), 表示返回的指针拥有资源所有权; 对应Rust接口的返回类型直接声明为映射的类类型, Rust对象析构时自动调用`destroy`函数释放资源, 避免泄露. | ||
| 560 | +1. `hicc::owned_ptr<T>`仅用于返回值, 不应该用于任何函数的参数. | ||
| 559 | 561 | ||
| 560 | ## 读写C++变量 | 562 | ## 读写C++变量 |
| 561 | 563 | ||
| @@ -610,3 +612,25 @@ hicc::import_class! { | |||
| 610 | 612 | ||
| 611 | 1. 如在`class`中定义,只能是静态函数,静态函数所在的类空间名是`SelfMethods`, 具体的C++类型可由`Self`引用. | 613 | 1. 如在`class`中定义,只能是静态函数,静态函数所在的类空间名是`SelfMethods`, 具体的C++类型可由`Self`引用. |
| 612 | 2. 因为是成员函数,首参数必须是`Self`或其引用或其指针. | 614 | 2. 因为是成员函数,首参数必须是`Self`或其引用或其指针. |
| 615 | + | ||
| 616 | +## impl块关联函数与`Self`占位符 | ||
| 617 | + | ||
| 618 | +`import_lib!` 中 `impl` 块的关联函数会被提取为自由函数导出,其 `#[cpp(func = ...)]` 串中的 `Self` 由生成器整词替换为按类唯一的别名,并在导出表作用域注入 `typedef {C++类型} {别名};` 解析. | ||
| 619 | + | ||
| 620 | +```rust! | ||
| 621 | +hicc::import_lib! { | ||
| 622 | + #![link_name = "example"] | ||
| 623 | + #[cpp(class = "Foo")] | ||
| 624 | + class Foo {} | ||
| 625 | + impl Foo { | ||
| 626 | + #[cpp(func = "Self hicc::make_constructor<Self>(const Self&)")] | ||
| 627 | + pub fn make_clone(this: &Self) -> Self; | ||
| 628 | + } | ||
| 629 | +} | ||
| 630 | +``` | ||
| 631 | + | ||
| 632 | +**注意**: | ||
| 633 | + | ||
| 634 | +1. `Self` 占位符仅支持**非泛型类**;泛型类实例(如 `impl Foo<i32>`)在导出表作用域无法解析出具体模板实例. | ||
| 635 | +2. 泛型场景请在 cpp 串中直写具体 C++ 类型(如 `Foo<int>`),使用 `Self` 会导致 Rust 编译错误(宏展开期报错,指向 cpp 属性所在行). | ||
| 636 | +3. cpp 串直写具体类型时,若该类型未在 DSL 中声明,由使用者保证其在生成代码作用域可见(与既有契约一致). | ||
| @@ -17,6 +17,9 @@ pub trait AbiClass: Sized { | |||
| 17 | /// | 17 | /// |
| 18 | /// 对应到`c++`接口返回指针的应用场景.业务需要主动释放指针分配的资源时调用. | 18 | /// 对应到`c++`接口返回指针的应用场景.业务需要主动释放指针分配的资源时调用. |
| 19 | /// 需要确保此时`self`是资源的唯一引用者. | 19 | /// 需要确保此时`self`是资源的唯一引用者. |
| 20 | + /// | ||
| 21 | + /// 注意: 不再推荐使用此接口接管资源所有权, 建议在`c++`侧将适配函数的返回值 | ||
| 22 | + /// 包装为`hicc::owned_ptr<T>`传递所有权(参见`examples/destroy`样例). | ||
| 20 | unsafe fn into_unique(self) -> Self; | 23 | unsafe fn into_unique(self) -> Self; |
| 21 | /// | 24 | /// |
| 22 | /// 更新对象本身. | 25 | /// 更新对象本身. |
| @@ -451,7 +451,11 @@ | |||
| 451 | /// `_hicc_Foo_new`、`_hicc_Generic_hicc_Pod_i32_create`),`_hicc_` 前缀 | 451 | /// `_hicc_Foo_new`、`_hicc_Generic_hicc_Pod_i32_create`),`_hicc_` 前缀 |
| 452 | /// 预留内部命名空间,避免与用户自由函数冲突; | 452 | /// 预留内部命名空间,避免与用户自由函数冲突; |
| 453 | /// 5. 泛型 `impl`(`impl<T> Foo<T>`)与 `trait impl` 一律透传; | 453 | /// 5. 泛型 `impl`(`impl<T> Foo<T>`)与 `trait impl` 一律透传; |
| 454 | -/// 泛型类的**具体实例化**(`impl Generic<hicc::Pod<i32>>`)支持。 | 454 | +/// 泛型类的**具体实例化**(`impl Generic<hicc::Pod<i32>>`)支持; |
| 455 | +/// 6. cpp 串中的 `Self` 占位符仅**非泛型类**支持(导出表作用域注入 | ||
| 456 | +/// `typedef {C++类型} {别名};` 解析);泛型类实例无法解析出具体 | ||
| 457 | +/// 模板实例,cpp 串必须直写具体 C++ 类型(如样例的 `Generic<int>*`), | ||
| 458 | +/// 使用 `Self` 会导致 Rust 编译错误(宏展开期报错,指向 cpp 属性所在行)。 | ||
| 455 | /// | 459 | /// |
| 456 | /// 样例: | 460 | /// 样例: |
| 457 | /// | 461 | /// |
| @@ -726,12 +730,19 @@ pub use hicc_macros::import_lib; | |||
| 726 | /// 需要如下定义: | 730 | /// 需要如下定义: |
| 727 | /// ```text | 731 | /// ```text |
| 728 | /// hicc::import_class! { | 732 | /// hicc::import_class! { |
| 729 | -/// #[cpp(class = "Foo", destroy = "Foo::free_intance")] | 733 | +/// #[cpp(class = "Foo", destroy = "Foo::free_instance")] |
| 730 | /// class Foo { | 734 | /// class Foo { |
| 731 | /// } | 735 | /// } |
| 732 | /// } | 736 | /// } |
| 733 | /// ``` | 737 | /// ``` |
| 734 | /// | 738 | /// |
| 739 | +/// 此时C++侧资源创建函数的返回值应包装为`hicc::owned_ptr<T>`(定义于`hicc/std/memory.hpp`), | ||
| 740 | +/// 表示返回的指针拥有资源所有权; 对应`rust`接口的返回类型直接声明为映射的类类型, | ||
| 741 | +/// `rust`对象析构时自动调用`destroy`函数释放资源, 避免泄露. | ||
| 742 | +/// | ||
| 743 | +/// 注意: `hicc::owned_ptr<T>`仅用于返回值, 不应该用于任何函数的参数. | ||
| 744 | +/// 完整样例参见`examples/destroy`. | ||
| 745 | +/// | ||
| 735 | /// ## `interface` / `trait`关键字 | 746 | /// ## `interface` / `trait`关键字 |
| 736 | /// | 747 | /// |
| 737 | /// 接口(纯抽象类)映射为`rust`的`trait`. | 748 | /// 接口(纯抽象类)映射为`rust`的`trait`. |
| @@ -831,15 +842,19 @@ pub use hicc_macros::import_lib; | |||
| 831 | /// 直接实例化`c++`本就不允许, 改走静态工厂(工厂为`protected`时列入`protected`即可). | 842 | /// 直接实例化`c++`本就不允许, 改走静态工厂(工厂为`protected`时列入`protected`即可). |
| 832 | /// - `protected`析构函数: `destroy = "Foo::free"`的目标函数可列入`protected`(经桥调用). | 843 | /// - `protected`析构函数: `destroy = "Foo::free"`的目标函数可列入`protected`(经桥调用). |
| 833 | /// 首 token 形态`T @make_proxy<T>(..)`经`std::unique_ptr<T>`返回, 其`default_delete` | 844 | /// 首 token 形态`T @make_proxy<T>(..)`经`std::unique_ptr<T>`返回, 其`default_delete` |
| 834 | -/// 要求`~T`可访问, 不兼容; 改用嵌入形态返回裸指针(借用语义, `into_unique`接管后 | 845 | +/// 要求`~T`可访问, 不兼容; 改用嵌入形态工厂, C++侧适配函数返回`hicc::owned_ptr<Foo>` |
| 835 | -/// 经`Deleter`释放): | 846 | +/// 传递资源所有权(推荐做法, 参见`destroy`属性说明与`examples/destroy`样例): |
| 836 | /// | 847 | /// |
| 837 | /// ```text | 848 | /// ```text |
| 838 | /// #[cpp(class = "Foo", ctor = "Foo(int)", destroy = "Foo::free", protected = "free")] | 849 | /// #[cpp(class = "Foo", ctor = "Foo(int)", destroy = "Foo::free", protected = "free")] |
| 839 | /// struct Foo: FooTrait {} | 850 | /// struct Foo: FooTrait {} |
| 840 | -/// #[cpp(func = "Foo* my_new<@make_proxy<Foo>>(int)")] | 851 | +/// |
| 852 | +/// // c++侧适配函数返回 owned_ptr 传递所有权: | ||
| 853 | +/// // template <class P, class... A> | ||
| 854 | +/// // static hicc::owned_ptr<Foo> my_new(A&&... args) { return new P(std::forward<A>(args)...); } | ||
| 855 | +/// #[cpp(func = "hicc::owned_ptr<Foo> my_new<@make_proxy<Foo>>(int)")] | ||
| 841 | /// fn new_foo<I: FooTrait>(v: I, tag: i32) -> Foo; | 856 | /// fn new_foo<I: FooTrait>(v: I, tag: i32) -> Foo; |
| 842 | -/// // let owned = unsafe { new_foo(impl, 7).into_unique() }; // drop → Foo::free | 857 | +/// // let foo = new_foo(impl, 7); // 拥有所有权, drop → Foo::free → ~Foo |
| 843 | /// ``` | 858 | /// ``` |
| 844 | /// | 859 | /// |
| 845 | /// ## `#[cpp(method = ...)]` | 860 | /// ## `#[cpp(method = ...)]` |