blob: 02e92b22f0e65f3cbfeb79a6d020d81aec00a97f [file] [log] [blame]
Brian Silverman4e662aa2022-05-11 23:10:19 -07001// Copyright 2020 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9mod 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::{make_ident, 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::{original_name_map_from_apis, type_to_cpp, CppNameMap};
24
25use self::type_to_cpp::{
26 final_ident_using_original_name_map, namespaced_name_using_original_name_map,
27};
28
29use super::{
30 analysis::{
31 fun::{
32 function_wrapper::{CppFunction, CppFunctionBody},
33 FnPhase, PodAndDepAnalysis,
34 },
35 pod::PodAnalysis,
36 },
37 api::{Api, Provenance, SubclassName, TypeKind},
38 apivec::ApiVec,
39 ConvertError,
40};
41
42#[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Hash)]
43enum Header {
44 System(&'static str),
45 CxxH,
46 CxxgenH,
47 NewDeletePrelude,
48}
49
50impl Header {
51 fn include_stmt(
52 &self,
53 cpp_codegen_options: &CppCodegenOptions,
54 cxxgen_header_name: &str,
55 ) -> String {
56 let blank = "".to_string();
57 match self {
58 Self::System(name) => format!("#include <{}>", name),
59 Self::CxxH => {
60 let prefix = cpp_codegen_options.path_to_cxx_h.as_ref().unwrap_or(&blank);
61 format!("#include \"{}cxx.h\"", prefix)
62 }
63 Self::CxxgenH => {
64 let prefix = cpp_codegen_options
65 .path_to_cxxgen_h
66 .as_ref()
67 .unwrap_or(&blank);
68 format!("#include \"{}{}\"", prefix, cxxgen_header_name)
69 }
70 Header::NewDeletePrelude => new_and_delete_prelude::NEW_AND_DELETE_PRELUDE.to_string(),
71 }
72 }
73
74 fn is_system(&self) -> bool {
75 matches!(self, Header::System(_) | Header::CxxH)
76 }
77}
78
79enum ConversionDirection {
80 RustCallsCpp,
81 CppCallsCpp,
82 CppCallsRust,
83}
84
85/// Some extra snippet of C++ which we (autocxx) need to generate, beyond
86/// that which cxx itself generates.
87#[derive(Default)]
88struct ExtraCpp {
89 type_definition: Option<String>, // are output before main declarations
90 declaration: Option<String>,
91 definition: Option<String>,
92 headers: Vec<Header>,
93 cpp_headers: Vec<Header>,
94}
95
96/// Generates additional C++ glue functions needed by autocxx.
97/// In some ways it would be preferable to be able to pass snippets
98/// of C++ through to `cxx` for inclusion in the C++ file which it
99/// generates, and perhaps we'll explore that in future. But for now,
100/// autocxx generates its own _additional_ C++ files which therefore
101/// need to be built and included in linking procedures.
102pub(crate) struct CppCodeGenerator<'a> {
103 additional_functions: Vec<ExtraCpp>,
104 inclusions: String,
105 original_name_map: CppNameMap,
106 config: &'a IncludeCppConfig,
107 cpp_codegen_options: &'a CppCodegenOptions<'a>,
108 cxxgen_header_name: &'a str,
109}
110
111struct SubclassFunction<'a> {
112 fun: &'a CppFunction,
113 is_pure_virtual: bool,
114}
115
116impl<'a> CppCodeGenerator<'a> {
117 pub(crate) fn generate_cpp_code(
118 inclusions: String,
119 apis: &ApiVec<FnPhase>,
120 config: &'a IncludeCppConfig,
121 cpp_codegen_options: &CppCodegenOptions,
122 cxxgen_header_name: &str,
123 ) -> Result<Option<CppFilePair>, ConvertError> {
124 let mut gen = CppCodeGenerator {
125 additional_functions: Vec::new(),
126 inclusions,
127 original_name_map: original_name_map_from_apis(apis),
128 config,
129 cpp_codegen_options,
130 cxxgen_header_name,
131 };
132 // The 'filter' on the following line is designed to ensure we don't accidentally
133 // end up out of sync with needs_cpp_codegen
134 gen.add_needs(apis.iter().filter(|api| api.needs_cpp_codegen()))?;
135 Ok(gen.generate())
136 }
137
138 // It's important to keep this in sync with Api::needs_cpp_codegen.
139 fn add_needs<'b>(
140 &mut self,
141 apis: impl Iterator<Item = &'a Api<FnPhase>>,
142 ) -> Result<(), ConvertError> {
143 let mut constructors_by_subclass: HashMap<SubclassName, Vec<&CppFunction>> = HashMap::new();
144 let mut methods_by_subclass: HashMap<SubclassName, Vec<SubclassFunction>> = HashMap::new();
145 let mut deferred_apis = Vec::new();
146 for api in apis {
147 match &api {
148 Api::StringConstructor { .. } => self.generate_string_constructor(),
149 Api::Function {
150 analysis:
151 FnAnalysis {
152 cpp_wrapper: Some(cpp_wrapper),
153 ignore_reason: Ok(_),
154 externally_callable: true,
155 ..
156 },
157 fun,
158 ..
159 } => {
160 if let Provenance::SynthesizedSubclassConstructor(details) = &fun.provenance {
161 constructors_by_subclass
162 .entry(details.subclass.clone())
163 .or_default()
164 .push(&details.cpp_impl);
165 }
166 self.generate_cpp_function(cpp_wrapper)?
167 }
168 Api::ConcreteType {
169 rs_definition,
170 cpp_definition,
171 ..
172 } => {
173 let effective_cpp_definition = match rs_definition {
174 Some(rs_definition) => {
175 Cow::Owned(type_to_cpp(rs_definition, &self.original_name_map)?)
176 }
177 None => Cow::Borrowed(cpp_definition),
178 };
179
180 self.generate_typedef(api.name(), &effective_cpp_definition)
181 }
182 Api::CType { typename, .. } => self.generate_ctype_typedef(typename),
183 Api::Subclass { .. } => deferred_apis.push(api),
184 Api::RustSubclassFn {
185 subclass, details, ..
186 } => {
187 methods_by_subclass
188 .entry(subclass.clone())
189 .or_default()
190 .push(SubclassFunction {
191 fun: &details.cpp_impl,
192 is_pure_virtual: details.is_pure_virtual,
193 });
194 }
195 Api::Struct {
196 name,
197 analysis:
198 PodAndDepAnalysis {
199 pod:
200 PodAnalysis {
201 kind: TypeKind::Pod,
202 ..
203 },
204 ..
205 },
206 ..
207 } => {
208 self.generate_pod_assertion(name.qualified_cpp_name());
209 }
210 _ => panic!("Should have filtered on needs_cpp_codegen"),
211 }
212 }
213
214 for api in deferred_apis.into_iter() {
215 match api {
216 Api::Subclass { name, superclass } => self.generate_subclass(
217 superclass,
218 name,
219 constructors_by_subclass.remove(name).unwrap_or_default(),
220 methods_by_subclass.remove(name).unwrap_or_default(),
221 )?,
222 _ => panic!("Unexpected deferred API"),
223 }
224 }
225 Ok(())
226 }
227
228 fn generate(&self) -> Option<CppFilePair> {
229 if self.additional_functions.is_empty() {
230 None
231 } else {
232 let headers = self.collect_headers(|additional_need| &additional_need.headers);
233 let cpp_headers = self.collect_headers(|additional_need| &additional_need.cpp_headers);
234 let type_definitions = self.concat_additional_items(|x| x.type_definition.as_ref());
235 let declarations = self.concat_additional_items(|x| x.declaration.as_ref());
236 let declarations = format!(
237 "#ifndef __AUTOCXXGEN_H__\n#define __AUTOCXXGEN_H__\n\n{}\n{}\n{}\n{}#endif // __AUTOCXXGEN_H__\n",
238 headers, self.inclusions, type_definitions, declarations
239 );
240 log::info!("Additional C++ decls:\n{}", declarations);
241 let header_name = self
242 .cpp_codegen_options
243 .autocxxgen_header_namer
244 .name_header(self.config.get_mod_name().to_string());
245 let implementation = if self
246 .additional_functions
247 .iter()
248 .any(|x| x.definition.is_some())
249 {
250 let definitions = self.concat_additional_items(|x| x.definition.as_ref());
251 let definitions = format!(
252 "#include \"{}\"\n{}\n{}",
253 header_name, cpp_headers, definitions
254 );
255 log::info!("Additional C++ defs:\n{}", definitions);
256 Some(definitions.into_bytes())
257 } else {
258 None
259 };
260 Some(CppFilePair {
261 header: declarations.into_bytes(),
262 implementation,
263 header_name,
264 })
265 }
266 }
267
268 fn collect_headers<F>(&self, filter: F) -> String
269 where
270 F: Fn(&ExtraCpp) -> &[Header],
271 {
272 let cpp_headers: HashSet<_> = self
273 .additional_functions
274 .iter()
275 .flat_map(|x| filter(x).iter())
276 .filter(|x| !self.cpp_codegen_options.suppress_system_headers || !x.is_system())
277 .collect(); // uniqify
278 cpp_headers
279 .iter()
280 .map(|x| x.include_stmt(self.cpp_codegen_options, self.cxxgen_header_name))
281 .join("\n")
282 }
283
284 fn concat_additional_items<F>(&self, field_access: F) -> String
285 where
286 F: FnMut(&ExtraCpp) -> Option<&String>,
287 {
288 let mut s = self
289 .additional_functions
290 .iter()
291 .flat_map(field_access)
292 .join("\n");
293 s.push('\n');
294 s
295 }
296
297 fn generate_pod_assertion(&mut self, name: String) {
298 // These assertions are generated by cxx for trivial ExternTypes but
299 // *only if* such types are used as trivial types in the cxx::bridge.
300 // It's possible for types which we generate to be used even without
301 // passing through the cxx::bridge, and as we generate Drop impls, that
302 // can result in destructors for nested types being called multiple times
303 // if we represent them as trivial types. So generate an extra
304 // assertion to make sure.
305 let declaration = Some(format!("static_assert(::rust::IsRelocatable<{}>::value, \"type {} should be trivially move constructible and trivially destructible to be used with generate_pod! in autocxx\");", name, name));
306 self.additional_functions.push(ExtraCpp {
307 declaration,
308 headers: vec![Header::CxxH],
309 ..Default::default()
310 })
311 }
312
313 fn generate_string_constructor(&mut self) {
314 let makestring_name = self.config.get_makestring_name();
315 let declaration = Some(format!("inline std::unique_ptr<std::string> {}(::rust::Str str) {{ return std::make_unique<std::string>(std::string(str)); }}", makestring_name));
316 self.additional_functions.push(ExtraCpp {
317 declaration,
318 headers: vec![
319 Header::System("memory"),
320 Header::System("string"),
321 Header::CxxH,
322 ],
323 ..Default::default()
324 })
325 }
326
327 fn generate_cpp_function(&mut self, details: &CppFunction) -> Result<(), ConvertError> {
328 self.additional_functions
329 .push(self.generate_cpp_function_inner(
330 details,
331 false,
332 ConversionDirection::RustCallsCpp,
333 false,
334 None,
335 )?);
336 Ok(())
337 }
338
339 fn generate_cpp_function_inner(
340 &self,
341 details: &CppFunction,
342 avoid_this: bool,
343 conversion_direction: ConversionDirection,
344 requires_rust_declarations: bool,
345 force_name: Option<&str>,
346 ) -> Result<ExtraCpp, ConvertError> {
347 // Even if the original function call is in a namespace,
348 // we generate this wrapper in the global namespace.
349 // We could easily do this the other way round, and when
350 // cxx::bridge comes to support nested namespace mods then
351 // we wil wish to do that to avoid name conflicts. However,
352 // at the moment this is simpler because it avoids us having
353 // to generate namespace blocks in the generated C++.
354 let is_a_method = !avoid_this
355 && matches!(
356 details.kind,
357 CppFunctionKind::Method
358 | CppFunctionKind::ConstMethod
359 | CppFunctionKind::Constructor
360 );
361 let name = match force_name {
362 Some(n) => n.to_string(),
363 None => details.wrapper_function_name.to_string(),
364 };
365 let get_arg_name = |counter: usize| -> String {
366 if is_a_method && counter == 0 {
367 // For method calls that we generate, the first
368 // argument name needs to be such that we recognize
369 // it as a method in the second invocation of
370 // bridge_converter after it's flowed again through
371 // bindgen.
372 // TODO this may not be the case any longer. We
373 // may be able to remove this.
374 "autocxx_gen_this".to_string()
375 } else {
376 format!("arg{}", counter)
377 }
378 };
379 // If this returns a non-POD value, we may instead wish to emplace
380 // it into a parameter, let's see.
381 let args: Result<Vec<_>, _> = details
382 .argument_conversion
383 .iter()
384 .enumerate()
385 .map(|(counter, ty)| {
386 Ok(format!(
387 "{} {}",
388 match conversion_direction {
389 ConversionDirection::RustCallsCpp =>
390 ty.unconverted_type(&self.original_name_map)?,
391 ConversionDirection::CppCallsCpp =>
392 ty.converted_type(&self.original_name_map)?,
393 ConversionDirection::CppCallsRust =>
394 ty.inverse().unconverted_type(&self.original_name_map)?,
395 },
396 get_arg_name(counter)
397 ))
398 })
399 .collect();
400 let args = args?.join(", ");
401 let default_return = match details.kind {
402 CppFunctionKind::SynthesizedConstructor => "",
403 _ => "void",
404 };
405 let ret_type = details
406 .return_conversion
407 .as_ref()
408 .and_then(|x| match conversion_direction {
409 ConversionDirection::RustCallsCpp => {
410 if x.populate_return_value() {
411 Some(x.converted_type(&self.original_name_map))
412 } else {
413 None
414 }
415 }
416 ConversionDirection::CppCallsCpp => {
417 Some(x.unconverted_type(&self.original_name_map))
418 }
419 ConversionDirection::CppCallsRust => {
420 Some(x.inverse().converted_type(&self.original_name_map))
421 }
422 })
423 .unwrap_or_else(|| Ok(default_return.to_string()))?;
424 let constness = match details.kind {
425 CppFunctionKind::ConstMethod => " const",
426 _ => "",
427 };
428 let declaration = format!("{} {}({}){}", ret_type, name, args, constness);
429 let qualification = if let Some(qualification) = &details.qualification {
430 format!("{}::", qualification.to_cpp_name())
431 } else {
432 "".to_string()
433 };
434 let qualified_declaration = format!(
435 "{} {}{}({}){}",
436 ret_type, qualification, name, args, constness
437 );
438 // Whether there's a placement param in which to put the return value
439 let placement_param = details
440 .argument_conversion
441 .iter()
442 .enumerate()
443 .filter_map(|(counter, conv)| {
444 if conv.is_placement_parameter() {
445 Some(get_arg_name(counter))
446 } else {
447 None
448 }
449 })
450 .next();
451 // Arguments to underlying function call
452 let arg_list: Result<Vec<_>, _> = details
453 .argument_conversion
454 .iter()
455 .enumerate()
456 .map(|(counter, conv)| match conversion_direction {
457 ConversionDirection::RustCallsCpp => {
458 conv.cpp_conversion(&get_arg_name(counter), &self.original_name_map, false)
459 }
460 ConversionDirection::CppCallsCpp => Ok(Some(get_arg_name(counter))),
461 ConversionDirection::CppCallsRust => conv.inverse().cpp_conversion(
462 &get_arg_name(counter),
463 &self.original_name_map,
464 false,
465 ),
466 })
467 .collect();
468 let mut arg_list = arg_list?.into_iter().flatten();
469 let receiver = if is_a_method { arg_list.next() } else { None };
470 if matches!(&details.payload, CppFunctionBody::ConstructSuperclass(_)) {
471 arg_list.next();
472 }
473 let arg_list = if details.pass_obs_field {
474 std::iter::once("*obs".to_string())
475 .chain(arg_list)
476 .join(",")
477 } else {
478 arg_list.join(", ")
479 };
480 let (mut underlying_function_call, field_assignments, need_allocators) = match &details
481 .payload
482 {
483 CppFunctionBody::Cast => (arg_list, "".to_string(), false),
484 CppFunctionBody::PlacementNew(ns, id) => {
485 let ty_id = QualifiedName::new(ns, id.clone());
486 let ty_id = self.namespaced_name(&ty_id);
487 (
488 format!("new ({}) {}({})", receiver.unwrap(), ty_id, arg_list),
489 "".to_string(),
490 false,
491 )
492 }
493 CppFunctionBody::Destructor(ns, id) => {
494 let ty_id = QualifiedName::new(ns, id.clone());
495 let ty_id = final_ident_using_original_name_map(&ty_id, &self.original_name_map);
496 (format!("{}->~{}()", arg_list, ty_id), "".to_string(), false)
497 }
498 CppFunctionBody::FunctionCall(ns, id) => match receiver {
499 Some(receiver) => (
500 format!("{}.{}({})", receiver, id, arg_list),
501 "".to_string(),
502 false,
503 ),
504 None => {
505 let underlying_function_call = ns
506 .into_iter()
507 .cloned()
508 .chain(std::iter::once(id.to_string()))
509 .join("::");
510 (
511 format!("{}({})", underlying_function_call, arg_list),
512 "".to_string(),
513 false,
514 )
515 }
516 },
517 CppFunctionBody::StaticMethodCall(ns, ty_id, fn_id) => {
518 let underlying_function_call = ns
519 .into_iter()
520 .cloned()
521 .chain([ty_id.to_string(), fn_id.to_string()].iter().cloned())
522 .join("::");
523 (
524 format!("{}({})", underlying_function_call, arg_list),
525 "".to_string(),
526 false,
527 )
528 }
529 CppFunctionBody::ConstructSuperclass(_) => ("".to_string(), arg_list, false),
530 CppFunctionBody::AllocUninitialized(ty) => {
531 let namespaced_ty = self.namespaced_name(ty);
532 (
533 format!("new_appropriately<{}>();", namespaced_ty,),
534 "".to_string(),
535 true,
536 )
537 }
538 CppFunctionBody::FreeUninitialized(ty) => (
539 format!("delete_appropriately<{}>(arg0);", self.namespaced_name(ty)),
540 "".to_string(),
541 true,
542 ),
543 };
544 if let Some(ret) = &details.return_conversion {
545 let call_itself = match conversion_direction {
546 ConversionDirection::RustCallsCpp => {
547 ret.cpp_conversion(&underlying_function_call, &self.original_name_map, true)?
548 }
549 ConversionDirection::CppCallsCpp => Some(underlying_function_call),
550 ConversionDirection::CppCallsRust => ret.inverse().cpp_conversion(
551 &underlying_function_call,
552 &self.original_name_map,
553 true,
554 )?,
555 }
556 .expect(
557 "Expected some conversion type for return value which resulted in a parameter name",
558 );
559
560 underlying_function_call = match placement_param {
561 Some(placement_param) => {
562 let tyname = type_to_cpp(&ret.unwrapped_type, &self.original_name_map)?;
563 format!("new({}) {}({})", placement_param, tyname, call_itself)
564 }
565 None => format!("return {}", call_itself),
566 };
567 };
568 if !underlying_function_call.is_empty() {
569 underlying_function_call = format!("{};", underlying_function_call);
570 }
571 let field_assignments =
572 if let CppFunctionBody::ConstructSuperclass(superclass_name) = &details.payload {
573 let superclass_assignments = if field_assignments.is_empty() {
574 "".to_string()
575 } else {
576 format!("{}({}), ", superclass_name, field_assignments)
577 };
578 format!(": {}obs(std::move(arg0))", superclass_assignments)
579 } else {
580 "".into()
581 };
582 let definition_after_sig =
583 format!("{} {{ {} }}", field_assignments, underlying_function_call,);
584 let (declaration, definition) = if requires_rust_declarations {
585 (
586 Some(format!("{};", declaration)),
587 Some(format!(
588 "{} {}",
589 qualified_declaration, definition_after_sig
590 )),
591 )
592 } else {
593 (
594 Some(format!("inline {} {}", declaration, definition_after_sig)),
595 None,
596 )
597 };
598 let mut headers = vec![Header::System("memory")];
599 if need_allocators {
600 headers.push(Header::System("stddef.h"));
601 headers.push(Header::NewDeletePrelude);
602 }
603 Ok(ExtraCpp {
604 declaration,
605 definition,
606 headers,
607 ..Default::default()
608 })
609 }
610
611 fn namespaced_name(&self, name: &QualifiedName) -> String {
612 namespaced_name_using_original_name_map(name, &self.original_name_map)
613 }
614
615 fn generate_ctype_typedef(&mut self, tn: &QualifiedName) {
616 let cpp_name = tn.to_cpp_name();
617 self.generate_typedef(tn, &cpp_name)
618 }
619
620 fn generate_typedef(&mut self, tn: &QualifiedName, definition: &str) {
621 let our_name = tn.get_final_item();
622 self.additional_functions.push(ExtraCpp {
623 type_definition: Some(format!("typedef {} {};", definition, our_name)),
624 ..Default::default()
625 })
626 }
627
628 fn generate_subclass(
629 &mut self,
630 superclass: &QualifiedName,
631 subclass: &SubclassName,
632 constructors: Vec<&CppFunction>,
633 methods: Vec<SubclassFunction>,
634 ) -> Result<(), ConvertError> {
635 let holder = subclass.holder();
636 self.additional_functions.push(ExtraCpp {
637 type_definition: Some(format!("struct {};", holder)),
638 ..Default::default()
639 });
640 let mut method_decls = Vec::new();
641 for method in methods {
642 // First the method which calls from C++ to Rust
643 let mut fn_impl = self.generate_cpp_function_inner(
644 method.fun,
645 true,
646 ConversionDirection::CppCallsRust,
647 true,
648 Some(&method.fun.original_cpp_name),
649 )?;
650 method_decls.push(fn_impl.declaration.take().unwrap());
651 self.additional_functions.push(fn_impl);
652 // And now the function to be called from Rust for default implementation (calls superclass in C++)
653 if !method.is_pure_virtual {
654 let mut super_method = method.fun.clone();
655 super_method.pass_obs_field = false;
656 super_method.wrapper_function_name = SubclassName::get_super_fn_name(
657 superclass.get_namespace(),
658 &method.fun.wrapper_function_name.to_string(),
659 )
660 .get_final_ident();
661 super_method.payload = CppFunctionBody::StaticMethodCall(
662 superclass.get_namespace().clone(),
663 superclass.get_final_ident(),
664 make_ident(&method.fun.original_cpp_name),
665 );
666 let mut super_fn_impl = self.generate_cpp_function_inner(
667 &super_method,
668 true,
669 ConversionDirection::CppCallsCpp,
670 false,
671 None,
672 )?;
673 method_decls.push(super_fn_impl.declaration.take().unwrap());
674 self.additional_functions.push(super_fn_impl);
675 }
676 }
677 // In future, for each superclass..
678 let super_name = superclass.get_final_item();
679 method_decls.push(format!(
680 "const {}& As_{}() const {{ return *this; }}",
681 super_name, super_name,
682 ));
683 method_decls.push(format!(
684 "{}& As_{}_mut() {{ return *this; }}",
685 super_name, super_name
686 ));
687 // And now constructors
688 let mut constructor_decls: Vec<String> = Vec::new();
689 for constructor in constructors {
690 let mut fn_impl = self.generate_cpp_function_inner(
691 constructor,
692 false,
693 ConversionDirection::CppCallsCpp,
694 false,
695 None,
696 )?;
697 let decl = fn_impl.declaration.take().unwrap();
698 constructor_decls.push(decl);
699 self.additional_functions.push(fn_impl);
700 }
701 self.additional_functions.push(ExtraCpp {
702 type_definition: Some(format!(
703 "class {} : {}\n{{\npublic:\n{}\n{}\nvoid {}() const;\nprivate:rust::Box<{}> obs;\nvoid really_remove_ownership();\n\n}};",
704 subclass.cpp(),
705 superclass.to_cpp_name(),
706 constructor_decls.join("\n"),
707 method_decls.join("\n"),
708 subclass.cpp_remove_ownership(),
709 holder
710 )),
711 definition: Some(format!(
712 "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",
713 subclass.cpp(),
714 subclass.cpp_remove_ownership(),
715 subclass.cpp(),
716 subclass.cpp(),
717 subclass.remove_ownership()
718 )),
719 cpp_headers: vec![Header::CxxgenH],
720 ..Default::default()
721 });
722 Ok(())
723 }
724}