1mod function_wrapper_cpp;
10mod new_and_delete_prelude;
11pub(crate) mod type_to_cpp;
12
13use crate::{
14 conversion::analysis::fun::{function_wrapper::CppFunctionKind, FnAnalysis},
15 types::QualifiedName,
16 CppCodegenOptions, CppFilePair,
17};
18use autocxx_parser::IncludeCppConfig;
19use indexmap::map::IndexMap as HashMap;
20use indexmap::set::IndexSet as HashSet;
21use itertools::Itertools;
22use std::borrow::Cow;
23use type_to_cpp::CppNameMap;
24
25use super::{
26 analysis::{
27 fun::{
28 function_wrapper::{CppFunction, CppFunctionBody},
29 FnPhase, PodAndDepAnalysis,
30 },
31 pod::PodAnalysis,
32 },
33 api::{Api, Provenance, SubclassName, TypeKind},
34 apivec::ApiVec,
35 ConvertErrorFromCpp, CppEffectiveName,
36};
37
38static GENERATED_FILE_HEADER: &str =
39 "// Generated using autocxx - do not edit directly.\n// @generated.\n\n";
40
41#[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Hash)]
42enum Header {
43 System(&'static str),
44 CxxH,
45 CxxgenH,
46 NewDeletePrelude,
47}
48
49impl Header {
50 fn include_stmt(
51 &self,
52 cpp_codegen_options: &CppCodegenOptions,
53 cxxgen_header_name: &str,
54 ) -> String {
55 let blank = "".to_string();
56 match self {
57 Self::System(name) => format!("#include <{name}>"),
58 Self::CxxH => {
59 let prefix = cpp_codegen_options.path_to_cxx_h.as_ref().unwrap_or(&blank);
60 format!("#include \"{prefix}cxx.h\"")
61 }
62 Self::CxxgenH => {
63 let prefix = cpp_codegen_options
64 .path_to_cxxgen_h
65 .as_ref()
66 .unwrap_or(&blank);
67 format!("#include \"{prefix}{cxxgen_header_name}\"")
68 }
69 Header::NewDeletePrelude => new_and_delete_prelude::NEW_AND_DELETE_PRELUDE.to_string(),
70 }
71 }
72
73 fn is_system(&self) -> bool {
74 matches!(self, Header::System(_) | Header::CxxH)
75 }
76}
77
78enum ConversionDirection {
79 RustCallsCpp,
80 CppCallsCpp,
81 CppCallsRust,
82}
83
84#[derive(Default)]
87struct ExtraCpp {
88 type_definition: Option<String>, declaration: Option<String>,
90 definition: Option<String>,
91 headers: Vec<Header>,
92 cpp_headers: Vec<Header>,
93}
94
95pub(crate) struct CppCodeGenerator<'a> {
102 additional_functions: Vec<ExtraCpp>,
103 inclusions: String,
104 original_name_map: CppNameMap,
105 config: &'a IncludeCppConfig,
106 cpp_codegen_options: &'a CppCodegenOptions<'a>,
107 cxxgen_header_name: &'a str,
108}
109
110struct SubclassFunction<'a> {
111 fun: &'a CppFunction,
112 is_pure_virtual: bool,
113}
114
115impl<'a> CppCodeGenerator<'a> {
116 pub(crate) fn generate_cpp_code(
117 inclusions: String,
118 apis: &ApiVec<FnPhase>,
119 config: &'a IncludeCppConfig,
120 cpp_codegen_options: &CppCodegenOptions,
121 cxxgen_header_name: &str,
122 ) -> Result<Option<CppFilePair>, ConvertErrorFromCpp> {
123 let mut gen = CppCodeGenerator {
124 additional_functions: Vec::new(),
125 inclusions,
126 original_name_map: CppNameMap::new_from_apis(apis),
127 config,
128 cpp_codegen_options,
129 cxxgen_header_name,
130 };
131 gen.add_needs(apis.iter().filter(|api| api.needs_cpp_codegen()))?;
134 Ok(gen.generate())
135 }
136
137 fn add_needs<'b>(
139 &mut self,
140 apis: impl Iterator<Item = &'a Api<FnPhase>>,
141 ) -> Result<(), ConvertErrorFromCpp> {
142 let mut constructors_by_subclass: HashMap<SubclassName, Vec<&CppFunction>> = HashMap::new();
143 let mut methods_by_subclass: HashMap<SubclassName, Vec<SubclassFunction>> = HashMap::new();
144 let mut deferred_apis = Vec::new();
145 for api in apis {
146 match &api {
147 Api::StringConstructor { .. } => self.generate_string_constructor(),
148 Api::Function {
149 analysis:
150 FnAnalysis {
151 cpp_wrapper: Some(cpp_wrapper),
152 ignore_reason: Ok(_),
153 externally_callable: true,
154 ..
155 },
156 fun,
157 ..
158 } => {
159 if let Provenance::SynthesizedSubclassConstructor(details) = &fun.provenance {
160 constructors_by_subclass
161 .entry(details.subclass.clone())
162 .or_default()
163 .push(&details.cpp_impl);
164 }
165 self.generate_cpp_function(cpp_wrapper)?
166 }
167 Api::ConcreteType {
168 rs_definition,
169 cpp_definition,
170 ..
171 } => {
172 let effective_cpp_definition = match rs_definition {
173 Some(rs_definition) => {
174 Cow::Owned(self.original_name_map.type_to_cpp(rs_definition)?)
175 }
176 None => Cow::Borrowed(cpp_definition),
177 };
178
179 self.generate_typedef(api.name(), &effective_cpp_definition)
180 }
181 Api::CType { typename, .. } => self.generate_ctype_typedef(typename),
182 Api::Subclass { .. } => deferred_apis.push(api),
183 Api::RustSubclassFn {
184 subclass, details, ..
185 } => {
186 methods_by_subclass
187 .entry(subclass.clone())
188 .or_default()
189 .push(SubclassFunction {
190 fun: &details.cpp_impl,
191 is_pure_virtual: details.is_pure_virtual,
192 });
193 }
194 Api::Struct {
195 name,
196 analysis:
197 PodAndDepAnalysis {
198 pod:
199 PodAnalysis {
200 kind: TypeKind::Pod,
201 ..
202 },
203 ..
204 },
205 ..
206 } => {
207 self.generate_pod_assertion(name.qualified_cpp_name());
208 }
209 _ => panic!("Should have filtered on needs_cpp_codegen"),
210 }
211 }
212
213 for api in deferred_apis.into_iter() {
214 match api {
215 Api::Subclass { name, superclass } => self.generate_subclass(
216 superclass,
217 name,
218 constructors_by_subclass.remove(name).unwrap_or_default(),
219 methods_by_subclass.remove(name).unwrap_or_default(),
220 )?,
221 _ => panic!("Unexpected deferred API"),
222 }
223 }
224 Ok(())
225 }
226
227 fn generate(&self) -> Option<CppFilePair> {
228 if self.additional_functions.is_empty() {
229 None
230 } else {
231 let headers = self.collect_headers(|additional_need| &additional_need.headers);
232 let cpp_headers = self.collect_headers(|additional_need| &additional_need.cpp_headers);
233 let type_definitions = self.concat_additional_items(|x| x.type_definition.as_ref());
234 let declarations = self.concat_additional_items(|x| x.declaration.as_ref());
235 let declarations = format!(
236 "{}\n#ifndef __AUTOCXXGEN_H__\n#define __AUTOCXXGEN_H__\n\n{}\n{}\n{}\n{}#endif // __AUTOCXXGEN_H__\n",
237 GENERATED_FILE_HEADER, headers, self.inclusions, type_definitions, declarations
238 );
239 log::info!("Additional C++ decls:\n{}", declarations);
240 let header_name = self
241 .cpp_codegen_options
242 .autocxxgen_header_namer
243 .name_header(self.config.get_mod_name().to_string());
244 let implementation = if self
245 .additional_functions
246 .iter()
247 .any(|x| x.definition.is_some())
248 {
249 let definitions = self.concat_additional_items(|x| x.definition.as_ref());
250 let definitions =
251 format!("{GENERATED_FILE_HEADER}\n#include \"{header_name}\"\n{cpp_headers}\n{definitions}");
252 log::info!("Additional C++ defs:\n{}", definitions);
253 Some(definitions.into_bytes())
254 } else {
255 None
256 };
257 Some(CppFilePair {
258 header: declarations.into_bytes(),
259 implementation,
260 header_name,
261 })
262 }
263 }
264
265 fn collect_headers<F>(&self, filter: F) -> String
266 where
267 F: Fn(&ExtraCpp) -> &[Header],
268 {
269 let cpp_headers: HashSet<_> = self
270 .additional_functions
271 .iter()
272 .flat_map(|x| filter(x).iter())
273 .filter(|x| !self.cpp_codegen_options.suppress_system_headers || !x.is_system())
274 .collect(); cpp_headers
276 .iter()
277 .map(|x| x.include_stmt(self.cpp_codegen_options, self.cxxgen_header_name))
278 .join("\n")
279 }
280
281 fn concat_additional_items<F>(&self, field_access: F) -> String
282 where
283 F: FnMut(&ExtraCpp) -> Option<&String>,
284 {
285 let mut s = self
286 .additional_functions
287 .iter()
288 .flat_map(field_access)
289 .join("\n");
290 s.push('\n');
291 s
292 }
293
294 fn generate_pod_assertion(&mut self, name: String) {
295 let declaration = Some(format!("static_assert(::rust::IsRelocatable<{name}>::value, \"type {name} should be trivially move constructible and trivially destructible to be used with generate_pod! in autocxx\");"));
303 self.additional_functions.push(ExtraCpp {
304 declaration,
305 headers: vec![Header::CxxH],
306 ..Default::default()
307 })
308 }
309
310 fn generate_string_constructor(&mut self) {
311 let makestring_name = self.config.get_makestring_name();
312 let declaration = Some(format!("inline std::unique_ptr<std::string> {makestring_name}(::rust::Str str) {{ return std::make_unique<std::string>(std::string(str)); }}"));
313 self.additional_functions.push(ExtraCpp {
314 declaration,
315 headers: vec![
316 Header::System("memory"),
317 Header::System("string"),
318 Header::CxxH,
319 ],
320 ..Default::default()
321 })
322 }
323
324 fn generate_cpp_function(&mut self, details: &CppFunction) -> Result<(), ConvertErrorFromCpp> {
325 self.additional_functions
326 .push(self.generate_cpp_function_inner(
327 details,
328 false,
329 ConversionDirection::RustCallsCpp,
330 false,
331 None,
332 )?);
333 Ok(())
334 }
335
336 fn generate_cpp_function_inner(
337 &self,
338 details: &CppFunction,
339 avoid_this: bool,
340 conversion_direction: ConversionDirection,
341 requires_rust_declarations: bool,
342 force_name: Option<&CppEffectiveName>,
343 ) -> Result<ExtraCpp, ConvertErrorFromCpp> {
344 let is_a_method = !avoid_this
352 && matches!(
353 details.kind,
354 CppFunctionKind::Method
355 | CppFunctionKind::ConstMethod
356 | CppFunctionKind::Constructor
357 );
358 let name = match force_name {
359 Some(n) => n.to_string_for_cpp_generation().to_string(),
360 None => details.wrapper_function_name.to_string(),
361 };
362 let get_arg_name = |counter: usize| -> String {
363 if is_a_method && counter == 0 {
364 "autocxx_gen_this".to_string()
372 } else {
373 format!("arg{counter}")
374 }
375 };
376 let args: Result<Vec<_>, _> = details
379 .argument_conversion
380 .iter()
381 .enumerate()
382 .map(|(counter, ty)| {
383 Ok(format!(
384 "{} {}",
385 match conversion_direction {
386 ConversionDirection::RustCallsCpp =>
387 ty.unconverted_type(&self.original_name_map)?,
388 ConversionDirection::CppCallsCpp =>
389 ty.converted_type(&self.original_name_map)?,
390 ConversionDirection::CppCallsRust =>
391 ty.inverse().unconverted_type(&self.original_name_map)?,
392 },
393 get_arg_name(counter)
394 ))
395 })
396 .collect();
397 let args = args?.join(", ");
398 let default_return = match details.kind {
399 CppFunctionKind::SynthesizedConstructor => "",
400 _ => "void",
401 };
402 let ret_type = details
403 .return_conversion
404 .as_ref()
405 .and_then(|x| match conversion_direction {
406 ConversionDirection::RustCallsCpp => {
407 if x.populate_return_value() {
408 Some(x.converted_type(&self.original_name_map))
409 } else {
410 None
411 }
412 }
413 ConversionDirection::CppCallsCpp => {
414 Some(x.unconverted_type(&self.original_name_map))
415 }
416 ConversionDirection::CppCallsRust => {
417 Some(x.inverse().converted_type(&self.original_name_map))
418 }
419 })
420 .unwrap_or_else(|| Ok(default_return.to_string()))?;
421 let constness = match details.kind {
422 CppFunctionKind::ConstMethod => " const",
423 _ => "",
424 };
425 let declaration = format!("{ret_type} {name}({args}){constness}");
426 let qualification = if let Some(qualification) = &details.qualification {
427 format!("{}::", qualification.to_cpp_name())
428 } else {
429 "".to_string()
430 };
431 let qualified_declaration = format!("{ret_type} {qualification}{name}({args}){constness}");
432 let placement_param = details
434 .argument_conversion
435 .iter()
436 .enumerate()
437 .filter_map(|(counter, conv)| {
438 if conv.is_placement_parameter() {
439 Some(get_arg_name(counter))
440 } else {
441 None
442 }
443 })
444 .next();
445 let arg_list: Result<Vec<_>, _> = details
447 .argument_conversion
448 .iter()
449 .enumerate()
450 .map(|(counter, conv)| match conversion_direction {
451 ConversionDirection::RustCallsCpp => {
452 conv.cpp_conversion(&get_arg_name(counter), &self.original_name_map, false)
453 }
454 ConversionDirection::CppCallsCpp => Ok(Some(get_arg_name(counter))),
455 ConversionDirection::CppCallsRust => conv.inverse().cpp_conversion(
456 &get_arg_name(counter),
457 &self.original_name_map,
458 false,
459 ),
460 })
461 .collect();
462 let mut arg_list = arg_list?.into_iter().flatten();
463 let receiver = if is_a_method { arg_list.next() } else { None };
464 if matches!(&details.payload, CppFunctionBody::ConstructSuperclass(_)) {
465 arg_list.next();
466 }
467 let arg_list = if details.pass_obs_field {
468 std::iter::once("*obs".to_string())
469 .chain(arg_list)
470 .join(",")
471 } else {
472 arg_list.join(", ")
473 };
474 let (mut underlying_function_call, field_assignments, need_allocators) = match &details
475 .payload
476 {
477 CppFunctionBody::Cast => (arg_list, "".to_string(), false),
478 CppFunctionBody::PlacementNew(ns, id) => {
479 let ty_id = QualifiedName::new(ns, id.clone());
480 let ty_id = self.namespaced_name(&ty_id);
481 (
482 format!("new ({}) {}({})", receiver.unwrap(), ty_id, arg_list),
483 "".to_string(),
484 false,
485 )
486 }
487 CppFunctionBody::Destructor(ns, id) => {
488 let full_name = QualifiedName::new(ns, id.clone());
489 let ty_id = self.original_name_map.get_final_item(&full_name);
490 let is_a_nested_struct = self.original_name_map.get(&full_name).is_some();
491 let destructor_call = format!("{arg_list}->{ty_id}::~{ty_id}()");
505 let destructor_call = if ns.is_empty() {
506 destructor_call
507 } else {
508 let path = self.original_name_map.map(&full_name);
509 if is_a_nested_struct {
510 format!("{{ using {ty_id} = {path}; {destructor_call}; {ty_id}* pointless; (void)pointless; }}")
511 } else {
512 format!("{{ using {path}; {destructor_call}; }}")
513 }
514 };
515 (destructor_call, "".to_string(), false)
516 }
517 CppFunctionBody::FunctionCall(ns, id) => match receiver {
518 Some(receiver) => (
519 format!(
520 "{receiver}.{}({arg_list})",
521 id.to_string_for_cpp_generation()
522 ),
523 "".to_string(),
524 false,
525 ),
526 None => {
527 let underlying_function_call = ns
528 .into_iter()
529 .cloned()
530 .chain(std::iter::once(
531 id.to_string_for_cpp_generation().to_string(),
532 ))
533 .join("::");
534 (
535 format!("{underlying_function_call}({arg_list})"),
536 "".to_string(),
537 false,
538 )
539 }
540 },
541 CppFunctionBody::StaticMethodCall(ns, ty_id, fn_id) => {
542 let underlying_function_call = ns
543 .into_iter()
544 .cloned()
545 .chain(
546 [
547 ty_id.to_string(),
548 fn_id.to_string_for_cpp_generation().to_string(),
549 ]
550 .iter()
551 .cloned(),
552 )
553 .join("::");
554 (
555 format!("{underlying_function_call}({arg_list})"),
556 "".to_string(),
557 false,
558 )
559 }
560 CppFunctionBody::ConstructSuperclass(_) => ("".to_string(), arg_list, false),
561 CppFunctionBody::AllocUninitialized(ty) => {
562 let namespaced_ty = self.namespaced_name(ty);
563 (
564 format!("new_appropriately<{namespaced_ty}>();",),
565 "".to_string(),
566 true,
567 )
568 }
569 CppFunctionBody::FreeUninitialized(ty) => (
570 format!("delete_appropriately<{}>(arg0);", self.namespaced_name(ty)),
571 "".to_string(),
572 true,
573 ),
574 };
575 if let Some(ret) = &details.return_conversion {
576 let call_itself = match conversion_direction {
577 ConversionDirection::RustCallsCpp => {
578 ret.cpp_conversion(&underlying_function_call, &self.original_name_map, true)?
579 }
580 ConversionDirection::CppCallsCpp => Some(underlying_function_call),
581 ConversionDirection::CppCallsRust => ret.inverse().cpp_conversion(
582 &underlying_function_call,
583 &self.original_name_map,
584 true,
585 )?,
586 }
587 .expect(
588 "Expected some conversion type for return value which resulted in a parameter name",
589 );
590
591 underlying_function_call = match placement_param {
592 Some(placement_param) => {
593 let tyname = self.original_name_map.type_to_cpp(ret.cxxbridge_type())?;
594 format!("new({placement_param}) {tyname}({call_itself})")
595 }
596 None => format!("return {call_itself}"),
597 };
598 };
599 if !underlying_function_call.is_empty() {
600 underlying_function_call = format!("{underlying_function_call};");
601 }
602 let field_assignments =
603 if let CppFunctionBody::ConstructSuperclass(superclass_name) = &details.payload {
604 let superclass_assignments = if field_assignments.is_empty() {
605 "".to_string()
606 } else {
607 format!("{superclass_name}({field_assignments}), ")
608 };
609 format!(": {superclass_assignments}obs(std::move(arg0))")
610 } else {
611 "".into()
612 };
613 let definition_after_sig = format!("{field_assignments} {{ {underlying_function_call} }}",);
614 let (declaration, definition) = if requires_rust_declarations {
615 (
616 Some(format!("{declaration};")),
617 Some(format!("{qualified_declaration} {definition_after_sig}")),
618 )
619 } else {
620 (
621 Some(format!("inline {declaration} {definition_after_sig}")),
622 None,
623 )
624 };
625 let mut headers = vec![Header::System("memory")];
626 if need_allocators {
627 headers.push(Header::System("stddef.h"));
628 headers.push(Header::NewDeletePrelude);
629 }
630 Ok(ExtraCpp {
631 declaration,
632 definition,
633 headers,
634 ..Default::default()
635 })
636 }
637
638 fn namespaced_name(&self, name: &QualifiedName) -> String {
639 self.original_name_map.map(name)
640 }
641
642 fn generate_ctype_typedef(&mut self, tn: &QualifiedName) {
643 let cpp_name = tn.to_cpp_name();
644 self.generate_typedef(tn, &cpp_name)
645 }
646
647 fn generate_typedef(&mut self, tn: &QualifiedName, definition: &str) {
648 let our_name = tn.get_final_item();
649 self.additional_functions.push(ExtraCpp {
650 type_definition: Some(format!("typedef {definition} {our_name};")),
651 ..Default::default()
652 })
653 }
654
655 fn generate_subclass(
656 &mut self,
657 superclass: &QualifiedName,
658 subclass: &SubclassName,
659 constructors: Vec<&CppFunction>,
660 methods: Vec<SubclassFunction>,
661 ) -> Result<(), ConvertErrorFromCpp> {
662 let holder = subclass.holder();
663 self.additional_functions.push(ExtraCpp {
664 type_definition: Some(format!("struct {holder};")),
665 ..Default::default()
666 });
667 let mut method_decls = Vec::new();
668 for method in methods {
669 let mut fn_impl = self.generate_cpp_function_inner(
671 method.fun,
672 true,
673 ConversionDirection::CppCallsRust,
674 true,
675 Some(&method.fun.original_cpp_name),
676 )?;
677 method_decls.push(fn_impl.declaration.take().unwrap());
678 self.additional_functions.push(fn_impl);
679 if !method.is_pure_virtual {
681 let mut super_method = method.fun.clone();
682 super_method.pass_obs_field = false;
683 super_method.wrapper_function_name = SubclassName::get_super_fn_name(
684 superclass.get_namespace(),
685 &method.fun.wrapper_function_name.to_string(),
686 )
687 .get_final_ident();
688 super_method.payload = CppFunctionBody::StaticMethodCall(
689 superclass.get_namespace().clone(),
690 superclass.get_final_ident(),
691 method.fun.original_cpp_name.clone(),
692 );
693 let mut super_fn_impl = self.generate_cpp_function_inner(
694 &super_method,
695 true,
696 ConversionDirection::CppCallsCpp,
697 false,
698 None,
699 )?;
700 method_decls.push(super_fn_impl.declaration.take().unwrap());
701 self.additional_functions.push(super_fn_impl);
702 }
703 }
704 let super_name = superclass.get_final_item();
706 method_decls.push(format!(
707 "const {super_name}& As_{super_name}() const {{ return *this; }}",
708 ));
709 method_decls.push(format!(
710 "{super_name}& As_{super_name}_mut() {{ return *this; }}"
711 ));
712 self.additional_functions.push(ExtraCpp {
713 declaration: Some(format!(
714 "inline std::unique_ptr<{}> {}_As_{}_UniquePtr(std::unique_ptr<{}> u) {{ return std::unique_ptr<{}>(u.release()); }}",
715 superclass.to_cpp_name(), subclass.cpp(), super_name, subclass.cpp(), superclass.to_cpp_name(),
716 )),
717 ..Default::default()
718 });
719 let mut constructor_decls: Vec<String> = Vec::new();
721 for constructor in constructors {
722 let mut fn_impl = self.generate_cpp_function_inner(
723 constructor,
724 false,
725 ConversionDirection::CppCallsCpp,
726 false,
727 None,
728 )?;
729 let decl = fn_impl.declaration.take().unwrap();
730 constructor_decls.push(decl);
731 self.additional_functions.push(fn_impl);
732 }
733 self.additional_functions.push(ExtraCpp {
734 type_definition: Some(format!(
735 "class {} : public {}\n{{\npublic:\n{}\n{}\nvoid {}() const;\nprivate:rust::Box<{}> obs;\nvoid really_remove_ownership();\n\n}};",
736 subclass.cpp(),
737 superclass.to_cpp_name(),
738 constructor_decls.join("\n"),
739 method_decls.join("\n"),
740 subclass.cpp_remove_ownership(),
741 holder
742 )),
743 definition: Some(format!(
744 "void {}::{}() const {{\nconst_cast<{}*>(this)->really_remove_ownership();\n}}\nvoid {}::really_remove_ownership() {{\nauto new_obs = {}(std::move(obs));\nobs = std::move(new_obs);\n}}\n",
745 subclass.cpp(),
746 subclass.cpp_remove_ownership(),
747 subclass.cpp(),
748 subclass.cpp(),
749 subclass.remove_ownership()
750 )),
751 cpp_headers: vec![Header::CxxgenH],
752 ..Default::default()
753 });
754 Ok(())
755 }
756}