1use core::cell::RefCell;
16use std::collections::{BTreeMap, BTreeSet};
17use std::fmt::Write;
18
19use zerodds_idl::ast::{
20 Annotation, AnnotationParams, Case, CaseLabel, ConstExpr, ConstrTypeDecl, Declarator,
21 Definition, EnumDef, ExceptDecl, Export, InterfaceDcl, InterfaceDef, Literal, LiteralKind,
22 Member, ModuleDef, OpDecl, ParamAttribute, PrimitiveType, ScopedName, Specification,
23 StateVisibility, StructDcl, StructDef, SwitchTypeSpec, TypeDecl, TypeSpec, TypedefDecl,
24 UnionDcl, UnionDef, ValueDef, ValueElement,
25};
26
27use zerodds_idl::semantics::annotations::PlacementKind;
28
29use crate::bitset::{emit_bitmask, emit_bitset};
30use crate::error::CppGenError;
31use crate::type_map::{check_identifier, is_reserved, primitive_to_cpp};
32use crate::verbatim::emit_verbatim_at;
33use crate::{CppGenOptions, TIME_DURATION_TYPES};
34
35#[derive(Debug, Default, Clone)]
37struct Includes {
38 headers: BTreeSet<&'static str>,
39}
40
41impl Includes {
42 fn add(&mut self, h: &'static str) {
43 self.headers.insert(h);
44 }
45}
46
47fn type_has_bounded_collection(ts: &TypeSpec) -> bool {
54 match ts {
55 TypeSpec::Sequence(s) => s.bound.is_some() || type_has_bounded_collection(&s.elem),
56 TypeSpec::String(s) => s.bound.is_some(),
58 _ => false,
59 }
60}
61
62fn spec_has_bounded_collection(spec: &Specification) -> bool {
64 let mut structs: Vec<(String, &StructDef)> = Vec::new();
65 collect_topic_structs(&spec.definitions, "", &mut structs);
66 structs.iter().any(|(_, s)| {
67 s.members
68 .iter()
69 .any(|m| type_has_bounded_collection(&m.type_spec))
70 })
71}
72
73pub(crate) fn emit_header(
74 spec: &Specification,
75 opts: &CppGenOptions,
76) -> Result<String, CppGenError> {
77 detect_inheritance_cycles(spec)?;
79
80 set_type_registry(spec);
83
84 let mut includes = Includes::default();
86 includes.add("<cstdint>"); collect_includes(spec, &mut includes);
88 if spec_has_bounded_collection(spec) {
92 includes.add("<stdexcept>");
93 }
94
95 let mut out = String::new();
97 write_header_preamble(&mut out, opts, &includes)?;
98
99 let mut ctx = EmitCtx::new(opts);
101 let outer_prefix: Option<&str> = opts.namespace_prefix.as_deref().filter(|p| !p.is_empty());
102 if let Some(prefix) = outer_prefix {
103 ctx.open_namespace(&mut out, prefix)?;
104 }
105
106 for d in &spec.definitions {
110 if let Some(anns) = top_level_annotations(d) {
111 emit_verbatim_at(&mut out, "", anns, PlacementKind::BeginFile)?;
112 }
113 }
114
115 let mut probe_structs: Vec<(String, &StructDef)> = Vec::new();
123 collect_topic_structs(&spec.definitions, "", &mut probe_structs);
124 let mut have_unions = Vec::new();
125 collect_topic_unions(&spec.definitions, "", &mut have_unions);
126 if !probe_structs.is_empty() || !have_unions.is_empty() {
127 writeln!(&mut out, "#include \"dds/topic/TopicTraits.hpp\"").map_err(fmt_err)?;
133 writeln!(&mut out, "#include \"dds/topic/xcdr2.hpp\"").map_err(fmt_err)?;
134 writeln!(&mut out, "#include \"dds/topic/xcdr2_md5.hpp\"").map_err(fmt_err)?;
135 writeln!(&mut out, "#include \"dds/core/Fixed.hpp\"").map_err(fmt_err)?;
138 writeln!(&mut out).map_err(fmt_err)?;
139 }
140
141 for d in &spec.definitions {
143 emit_definition(&mut out, &mut ctx, d)?;
144 }
145
146 for d in &spec.definitions {
149 if let Some(anns) = top_level_annotations(d) {
150 emit_verbatim_at(&mut out, "", anns, PlacementKind::EndFile)?;
151 }
152 }
153
154 if let Some(prefix) = outer_prefix {
156 ctx.close_namespace(&mut out, prefix)?;
157 }
158
159 let mut probe_unions: Vec<(String, &UnionDef)> = Vec::new();
163 collect_topic_unions(&spec.definitions, "", &mut probe_unions);
164 if !probe_structs.is_empty() || !probe_unions.is_empty() {
165 emit_topic_type_support_specs(&mut out, opts, &probe_structs, &probe_unions)?;
166 }
167
168 Ok(out)
169}
170
171fn top_level_annotations(d: &Definition) -> Option<&[Annotation]> {
174 match d {
175 Definition::Module(m) => Some(&m.annotations),
176 Definition::Type(TypeDecl::Constr(c)) => match c {
177 ConstrTypeDecl::Struct(StructDcl::Def(s)) => Some(&s.annotations),
178 ConstrTypeDecl::Union(UnionDcl::Def(u)) => Some(&u.annotations),
179 ConstrTypeDecl::Enum(e) => Some(&e.annotations),
180 _ => None,
181 },
182 Definition::Type(TypeDecl::Typedef(t)) => Some(&t.annotations),
183 Definition::Const(c) => Some(&c.annotations),
184 Definition::Except(e) => Some(&e.annotations),
185 _ => None,
186 }
187}
188
189fn write_header_preamble(
191 out: &mut String,
192 opts: &CppGenOptions,
193 includes: &Includes,
194) -> Result<(), CppGenError> {
195 writeln!(out, "// Generated by zerodds idl-cpp. Do not edit.").map_err(fmt_err)?;
196 writeln!(out, "#pragma once").map_err(fmt_err)?;
197 if let Some(guard) = opts.include_guard_prefix.as_deref() {
198 if !guard.is_empty() {
199 writeln!(out, "// guard-prefix: {guard}").map_err(fmt_err)?;
202 }
203 }
204 writeln!(out).map_err(fmt_err)?;
205 for h in &includes.headers {
206 writeln!(out, "#include {h}").map_err(fmt_err)?;
207 }
208 writeln!(out).map_err(fmt_err)?;
209 Ok(())
210}
211
212fn collect_includes(spec: &Specification, inc: &mut Includes) {
214 for d in &spec.definitions {
215 collect_in_def(d, inc);
216 }
217}
218
219fn collect_in_def(d: &Definition, inc: &mut Includes) {
221 match d {
222 Definition::Module(m) => {
223 for sub in &m.definitions {
224 collect_in_def(sub, inc);
225 }
226 }
227 Definition::Type(td) => collect_in_typedecl(td, inc),
228 Definition::Const(_) => {}
229 Definition::Except(e) => {
230 inc.add("<exception>");
231 for m in &e.members {
232 collect_in_typespec(&m.type_spec, inc);
233 for decl in &m.declarators {
234 if matches!(decl, Declarator::Array(_)) {
235 inc.add("<array>");
236 }
237 }
238 }
239 }
240 Definition::Interface(_)
241 | Definition::ValueBox(_)
242 | Definition::ValueForward(_)
243 | Definition::ValueDef(_)
244 | Definition::TypeId(_)
245 | Definition::TypePrefix(_)
246 | Definition::Import(_)
247 | Definition::Component(_)
248 | Definition::Home(_)
249 | Definition::Event(_)
250 | Definition::Porttype(_)
251 | Definition::Connector(_)
252 | Definition::TemplateModule(_)
253 | Definition::TemplateModuleInst(_)
254 | Definition::Annotation(_)
255 | Definition::VendorExtension(_) => {
256 }
259 }
260}
261
262fn collect_in_typedecl(td: &TypeDecl, inc: &mut Includes) {
263 match td {
264 TypeDecl::Constr(c) => match c {
265 ConstrTypeDecl::Struct(StructDcl::Def(s)) => {
266 if !s.members.is_empty() {
269 inc.add("<utility>");
270 }
271 for m in &s.members {
272 collect_in_typespec(&m.type_spec, inc);
273 for decl in &m.declarators {
274 if matches!(decl, Declarator::Array(_)) {
275 inc.add("<array>");
276 }
277 }
278 if has_optional_annotation(&m.annotations) {
279 inc.add("<optional>");
280 }
281 if has_shared_annotation(&m.annotations) {
282 inc.add("<memory>");
283 }
284 }
285 }
286 ConstrTypeDecl::Struct(StructDcl::Forward(_)) => {}
287 ConstrTypeDecl::Union(UnionDcl::Def(u)) => {
288 inc.add("<variant>");
289 for c in &u.cases {
290 collect_in_typespec(&c.element.type_spec, inc);
291 if matches!(c.element.declarator, Declarator::Array(_)) {
292 inc.add("<array>");
293 }
294 }
295 }
296 ConstrTypeDecl::Union(UnionDcl::Forward(_)) => {}
297 ConstrTypeDecl::Enum(_) | ConstrTypeDecl::Bitset(_) | ConstrTypeDecl::Bitmask(_) => {}
298 },
299 TypeDecl::Typedef(t) => {
300 collect_in_typespec(&t.type_spec, inc);
301 for decl in &t.declarators {
302 if matches!(decl, Declarator::Array(_)) {
303 inc.add("<array>");
304 }
305 }
306 }
307 TypeDecl::Native(_) => {}
309 }
310}
311
312fn collect_in_typespec(ts: &TypeSpec, inc: &mut Includes) {
314 match ts {
315 TypeSpec::Primitive(_) => {}
316 TypeSpec::Scoped(_) => {}
317 TypeSpec::Sequence(s) => {
318 inc.add("<vector>");
319 collect_in_typespec(&s.elem, inc);
320 }
321 TypeSpec::String(_) => {
322 inc.add("<string>");
324 }
325 TypeSpec::Map(m) => {
326 inc.add("<map>");
327 collect_in_typespec(&m.key, inc);
328 collect_in_typespec(&m.value, inc);
329 }
330 TypeSpec::Fixed(_) => {
331 inc.add("<cstdint>");
335 }
336 TypeSpec::Any => {
337 inc.add("<cstdint>");
339 }
340 }
341}
342
343struct EmitCtx<'o> {
345 opts: &'o CppGenOptions,
346 indent_level: usize,
347}
348
349impl<'o> EmitCtx<'o> {
350 fn new(opts: &'o CppGenOptions) -> Self {
351 Self {
352 opts,
353 indent_level: 0,
354 }
355 }
356
357 fn indent(&self) -> String {
358 " ".repeat(self.indent_level * self.opts.indent_width)
359 }
360
361 fn open_namespace(&mut self, out: &mut String, name: &str) -> Result<(), CppGenError> {
362 writeln!(out, "{}namespace {name} {{", self.indent()).map_err(fmt_err)?;
363 self.indent_level += 1;
364 Ok(())
365 }
366
367 fn close_namespace(&mut self, out: &mut String, name: &str) -> Result<(), CppGenError> {
368 self.indent_level = self.indent_level.saturating_sub(1);
369 writeln!(out, "{}}} // namespace {name}", self.indent()).map_err(fmt_err)?;
370 Ok(())
371 }
372}
373
374fn emit_definition(
376 out: &mut String,
377 ctx: &mut EmitCtx<'_>,
378 def: &Definition,
379) -> Result<(), CppGenError> {
380 match def {
381 Definition::Module(m) => emit_module(out, ctx, m),
382 Definition::Type(td) => emit_type_decl(out, ctx, td),
383 Definition::Const(c) => emit_const_decl(out, ctx, c),
384 Definition::Except(e) => emit_exception(out, ctx, e),
385 Definition::Interface(InterfaceDcl::Def(iface)) => {
386 emit_interface_stub(out, ctx, iface)
391 }
392 Definition::Interface(InterfaceDcl::Forward(f)) => {
393 check_identifier(&f.name.text)?;
394 writeln!(out, "{}class {};", ctx.indent(), f.name.text).map_err(fmt_err)?;
395 Ok(())
396 }
397 Definition::ValueDef(v) => emit_value_type(out, ctx, v),
398 Definition::ValueBox(_) | Definition::ValueForward(_) => {
399 Ok(())
403 }
404 Definition::TypeId(_)
405 | Definition::TypePrefix(_)
406 | Definition::Import(_)
407 | Definition::Component(_)
408 | Definition::Home(_)
409 | Definition::Event(_)
410 | Definition::Porttype(_)
411 | Definition::Connector(_)
412 | Definition::TemplateModule(_)
413 | Definition::TemplateModuleInst(_) => Err(CppGenError::UnsupportedConstruct {
414 construct: "corba/ccm/template construct".into(),
415 context: None,
416 }),
417 Definition::Annotation(_) => {
418 Ok(())
422 }
423 Definition::VendorExtension(v) => Err(CppGenError::UnsupportedConstruct {
424 construct: format!("vendor-extension:{}", v.production_name),
425 context: None,
426 }),
427 }
428}
429
430fn emit_module(out: &mut String, ctx: &mut EmitCtx<'_>, m: &ModuleDef) -> Result<(), CppGenError> {
432 check_identifier(&m.name.text)?;
433 ctx.open_namespace(out, &m.name.text)?;
434 for d in &m.definitions {
435 emit_definition(out, ctx, d)?;
436 }
437 ctx.close_namespace(out, &m.name.text)?;
438 Ok(())
439}
440
441fn emit_type_decl(
442 out: &mut String,
443 ctx: &mut EmitCtx<'_>,
444 td: &TypeDecl,
445) -> Result<(), CppGenError> {
446 match td {
447 TypeDecl::Constr(c) => match c {
448 ConstrTypeDecl::Struct(StructDcl::Def(s)) => emit_struct(out, ctx, s),
449 ConstrTypeDecl::Struct(StructDcl::Forward(f)) => {
450 check_identifier(&f.name.text)?;
451 writeln!(out, "{}class {};", ctx.indent(), f.name.text).map_err(fmt_err)?;
452 Ok(())
453 }
454 ConstrTypeDecl::Union(UnionDcl::Def(u)) => emit_union(out, ctx, u),
455 ConstrTypeDecl::Union(UnionDcl::Forward(f)) => {
456 check_identifier(&f.name.text)?;
457 writeln!(out, "{}class {};", ctx.indent(), f.name.text).map_err(fmt_err)?;
458 Ok(())
459 }
460 ConstrTypeDecl::Enum(e) => emit_enum(out, ctx, e),
461 ConstrTypeDecl::Bitset(b) => {
462 check_identifier(&b.name.text)?;
463 let ind = ctx.indent();
464 let inner = " ".repeat((ctx.indent_level + 1) * ctx.opts.indent_width);
465 emit_bitset(out, &ind, &inner, b)
466 }
467 ConstrTypeDecl::Bitmask(b) => {
468 check_identifier(&b.name.text)?;
469 let ind = ctx.indent();
470 let inner = " ".repeat((ctx.indent_level + 1) * ctx.opts.indent_width);
471 emit_bitmask(out, &ind, &inner, b)
472 }
473 },
474 TypeDecl::Typedef(t) => emit_typedef(out, ctx, t),
475 TypeDecl::Native(_) => Ok(()),
478 }
479}
480
481fn emit_struct(out: &mut String, ctx: &mut EmitCtx<'_>, s: &StructDef) -> Result<(), CppGenError> {
482 check_identifier(&s.name.text)?;
483 let ind = ctx.indent();
484
485 emit_verbatim_at(out, &ind, &s.annotations, PlacementKind::BeforeDeclaration)?;
488
489 if let Some(base) = &s.base {
491 let base_str = scoped_to_cpp(base);
492 writeln!(out, "{ind}class {} : public {} {{", s.name.text, base_str).map_err(fmt_err)?;
493 } else {
494 writeln!(out, "{ind}class {} {{", s.name.text).map_err(fmt_err)?;
495 }
496 writeln!(out, "{ind}public:").map_err(fmt_err)?;
497
498 let inner = " ".repeat((ctx.indent_level + 1) * ctx.opts.indent_width);
499
500 emit_verbatim_at(out, &inner, &s.annotations, PlacementKind::BeginDeclaration)?;
503
504 writeln!(out, "{inner}{}() = default;", s.name.text).map_err(fmt_err)?;
506 writeln!(out, "{inner}~{}() = default;", s.name.text).map_err(fmt_err)?;
507
508 emit_field_order_ctor(out, ctx, s, &inner)?;
514 writeln!(out).map_err(fmt_err)?;
515
516 writeln!(out, "{ind}private:").map_err(fmt_err)?;
518 for m in &s.members {
519 emit_struct_member_field(out, ctx, m)?;
520 }
521 writeln!(out).map_err(fmt_err)?;
522
523 writeln!(out, "{ind}public:").map_err(fmt_err)?;
525 for m in &s.members {
526 emit_struct_member_accessors(out, ctx, m)?;
527 }
528
529 emit_verbatim_at(out, &inner, &s.annotations, PlacementKind::EndDeclaration)?;
532
533 writeln!(out, "{ind}}};").map_err(fmt_err)?;
534
535 emit_verbatim_at(out, &ind, &s.annotations, PlacementKind::AfterDeclaration)?;
538
539 writeln!(out).map_err(fmt_err)?;
540 Ok(())
541}
542
543fn member_storage_type(m: &Member, decl: &Declarator) -> Result<String, CppGenError> {
547 let cpp_ty = type_for_declarator(&m.type_spec, decl)?;
548 let core_ty = if has_shared_annotation(&m.annotations) {
549 format!("std::shared_ptr<{cpp_ty}>")
550 } else {
551 cpp_ty
552 };
553 if has_optional_annotation(&m.annotations) {
554 Ok(format!("std::optional<{core_ty}>"))
555 } else {
556 Ok(core_ty)
557 }
558}
559
560fn emit_field_order_ctor(
566 out: &mut String,
567 _ctx: &EmitCtx<'_>,
568 s: &StructDef,
569 inner: &str,
570) -> Result<(), CppGenError> {
571 let mut fields: Vec<(String, String)> = Vec::new();
573 for m in &s.members {
574 for decl in &m.declarators {
575 let name = decl.name();
576 check_identifier(&name.text)?;
577 fields.push((member_storage_type(m, decl)?, name.text.clone()));
578 }
579 }
580 if fields.is_empty() {
581 return Ok(());
582 }
583
584 let params = fields
585 .iter()
586 .map(|(ty, name)| format!("{ty} {name}"))
587 .collect::<Vec<_>>()
588 .join(", ");
589 let inits = fields
590 .iter()
591 .map(|(_, name)| format!("{name}_(std::move({name}))"))
592 .collect::<Vec<_>>()
593 .join(", ");
594
595 writeln!(
596 out,
597 "{inner}{}({params})\n{inner} : {inits} {{}}",
598 s.name.text
599 )
600 .map_err(fmt_err)?;
601 Ok(())
602}
603
604fn emit_struct_member_field(
605 out: &mut String,
606 ctx: &EmitCtx<'_>,
607 m: &Member,
608) -> Result<(), CppGenError> {
609 let inner = " ".repeat((ctx.indent_level + 1) * ctx.opts.indent_width);
610 let optional = has_optional_annotation(&m.annotations);
611 let shared = has_shared_annotation(&m.annotations);
612 for decl in &m.declarators {
613 let cpp_ty = type_for_declarator(&m.type_spec, decl)?;
614 let name = decl.name();
615 check_identifier(&name.text)?;
616 let key_marker = if has_key_annotation(&m.annotations) {
617 " // @key"
618 } else {
619 ""
620 };
621 let core_ty = if shared {
624 format!("std::shared_ptr<{cpp_ty}>")
625 } else {
626 cpp_ty
627 };
628 if optional {
629 writeln!(
630 out,
631 "{inner}std::optional<{core_ty}> {}_;{key_marker}",
632 name.text
633 )
634 .map_err(fmt_err)?;
635 } else {
636 writeln!(out, "{inner}{core_ty} {}_;{key_marker}", name.text).map_err(fmt_err)?;
637 }
638 }
639 Ok(())
640}
641
642fn emit_struct_member_accessors(
643 out: &mut String,
644 ctx: &EmitCtx<'_>,
645 m: &Member,
646) -> Result<(), CppGenError> {
647 let inner = " ".repeat((ctx.indent_level + 1) * ctx.opts.indent_width);
648 let optional = has_optional_annotation(&m.annotations);
649 let shared = has_shared_annotation(&m.annotations);
650 for decl in &m.declarators {
651 let cpp_ty = type_for_declarator(&m.type_spec, decl)?;
652 let name = &decl.name().text;
653 let core_ty = if shared {
654 format!("std::shared_ptr<{cpp_ty}>")
655 } else {
656 cpp_ty.clone()
657 };
658 let storage_ty = if optional {
659 format!("std::optional<{core_ty}>")
660 } else {
661 core_ty
662 };
663 writeln!(out, "{inner}{storage_ty}& {name}() {{ return {name}_; }}").map_err(fmt_err)?;
664 writeln!(
665 out,
666 "{inner}const {storage_ty}& {name}() const {{ return {name}_; }}"
667 )
668 .map_err(fmt_err)?;
669 writeln!(
670 out,
671 "{inner}void {name}(const {storage_ty}& value) {{ {name}_ = value; }}"
672 )
673 .map_err(fmt_err)?;
674 }
675 Ok(())
676}
677
678fn emit_union(out: &mut String, ctx: &mut EmitCtx<'_>, u: &UnionDef) -> Result<(), CppGenError> {
679 check_identifier(&u.name.text)?;
680 let ind = ctx.indent();
681 emit_verbatim_at(out, &ind, &u.annotations, PlacementKind::BeforeDeclaration)?;
682 writeln!(out, "{ind}class {} {{", u.name.text).map_err(fmt_err)?;
683 writeln!(out, "{ind}public:").map_err(fmt_err)?;
684 let inner = " ".repeat((ctx.indent_level + 1) * ctx.opts.indent_width);
685 emit_verbatim_at(out, &inner, &u.annotations, PlacementKind::BeginDeclaration)?;
686
687 let disc_ty = switch_type_to_cpp(&u.switch_type)?;
688
689 let mut variant_types: Vec<String> = Vec::new();
691 for c in &u.cases {
692 let cpp_ty = type_for_declarator(&c.element.type_spec, &c.element.declarator)?;
693 if !variant_types.iter().any(|t| t == &cpp_ty) {
694 variant_types.push(cpp_ty);
695 }
696 }
697 let variant_str = if variant_types.is_empty() {
698 "std::monostate".to_string()
699 } else {
700 variant_types.join(", ")
701 };
702
703 writeln!(
704 out,
705 "{inner}using value_type = std::variant<{variant_str}>;"
706 )
707 .map_err(fmt_err)?;
708 writeln!(out, "{inner}{}() = default;", u.name.text).map_err(fmt_err)?;
709 writeln!(out, "{inner}~{}() = default;", u.name.text).map_err(fmt_err)?;
710 writeln!(out).map_err(fmt_err)?;
711
712 writeln!(
714 out,
715 "{inner}{disc_ty} _d() const {{ return discriminator_; }}"
716 )
717 .map_err(fmt_err)?;
718 writeln!(out, "{inner}void _d({disc_ty} d) {{ discriminator_ = d; }}").map_err(fmt_err)?;
719 writeln!(out, "{inner}value_type& value() {{ return value_; }}").map_err(fmt_err)?;
720 writeln!(
721 out,
722 "{inner}const value_type& value() const {{ return value_; }}"
723 )
724 .map_err(fmt_err)?;
725 writeln!(out).map_err(fmt_err)?;
726
727 let mut has_default = false;
729 for c in &u.cases {
730 emit_union_case_comment(out, &inner, c, &mut has_default)?;
731 }
732 if !has_default {
733 writeln!(out, "{inner}// no explicit 'default:' branch").map_err(fmt_err)?;
734 }
735
736 writeln!(out).map_err(fmt_err)?;
737 writeln!(out, "{ind}private:").map_err(fmt_err)?;
738 writeln!(out, "{inner}{disc_ty} discriminator_{{}};").map_err(fmt_err)?;
739 writeln!(out, "{inner}value_type value_{{}};").map_err(fmt_err)?;
740 emit_verbatim_at(out, &inner, &u.annotations, PlacementKind::EndDeclaration)?;
741 writeln!(out, "{ind}}};").map_err(fmt_err)?;
742 emit_verbatim_at(out, &ind, &u.annotations, PlacementKind::AfterDeclaration)?;
743 writeln!(out).map_err(fmt_err)?;
744 Ok(())
745}
746
747fn emit_union_case_comment(
748 out: &mut String,
749 inner: &str,
750 c: &Case,
751 has_default: &mut bool,
752) -> Result<(), CppGenError> {
753 for label in &c.labels {
754 match label {
755 CaseLabel::Default => {
756 *has_default = true;
757 writeln!(
758 out,
759 "{inner}// case default -> {}",
760 declarator_name(&c.element.declarator)
761 )
762 .map_err(fmt_err)?;
763 }
764 CaseLabel::Value(expr) => {
765 let val = const_expr_to_cpp(expr);
766 writeln!(
767 out,
768 "{inner}// case {val} -> {}",
769 declarator_name(&c.element.declarator)
770 )
771 .map_err(fmt_err)?;
772 }
773 }
774 }
775 Ok(())
776}
777
778pub(crate) fn declarator_name(d: &Declarator) -> &str {
779 &d.name().text
780}
781
782fn emit_enum(out: &mut String, ctx: &mut EmitCtx<'_>, e: &EnumDef) -> Result<(), CppGenError> {
783 check_identifier(&e.name.text)?;
784 let ind = ctx.indent();
785 emit_verbatim_at(out, &ind, &e.annotations, PlacementKind::BeforeDeclaration)?;
786 writeln!(out, "{ind}enum class {} : int32_t {{", e.name.text).map_err(fmt_err)?;
787 let inner = " ".repeat((ctx.indent_level + 1) * ctx.opts.indent_width);
788 emit_verbatim_at(out, &inner, &e.annotations, PlacementKind::BeginDeclaration)?;
789 for en in &e.enumerators {
790 check_identifier(&en.name.text)?;
791 writeln!(out, "{inner}{},", en.name.text).map_err(fmt_err)?;
792 }
793 emit_verbatim_at(out, &inner, &e.annotations, PlacementKind::EndDeclaration)?;
794 writeln!(out, "{ind}}};").map_err(fmt_err)?;
795 emit_verbatim_at(out, &ind, &e.annotations, PlacementKind::AfterDeclaration)?;
796 writeln!(out).map_err(fmt_err)?;
797 Ok(())
798}
799
800fn emit_interface_stub(
801 out: &mut String,
802 ctx: &mut EmitCtx<'_>,
803 iface: &InterfaceDef,
804) -> Result<(), CppGenError> {
805 let name = &iface.name.text;
806 check_identifier(name)?;
807 let ind = ctx.indent();
808 let inner = " ".repeat((ctx.indent_level + 1) * ctx.opts.indent_width);
809
810 emit_verbatim_at(
811 out,
812 &ind,
813 &iface.annotations,
814 PlacementKind::BeforeDeclaration,
815 )?;
816
817 if iface.bases.is_empty() {
819 writeln!(out, "{ind}class {name} {{").map_err(fmt_err)?;
820 } else {
821 let bases: Vec<String> = iface
822 .bases
823 .iter()
824 .map(|b| format!("public virtual {}", scoped_to_cpp(b)))
825 .collect();
826 writeln!(out, "{ind}class {name} : {} {{", bases.join(", ")).map_err(fmt_err)?;
827 }
828 writeln!(out, "{ind}public:").map_err(fmt_err)?;
829 writeln!(out, "{inner}virtual ~{name}() = default;").map_err(fmt_err)?;
830
831 for export in &iface.exports {
832 match export {
833 Export::Op(op) => emit_interface_op(out, &inner, op)?,
834 Export::Attr(attr) => emit_interface_attr(out, &inner, attr)?,
835 Export::Type(td) => emit_type_decl(out, ctx, td)?,
836 Export::Const(c) => emit_const_decl(out, ctx, c)?,
837 Export::Except(e) => emit_exception(out, ctx, e)?,
838 }
839 }
840
841 writeln!(out, "{ind}}};").map_err(fmt_err)?;
842 emit_verbatim_at(
843 out,
844 &ind,
845 &iface.annotations,
846 PlacementKind::AfterDeclaration,
847 )?;
848 writeln!(out).map_err(fmt_err)?;
849 Ok(())
850}
851
852fn emit_interface_op(out: &mut String, inner: &str, op: &OpDecl) -> Result<(), CppGenError> {
853 check_identifier(&op.name.text)?;
854 let ret = match &op.return_type {
855 None => "void".to_string(),
856 Some(t) => typespec_to_cpp(t)?,
857 };
858 let params: Vec<String> = op
859 .params
860 .iter()
861 .map(|p| -> Result<String, CppGenError> {
862 let ty = typespec_to_cpp(&p.type_spec)?;
863 let qual = match p.attribute {
866 ParamAttribute::In => format!("const {ty}&"),
867 ParamAttribute::Out | ParamAttribute::InOut => format!("{ty}&"),
868 };
869 Ok(format!("{qual} {}", p.name.text))
870 })
871 .collect::<Result<_, _>>()?;
872 let raises_comment = if op.raises.is_empty() {
873 String::new()
874 } else {
875 let raises: Vec<String> = op.raises.iter().map(scoped_to_cpp).collect();
876 format!(" /* throws {} */", raises.join(", "))
877 };
878 writeln!(
879 out,
880 "{inner}virtual {ret} {}({}) = 0;{raises_comment}",
881 op.name.text,
882 params.join(", ")
883 )
884 .map_err(fmt_err)?;
885 Ok(())
886}
887
888fn emit_interface_attr(
889 out: &mut String,
890 inner: &str,
891 attr: &zerodds_idl::ast::AttrDecl,
892) -> Result<(), CppGenError> {
893 check_identifier(&attr.name.text)?;
894 let ty = typespec_to_cpp(&attr.type_spec)?;
895 writeln!(out, "{inner}virtual {ty} {}() const = 0;", attr.name.text).map_err(fmt_err)?;
897 if !attr.readonly {
899 writeln!(
900 out,
901 "{inner}virtual void {}(const {ty}& value) = 0;",
902 attr.name.text
903 )
904 .map_err(fmt_err)?;
905 }
906 Ok(())
907}
908
909fn emit_value_type(
910 out: &mut String,
911 ctx: &mut EmitCtx<'_>,
912 v: &ValueDef,
913) -> Result<(), CppGenError> {
914 let name = &v.name.text;
915 check_identifier(name)?;
916 let ind = ctx.indent();
917 let inner = " ".repeat((ctx.indent_level + 1) * ctx.opts.indent_width);
918
919 emit_verbatim_at(out, &ind, &v.annotations, PlacementKind::BeforeDeclaration)?;
920
921 let mut bases: Vec<String> = Vec::new();
925 if let Some(inh) = &v.inheritance {
926 for b in &inh.bases {
927 bases.push(format!("public virtual {}", scoped_to_cpp(b)));
928 }
929 for s in &inh.supports {
930 bases.push(format!("public virtual {}", scoped_to_cpp(s)));
931 }
932 }
933 if bases.is_empty() {
934 writeln!(out, "{ind}class {name} {{").map_err(fmt_err)?;
935 } else {
936 writeln!(out, "{ind}class {name} : {} {{", bases.join(", ")).map_err(fmt_err)?;
937 }
938 writeln!(out, "{ind}public:").map_err(fmt_err)?;
939 writeln!(out, "{inner}virtual ~{name}() = default;").map_err(fmt_err)?;
940
941 let mut has_protected = false;
943 for el in &v.elements {
944 match el {
945 ValueElement::State(s) if matches!(s.visibility, StateVisibility::Public) => {
946 let ty = typespec_to_cpp(&s.type_spec)?;
947 for d in &s.declarators {
948 let n = &d.name().text;
949 writeln!(out, "{inner}virtual const {ty}& {n}() const = 0;")
950 .map_err(fmt_err)?;
951 writeln!(out, "{inner}virtual void {n}(const {ty}& value) = 0;")
952 .map_err(fmt_err)?;
953 }
954 }
955 ValueElement::State(s) if matches!(s.visibility, StateVisibility::Private) => {
956 has_protected = true;
957 }
958 ValueElement::Export(Export::Op(op)) => emit_interface_op(out, &inner, op)?,
959 ValueElement::Export(Export::Attr(a)) => emit_interface_attr(out, &inner, a)?,
960 _ => {}
961 }
962 }
963
964 if has_protected {
966 writeln!(out, "{ind}protected:").map_err(fmt_err)?;
967 for el in &v.elements {
968 if let ValueElement::State(s) = el {
969 if matches!(s.visibility, StateVisibility::Private) {
970 let ty = typespec_to_cpp(&s.type_spec)?;
971 for d in &s.declarators {
972 let n = &d.name().text;
973 writeln!(out, "{inner}virtual const {ty}& {n}() const = 0;")
974 .map_err(fmt_err)?;
975 writeln!(out, "{inner}virtual void {n}(const {ty}& value) = 0;")
976 .map_err(fmt_err)?;
977 }
978 }
979 }
980 }
981 }
982
983 writeln!(out, "{ind}}};").map_err(fmt_err)?;
984
985 let factories: Vec<&zerodds_idl::ast::InitDcl> = v
987 .elements
988 .iter()
989 .filter_map(|e| {
990 if let ValueElement::Init(i) = e {
991 Some(i)
992 } else {
993 None
994 }
995 })
996 .collect();
997 if !factories.is_empty() {
998 writeln!(out, "{ind}class {name}_factory {{").map_err(fmt_err)?;
999 writeln!(out, "{ind}public:").map_err(fmt_err)?;
1000 writeln!(out, "{inner}virtual ~{name}_factory() = default;").map_err(fmt_err)?;
1001 for f in &factories {
1002 check_identifier(&f.name.text)?;
1003 let params: Vec<String> = f
1004 .params
1005 .iter()
1006 .map(|p| -> Result<String, CppGenError> {
1007 let ty = typespec_to_cpp(&p.type_spec)?;
1008 let qual = match p.attribute {
1009 ParamAttribute::In => format!("const {ty}&"),
1010 ParamAttribute::Out | ParamAttribute::InOut => format!("{ty}&"),
1011 };
1012 Ok(format!("{qual} {}", p.name.text))
1013 })
1014 .collect::<Result<_, _>>()?;
1015 writeln!(
1016 out,
1017 "{inner}virtual std::shared_ptr<{name}> {}({}) = 0;",
1018 f.name.text,
1019 params.join(", ")
1020 )
1021 .map_err(fmt_err)?;
1022 }
1023 writeln!(out, "{ind}}};").map_err(fmt_err)?;
1024 }
1025
1026 emit_verbatim_at(out, &ind, &v.annotations, PlacementKind::AfterDeclaration)?;
1027 writeln!(out).map_err(fmt_err)?;
1028 Ok(())
1029}
1030
1031fn emit_typedef(
1032 out: &mut String,
1033 ctx: &mut EmitCtx<'_>,
1034 t: &TypedefDecl,
1035) -> Result<(), CppGenError> {
1036 let ind = ctx.indent();
1037 emit_verbatim_at(out, &ind, &t.annotations, PlacementKind::BeforeDeclaration)?;
1038 for decl in &t.declarators {
1039 let alias = &decl.name().text;
1040 check_identifier(alias)?;
1041 let target_ty = type_for_declarator(&t.type_spec, decl)?;
1042 writeln!(out, "{ind}using {alias} = {target_ty};").map_err(fmt_err)?;
1043 }
1044 emit_verbatim_at(out, &ind, &t.annotations, PlacementKind::AfterDeclaration)?;
1045 writeln!(out).map_err(fmt_err)?;
1046 Ok(())
1047}
1048
1049fn emit_const_decl(
1050 out: &mut String,
1051 ctx: &mut EmitCtx<'_>,
1052 c: &zerodds_idl::ast::ConstDecl,
1053) -> Result<(), CppGenError> {
1054 check_identifier(&c.name.text)?;
1055 let ind = ctx.indent();
1056 let cpp_ty = match &c.type_ {
1057 zerodds_idl::ast::ConstType::Integer(i) => crate::type_map::integer_to_cpp(*i).to_string(),
1058 zerodds_idl::ast::ConstType::Floating(f) => {
1059 crate::type_map::floating_to_cpp(*f).to_string()
1060 }
1061 zerodds_idl::ast::ConstType::Boolean => "bool".into(),
1062 zerodds_idl::ast::ConstType::Char => "char".into(),
1063 zerodds_idl::ast::ConstType::WideChar => "wchar_t".into(),
1064 zerodds_idl::ast::ConstType::Octet => "uint8_t".into(),
1065 zerodds_idl::ast::ConstType::String { wide: false } => "std::string".into(),
1066 zerodds_idl::ast::ConstType::String { wide: true } => "std::wstring".into(),
1067 zerodds_idl::ast::ConstType::Scoped(s) => scoped_to_cpp(s),
1068 zerodds_idl::ast::ConstType::Fixed => {
1069 "::dds::core::Fixed<31, 0>".into()
1073 }
1074 };
1075 let val = const_expr_to_cpp(&c.value);
1076 writeln!(out, "{ind}constexpr {cpp_ty} {} = {val};", c.name.text).map_err(fmt_err)?;
1077 Ok(())
1078}
1079
1080fn emit_exception(
1081 out: &mut String,
1082 ctx: &mut EmitCtx<'_>,
1083 e: &ExceptDecl,
1084) -> Result<(), CppGenError> {
1085 check_identifier(&e.name.text)?;
1086 let ind = ctx.indent();
1087 writeln!(out, "{ind}class {} : public std::exception {{", e.name.text).map_err(fmt_err)?;
1088 writeln!(out, "{ind}public:").map_err(fmt_err)?;
1089 let inner = " ".repeat((ctx.indent_level + 1) * ctx.opts.indent_width);
1090 writeln!(out, "{inner}{}() = default;", e.name.text).map_err(fmt_err)?;
1091 writeln!(out, "{inner}~{}() override = default;", e.name.text).map_err(fmt_err)?;
1092 writeln!(
1093 out,
1094 "{inner}const char* what() const noexcept override {{ return \"{}\"; }}",
1095 e.name.text
1096 )
1097 .map_err(fmt_err)?;
1098 writeln!(out).map_err(fmt_err)?;
1099 writeln!(out, "{ind}private:").map_err(fmt_err)?;
1100 for m in &e.members {
1101 for decl in &m.declarators {
1102 let cpp_ty = type_for_declarator(&m.type_spec, decl)?;
1103 let name = &decl.name().text;
1104 check_identifier(name)?;
1105 writeln!(out, "{inner}{cpp_ty} {name}_;").map_err(fmt_err)?;
1106 }
1107 }
1108 writeln!(out, "{ind}}};").map_err(fmt_err)?;
1109 writeln!(out).map_err(fmt_err)?;
1110 Ok(())
1111}
1112
1113pub(crate) fn type_for_declarator(ts: &TypeSpec, decl: &Declarator) -> Result<String, CppGenError> {
1120 let base = typespec_to_cpp(ts)?;
1121 match decl {
1122 Declarator::Simple(_) => Ok(base),
1123 Declarator::Array(arr) => {
1124 let mut out = base;
1127 for size in arr.sizes.iter().rev() {
1128 let n = const_expr_to_usize(size).unwrap_or_default();
1129 out = format!("std::array<{out}, {n}>");
1130 }
1131 Ok(out)
1132 }
1133 }
1134}
1135
1136pub(crate) fn typespec_to_cpp(ts: &TypeSpec) -> Result<String, CppGenError> {
1138 match ts {
1139 TypeSpec::Primitive(p) => Ok(primitive_to_cpp(*p).to_string()),
1140 TypeSpec::Scoped(s) => Ok(scoped_to_cpp(s)),
1141 TypeSpec::Sequence(s) => {
1142 let inner = typespec_to_cpp(&s.elem)?;
1143 Ok(format!("std::vector<{inner}>"))
1144 }
1145 TypeSpec::String(s) => {
1146 if s.wide {
1147 Ok("std::wstring".into())
1148 } else {
1149 Ok("std::string".into())
1150 }
1151 }
1152 TypeSpec::Map(m) => {
1153 let k = typespec_to_cpp(&m.key)?;
1154 let v = typespec_to_cpp(&m.value)?;
1155 Ok(format!("std::map<{k}, {v}>"))
1156 }
1157 TypeSpec::Fixed(f) => {
1158 let digits = const_expr_to_u32(&f.digits).unwrap_or(0);
1162 let scale = const_expr_to_u32(&f.scale).unwrap_or(0);
1163 Ok(format!("::dds::core::Fixed<{digits}, {scale}>"))
1164 }
1165 TypeSpec::Any => {
1166 Err(CppGenError::UnsupportedConstruct {
1174 construct: "any (no DDS-XTypes wire form / no TypeObject)".to_string(),
1175 context: None,
1176 })
1177 }
1178 }
1179}
1180
1181pub(crate) fn switch_type_to_cpp(s: &SwitchTypeSpec) -> Result<String, CppGenError> {
1182 Ok(match s {
1183 SwitchTypeSpec::Integer(i) => crate::type_map::integer_to_cpp(*i).to_string(),
1184 SwitchTypeSpec::Char => "char".into(),
1185 SwitchTypeSpec::Boolean => "bool".into(),
1186 SwitchTypeSpec::Octet => "uint8_t".into(),
1187 SwitchTypeSpec::Scoped(s) => scoped_to_cpp(s),
1188 })
1189}
1190
1191pub(crate) fn scoped_to_cpp(s: &ScopedName) -> String {
1192 if s.parts.len() == 1 {
1194 if let Some(mapped) = TIME_DURATION_TYPES
1195 .iter()
1196 .find(|(idl, _)| *idl == s.parts[0].text)
1197 .map(|(_, cpp)| *cpp)
1198 {
1199 return mapped.to_string();
1200 }
1201 }
1202 if let Some(last) = s.parts.last() {
1206 if let Some(path) = TYPE_PATHS.with(|r| r.borrow().get(&last.text).cloned()) {
1207 return path;
1208 }
1209 }
1210 let parts: Vec<String> = s.parts.iter().map(|p| p.text.clone()).collect();
1211 let joined = parts.join("::");
1212 if s.absolute {
1213 format!("::{joined}")
1214 } else {
1215 joined
1216 }
1217}
1218
1219fn const_expr_to_u32(e: &ConstExpr) -> Option<u32> {
1224 if let ConstExpr::Literal(l) = e {
1225 if matches!(l.kind, LiteralKind::Integer) {
1226 return l.raw.parse::<u32>().ok();
1227 }
1228 }
1229 None
1230}
1231
1232pub(crate) fn const_expr_to_cpp(e: &ConstExpr) -> String {
1233 match e {
1234 ConstExpr::Literal(l) => literal_to_cpp(l),
1235 ConstExpr::Scoped(s) => scoped_to_cpp(s),
1236 ConstExpr::Unary { op, operand, .. } => {
1237 let prefix = match op {
1238 zerodds_idl::ast::UnaryOp::Plus => "+",
1239 zerodds_idl::ast::UnaryOp::Minus => "-",
1240 zerodds_idl::ast::UnaryOp::BitNot => "~",
1241 };
1242 format!("{prefix}{}", const_expr_to_cpp(operand))
1243 }
1244 ConstExpr::Binary { op, lhs, rhs, .. } => {
1245 let opstr = match op {
1246 zerodds_idl::ast::BinaryOp::Or => "|",
1247 zerodds_idl::ast::BinaryOp::Xor => "^",
1248 zerodds_idl::ast::BinaryOp::And => "&",
1249 zerodds_idl::ast::BinaryOp::Shl => "<<",
1250 zerodds_idl::ast::BinaryOp::Shr => ">>",
1251 zerodds_idl::ast::BinaryOp::Add => "+",
1252 zerodds_idl::ast::BinaryOp::Sub => "-",
1253 zerodds_idl::ast::BinaryOp::Mul => "*",
1254 zerodds_idl::ast::BinaryOp::Div => "/",
1255 zerodds_idl::ast::BinaryOp::Mod => "%",
1256 };
1257 format!(
1258 "({} {opstr} {})",
1259 const_expr_to_cpp(lhs),
1260 const_expr_to_cpp(rhs)
1261 )
1262 }
1263 }
1264}
1265
1266fn literal_to_cpp(l: &Literal) -> String {
1267 match l.kind {
1268 LiteralKind::Boolean => l.raw.clone(),
1269 LiteralKind::Integer | LiteralKind::Floating => l.raw.clone(),
1270 LiteralKind::Char => l.raw.clone(),
1271 LiteralKind::WideChar => l.raw.clone(),
1272 LiteralKind::String => l.raw.clone(),
1273 LiteralKind::WideString => l.raw.clone(),
1274 LiteralKind::Fixed => l.raw.clone(),
1275 }
1276}
1277
1278fn const_expr_to_usize(e: &ConstExpr) -> Option<usize> {
1279 match e {
1280 ConstExpr::Literal(l) if l.kind == LiteralKind::Integer => l.raw.parse::<usize>().ok(),
1281 _ => None,
1282 }
1283}
1284
1285fn has_key_annotation(anns: &[Annotation]) -> bool {
1290 has_named_annotation(anns, "key")
1291}
1292
1293fn has_optional_annotation(anns: &[Annotation]) -> bool {
1294 has_named_annotation(anns, "optional")
1295}
1296
1297fn has_shared_annotation(anns: &[Annotation]) -> bool {
1298 has_named_annotation(anns, "shared")
1299}
1300
1301fn has_named_annotation(anns: &[Annotation], name: &str) -> bool {
1302 anns.iter().any(|a| {
1303 a.name.parts.last().is_some_and(|p| p.text == name)
1304 && matches!(a.params, AnnotationParams::None | AnnotationParams::Empty)
1305 })
1306}
1307
1308fn find_uint_annotation(anns: &[Annotation], name: &str) -> Option<u32> {
1310 for a in anns {
1311 if a.name.parts.last().is_some_and(|p| p.text == name) {
1312 if let AnnotationParams::Single(expr) = &a.params {
1313 if let Some(v) = const_expr_as_u32(expr) {
1314 return Some(v);
1315 }
1316 }
1317 }
1318 }
1319 None
1320}
1321fn const_expr_as_u32(e: &ConstExpr) -> Option<u32> {
1325 match e {
1326 ConstExpr::Literal(Literal {
1327 kind: LiteralKind::Integer,
1328 raw,
1329 ..
1330 }) => parse_int_literal(raw).and_then(|v| u32::try_from(v).ok()),
1331 ConstExpr::Unary {
1332 op: zerodds_idl::ast::UnaryOp::Plus,
1333 operand,
1334 ..
1335 } => const_expr_as_u32(operand),
1336 _ => None,
1337 }
1338}
1339
1340fn parse_int_literal(raw: &str) -> Option<u64> {
1342 let s = raw.trim_end_matches(|c: char| matches!(c, 'l' | 'L' | 'u' | 'U'));
1343 if let Some(hex) = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")) {
1344 u64::from_str_radix(hex, 16).ok()
1345 } else if s.len() > 1 && s.starts_with('0') {
1346 u64::from_str_radix(&s[1..], 8).ok()
1347 } else {
1348 s.parse::<u64>().ok()
1349 }
1350}
1351
1352#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1354enum Extensibility {
1355 Final,
1356 Appendable,
1357 Mutable,
1358}
1359
1360fn struct_extensibility(anns: &[Annotation]) -> Extensibility {
1361 if has_named_annotation(anns, "final") {
1362 Extensibility::Final
1363 } else if has_named_annotation(anns, "mutable") {
1364 Extensibility::Mutable
1365 } else if has_named_annotation(anns, "appendable") {
1366 Extensibility::Appendable
1367 } else {
1368 Extensibility::Appendable
1380 }
1381}
1382
1383fn collect_inheritance_edges(
1391 defs: &[Definition],
1392 parents: &mut std::collections::HashMap<String, String>,
1393 prefix: &str,
1394) {
1395 for d in defs {
1396 match d {
1397 Definition::Module(m) => {
1398 let new_prefix = if prefix.is_empty() {
1399 m.name.text.clone()
1400 } else {
1401 format!("{prefix}::{}", m.name.text)
1402 };
1403 collect_inheritance_edges(&m.definitions, parents, &new_prefix);
1404 }
1405 Definition::Type(TypeDecl::Constr(ConstrTypeDecl::Struct(StructDcl::Def(s)))) => {
1406 let key = if prefix.is_empty() {
1407 s.name.text.clone()
1408 } else {
1409 format!("{prefix}::{}", s.name.text)
1410 };
1411 if let Some(b) = &s.base {
1412 let base_str = b
1413 .parts
1414 .iter()
1415 .map(|p| p.text.clone())
1416 .collect::<Vec<_>>()
1417 .join("::");
1418 parents.insert(key, base_str);
1419 }
1420 }
1421 _ => {}
1422 }
1423 }
1424}
1425
1426fn detect_inheritance_cycles(spec: &Specification) -> Result<(), CppGenError> {
1427 use std::collections::HashMap;
1428
1429 let mut parents: HashMap<String, String> = HashMap::new();
1430 collect_inheritance_edges(&spec.definitions, &mut parents, "");
1431
1432 for start in parents.keys() {
1434 let mut current = start.clone();
1435 let mut visited: BTreeSet<String> = BTreeSet::new();
1436 visited.insert(current.clone());
1437 while let Some(p) = parents.get(¤t) {
1438 let resolved = parents
1440 .keys()
1441 .find(|k| *k == p || k.ends_with(&format!("::{p}")))
1442 .cloned()
1443 .unwrap_or_else(|| p.clone());
1444 if visited.contains(&resolved) {
1445 return Err(CppGenError::InheritanceCycle {
1446 type_name: short_name(&resolved),
1447 });
1448 }
1449 visited.insert(resolved.clone());
1450 if resolved == current {
1452 return Err(CppGenError::InheritanceCycle {
1453 type_name: short_name(&resolved),
1454 });
1455 }
1456 current = resolved;
1457 if !parents.contains_key(¤t) {
1458 break;
1459 }
1460 }
1461 }
1462 Ok(())
1463}
1464
1465fn short_name(s: &str) -> String {
1466 s.rsplit("::").next().unwrap_or(s).to_string()
1467}
1468
1469fn collect_topic_structs<'a>(
1492 defs: &'a [Definition],
1493 prefix: &str,
1494 out: &mut Vec<(String, &'a StructDef)>,
1495) {
1496 for d in defs {
1497 match d {
1498 Definition::Module(m) => {
1499 let np = if prefix.is_empty() {
1500 m.name.text.clone()
1501 } else {
1502 format!("{prefix}::{}", m.name.text)
1503 };
1504 collect_topic_structs(&m.definitions, &np, out);
1505 }
1506 Definition::Type(TypeDecl::Constr(ConstrTypeDecl::Struct(StructDcl::Def(s)))) => {
1507 let fqn = if prefix.is_empty() {
1508 s.name.text.clone()
1509 } else {
1510 format!("{prefix}::{}", s.name.text)
1511 };
1512 out.push((fqn, s));
1513 }
1514 _ => {}
1515 }
1516 }
1517}
1518
1519fn collect_topic_unions<'a>(
1524 defs: &'a [Definition],
1525 prefix: &str,
1526 out: &mut Vec<(String, &'a UnionDef)>,
1527) {
1528 for d in defs {
1529 match d {
1530 Definition::Module(m) => {
1531 let np = if prefix.is_empty() {
1532 m.name.text.clone()
1533 } else {
1534 format!("{prefix}::{}", m.name.text)
1535 };
1536 collect_topic_unions(&m.definitions, &np, out);
1537 }
1538 Definition::Type(TypeDecl::Constr(ConstrTypeDecl::Union(UnionDcl::Def(u)))) => {
1539 let fqn = if prefix.is_empty() {
1540 u.name.text.clone()
1541 } else {
1542 format!("{prefix}::{}", u.name.text)
1543 };
1544 out.push((fqn, u));
1545 }
1546 _ => {}
1547 }
1548 }
1549}
1550
1551fn emit_topic_type_support_specs(
1552 out: &mut String,
1553 opts: &CppGenOptions,
1554 structs: &[(String, &StructDef)],
1555 unions: &[(String, &UnionDef)],
1556) -> Result<(), CppGenError> {
1557 writeln!(out).map_err(fmt_err)?;
1558 writeln!(
1559 out,
1560 "// DDS-PSM-Cxx topic_type_support<T> -- auto-generiert (XCDR2 Wire, XTypes 1.3 7.4)."
1561 )
1562 .map_err(fmt_err)?;
1563 writeln!(out, "namespace dds {{").map_err(fmt_err)?;
1564 writeln!(out, "namespace topic {{").map_err(fmt_err)?;
1565 writeln!(out).map_err(fmt_err)?;
1566
1567 let user_prefix = opts
1568 .namespace_prefix
1569 .as_deref()
1570 .filter(|p| !p.is_empty())
1571 .unwrap_or("");
1572 let union_fqn = |fqn: &str| -> String {
1580 if user_prefix.is_empty() {
1581 format!("::{fqn}")
1582 } else {
1583 format!("::{user_prefix}::{fqn}")
1584 }
1585 };
1586
1587 for (fqn, u) in unions {
1589 emit_union_topic_type_support(out, &union_fqn(fqn), fqn, u, TtsPhase::Decl)?;
1590 }
1591 for (fqn, s) in structs {
1592 emit_topic_type_support_for(out, &union_fqn(fqn), fqn, s, TtsPhase::Decl)?;
1593 }
1594 for (fqn, u) in unions {
1596 emit_union_topic_type_support(out, &union_fqn(fqn), fqn, u, TtsPhase::Def)?;
1597 }
1598 for (fqn, s) in structs {
1599 emit_topic_type_support_for(out, &union_fqn(fqn), fqn, s, TtsPhase::Def)?;
1600 }
1601
1602 writeln!(out, "}} // namespace topic").map_err(fmt_err)?;
1603 writeln!(out, "}} // namespace dds").map_err(fmt_err)?;
1604 Ok(())
1605}
1606
1607#[derive(Clone, Copy, PartialEq, Eq)]
1612enum TtsPhase {
1613 Decl,
1614 Def,
1615}
1616
1617fn member_codegen_supported(m: &Member) -> bool {
1621 !has_shared_annotation(&m.annotations)
1622}
1623
1624fn typespec_supported(ts: &TypeSpec) -> bool {
1628 match ts {
1629 TypeSpec::Primitive(_) => true,
1630 TypeSpec::String(_) => true,
1632 TypeSpec::Sequence(seq) => match &*seq.elem {
1638 TypeSpec::Primitive(_) => true,
1639 TypeSpec::String(_) => true,
1640 TypeSpec::Scoped(s) => {
1644 scoped_is_enum(s) || scoped_struct(s).is_some() || scoped_union(s).is_some()
1645 }
1646 TypeSpec::Sequence(_) => typespec_supported(&seq.elem),
1647 TypeSpec::Map(m) => typespec_supported(&m.key) && typespec_supported(&m.value),
1648 _ => false,
1649 },
1650 TypeSpec::Scoped(s) => {
1656 scoped_is_enum(s)
1657 || scoped_struct(s).is_some()
1658 || scoped_union(s).is_some()
1659 || scoped_bitholder(s).is_some()
1660 }
1661 TypeSpec::Map(m) => typespec_supported(&m.key) && typespec_supported(&m.value),
1664 TypeSpec::Fixed(_) => true,
1668 _ => false,
1669 }
1670}
1671
1672thread_local! {
1680 static ENUM_NAMES: RefCell<BTreeSet<String>> = const { RefCell::new(BTreeSet::new()) };
1681 static STRUCT_NAMES: RefCell<BTreeSet<String>> = const { RefCell::new(BTreeSet::new()) };
1682 static STRUCT_DEFS: RefCell<BTreeMap<String, StructDef>> = const { RefCell::new(BTreeMap::new()) };
1683 static UNION_NAMES: RefCell<BTreeSet<String>> = const { RefCell::new(BTreeSet::new()) };
1688 static UNION_DEFS: RefCell<BTreeMap<String, UnionDef>> = const { RefCell::new(BTreeMap::new()) };
1689 static TYPE_PATHS: RefCell<BTreeMap<String, String>> = const { RefCell::new(BTreeMap::new()) };
1694 static TYPEDEF_MAP: RefCell<BTreeMap<String, TypeSpec>> = const { RefCell::new(BTreeMap::new()) };
1697 static BITHOLDER_BYTES: RefCell<BTreeMap<String, u32>> = const { RefCell::new(BTreeMap::new()) };
1704 static BITSET_NAMES: RefCell<BTreeSet<String>> = const { RefCell::new(BTreeSet::new()) };
1708 static ENUM_BYTES: RefCell<BTreeMap<String, u32>> = const { RefCell::new(BTreeMap::new()) };
1712 static NEST_CTR: core::cell::Cell<u32> = const { core::cell::Cell::new(0) };
1715 static VISITING: RefCell<BTreeSet<String>> = const { RefCell::new(BTreeSet::new()) };
1723}
1724
1725fn next_nest_id() -> u32 {
1726 NEST_CTR.with(|c| {
1727 let v = c.get();
1728 c.set(v.wrapping_add(1));
1729 v
1730 })
1731}
1732
1733fn set_type_registry(spec: &Specification) {
1734 let mut r = Registry::default();
1735 let mut scope: Vec<String> = Vec::new();
1736 collect_type_names(&spec.definitions, &mut scope, &mut r);
1737 ENUM_NAMES.with(|c| *c.borrow_mut() = r.enums);
1738 STRUCT_NAMES.with(|c| *c.borrow_mut() = r.structs);
1739 STRUCT_DEFS.with(|c| *c.borrow_mut() = r.struct_defs);
1740 UNION_NAMES.with(|c| *c.borrow_mut() = r.unions);
1741 UNION_DEFS.with(|c| *c.borrow_mut() = r.union_defs);
1742 TYPE_PATHS.with(|c| *c.borrow_mut() = r.paths);
1743 TYPEDEF_MAP.with(|c| *c.borrow_mut() = r.typedefs);
1744 BITHOLDER_BYTES.with(|c| *c.borrow_mut() = r.bitholders);
1745 ENUM_BYTES.with(|c| *c.borrow_mut() = r.enum_bytes);
1746 BITSET_NAMES.with(|c| *c.borrow_mut() = r.bitsets);
1747}
1748
1749#[derive(Default)]
1750struct Registry {
1751 enums: BTreeSet<String>,
1752 structs: BTreeSet<String>,
1753 struct_defs: BTreeMap<String, StructDef>,
1754 unions: BTreeSet<String>,
1755 union_defs: BTreeMap<String, UnionDef>,
1756 paths: BTreeMap<String, String>,
1757 typedefs: BTreeMap<String, TypeSpec>,
1758 bitholders: BTreeMap<String, u32>,
1759 bitsets: BTreeSet<String>,
1760 enum_bytes: BTreeMap<String, u32>,
1764}
1765
1766fn holder_uint_for_bytes(bytes: u32) -> &'static str {
1768 match bytes {
1769 1 => "uint8_t",
1770 2 => "uint16_t",
1771 4 => "uint32_t",
1772 _ => "uint64_t",
1773 }
1774}
1775
1776fn holder_bytes_for_bits(bits: u32) -> u32 {
1779 match bits {
1780 0..=8 => 1,
1781 9..=16 => 2,
1782 17..=32 => 4,
1783 _ => 8,
1784 }
1785}
1786
1787fn abs_path(scope: &[String], name: &str) -> String {
1789 let mut p = String::from("::");
1790 for s in scope {
1791 p.push_str(s);
1792 p.push_str("::");
1793 }
1794 p.push_str(name);
1795 p
1796}
1797
1798fn collect_type_names(defs: &[Definition], scope: &mut Vec<String>, r: &mut Registry) {
1800 for d in defs {
1801 match d {
1802 Definition::Module(m) => {
1803 scope.push(m.name.text.clone());
1804 collect_type_names(&m.definitions, scope, r);
1805 scope.pop();
1806 }
1807 Definition::Type(TypeDecl::Constr(ConstrTypeDecl::Enum(e))) => {
1808 r.enums.insert(e.name.text.clone());
1809 r.paths
1810 .insert(e.name.text.clone(), abs_path(scope, &e.name.text));
1811 let bound = find_uint_annotation(&e.annotations, "bit_bound")
1813 .filter(|&v| (1..=32).contains(&v))
1814 .unwrap_or(32);
1815 let bytes = if bound <= 8 {
1816 1
1817 } else if bound <= 16 {
1818 2
1819 } else {
1820 4
1821 };
1822 r.enum_bytes.insert(e.name.text.clone(), bytes);
1823 }
1824 Definition::Type(TypeDecl::Constr(ConstrTypeDecl::Struct(StructDcl::Def(s)))) => {
1825 r.structs.insert(s.name.text.clone());
1826 r.struct_defs.insert(s.name.text.clone(), s.clone());
1827 r.paths
1828 .insert(s.name.text.clone(), abs_path(scope, &s.name.text));
1829 }
1830 Definition::Type(TypeDecl::Constr(ConstrTypeDecl::Union(UnionDcl::Def(u)))) => {
1831 r.unions.insert(u.name.text.clone());
1834 r.union_defs.insert(u.name.text.clone(), u.clone());
1835 r.paths
1836 .insert(u.name.text.clone(), abs_path(scope, &u.name.text));
1837 }
1838 Definition::Type(TypeDecl::Typedef(td)) => {
1839 for decl in &td.declarators {
1840 if let Declarator::Simple(n) = decl {
1841 r.typedefs.insert(n.text.clone(), td.type_spec.clone());
1842 }
1843 }
1844 }
1845 Definition::Type(TypeDecl::Constr(ConstrTypeDecl::Bitmask(b))) => {
1846 let bits = find_uint_annotation(&b.annotations, "bit_bound").unwrap_or(32);
1852 r.bitholders
1853 .insert(b.name.text.clone(), holder_bytes_for_bits(bits));
1854 r.paths
1855 .insert(b.name.text.clone(), abs_path(scope, &b.name.text));
1856 }
1857 Definition::Type(TypeDecl::Constr(ConstrTypeDecl::Bitset(b))) => {
1858 let total: u32 = b
1860 .bitfields
1861 .iter()
1862 .filter_map(|f| const_expr_to_u32(&f.spec.width))
1863 .sum();
1864 r.bitholders
1865 .insert(b.name.text.clone(), holder_bytes_for_bits(total));
1866 r.bitsets.insert(b.name.text.clone());
1867 r.paths
1868 .insert(b.name.text.clone(), abs_path(scope, &b.name.text));
1869 }
1870 _ => {}
1871 }
1872 }
1873}
1874
1875fn resolve_typedef_spec(ts: &TypeSpec) -> TypeSpec {
1880 let mut cur = ts.clone();
1881 for _ in 0..16 {
1882 let TypeSpec::Scoped(s) = &cur else { break };
1883 let Some(last) = s.parts.last() else { break };
1884 let Some(aliased) = TYPEDEF_MAP.with(|r| r.borrow().get(&last.text).cloned()) else {
1885 break;
1886 };
1887 cur = aliased;
1888 }
1889 cur
1890}
1891
1892fn normalize_member(m: &Member) -> Member {
1896 let mut m2 = m.clone();
1897 m2.type_spec = resolve_typedef_spec(&m.type_spec);
1898 m2
1899}
1900
1901fn scoped_union(s: &ScopedName) -> Option<UnionDef> {
1905 let last = s.parts.last()?.text.clone();
1906 UNION_DEFS.with(|r| r.borrow().get(&last).cloned())
1907}
1908
1909fn scoped_bitholder(s: &ScopedName) -> Option<u32> {
1915 let last = s.parts.last()?.text.clone();
1916 BITHOLDER_BYTES.with(|r| r.borrow().get(&last).copied())
1917}
1918
1919fn scoped_enum_bytes(s: &ScopedName) -> u32 {
1922 let Some(last) = s.parts.last().map(|p| p.text.clone()) else {
1923 return 4;
1924 };
1925 ENUM_BYTES
1926 .with(|r| r.borrow().get(&last).copied())
1927 .unwrap_or(4)
1928}
1929
1930fn enum_wire_ctype(bytes: u32) -> &'static str {
1932 match bytes {
1933 1 => "int8_t",
1934 2 => "int16_t",
1935 _ => "int32_t",
1936 }
1937}
1938
1939fn scoped_is_bitset(s: &ScopedName) -> bool {
1942 let Some(last) = s.parts.last().map(|p| p.text.clone()) else {
1943 return false;
1944 };
1945 BITSET_NAMES.with(|r| r.borrow().contains(&last))
1946}
1947
1948fn scoped_is_enum(s: &ScopedName) -> bool {
1950 let Some(last) = s.parts.last().map(|p| p.text.clone()) else {
1951 return false;
1952 };
1953 let is_enum = ENUM_NAMES.with(|r| r.borrow().contains(&last));
1954 let is_struct = STRUCT_NAMES.with(|r| r.borrow().contains(&last));
1955 is_enum && !is_struct
1956}
1957
1958fn scoped_final_struct(s: &ScopedName) -> Option<StructDef> {
1965 match scoped_struct(s) {
1966 Some((def, Extensibility::Final)) => Some(def),
1967 _ => None,
1968 }
1969}
1970
1971fn scoped_struct(s: &ScopedName) -> Option<(StructDef, Extensibility)> {
1984 let last = s.parts.last()?.text.clone();
1985 let def = STRUCT_DEFS.with(|r| r.borrow().get(&last).cloned())?;
1986 let ext = effective_extensibility(&last, &def.annotations);
1987 if VISITING.with(|v| v.borrow().contains(&last)) {
1997 return Some((def, ext));
1998 }
1999 VISITING.with(|v| {
2000 v.borrow_mut().insert(last.clone());
2001 });
2002 let all_encodable = def.members.iter().all(|m| {
2003 m.declarators.len() == 1
2004 && matches!(m.declarators.first(), Some(Declarator::Simple(_)))
2005 && typespec_supported(&m.type_spec)
2006 });
2007 VISITING.with(|v| {
2008 v.borrow_mut().remove(&last);
2009 });
2010 if all_encodable {
2011 Some((def, ext))
2012 } else {
2013 None
2014 }
2015}
2016
2017fn effective_extensibility(name: &str, anns: &[Annotation]) -> Extensibility {
2027 let declared = struct_extensibility(anns);
2028 if matches!(declared, Extensibility::Final) && struct_is_recursive(name) {
2029 Extensibility::Appendable
2030 } else {
2031 declared
2032 }
2033}
2034
2035fn struct_is_recursive(root: &str) -> bool {
2040 let Some(def) = STRUCT_DEFS.with(|r| r.borrow().get(root).cloned()) else {
2041 return false;
2042 };
2043 let mut visited = BTreeSet::new();
2044 visited.insert(root.to_string());
2045 def.members
2046 .iter()
2047 .any(|m| type_reaches(root, &m.type_spec, &mut visited))
2048}
2049
2050fn type_reaches(target: &str, ts: &TypeSpec, visited: &mut BTreeSet<String>) -> bool {
2052 match resolve_typedef_spec(ts) {
2053 TypeSpec::Scoped(s) => {
2054 let Some(name) = s.parts.last().map(|p| p.text.clone()) else {
2055 return false;
2056 };
2057 if name == target {
2058 return true;
2059 }
2060 if !visited.insert(name.clone()) {
2061 return false;
2062 }
2063 let Some(def) = STRUCT_DEFS.with(|r| r.borrow().get(&name).cloned()) else {
2064 return false;
2065 };
2066 def.members
2067 .iter()
2068 .any(|m| type_reaches(target, &m.type_spec, visited))
2069 }
2070 TypeSpec::Sequence(seq) => type_reaches(target, &seq.elem, visited),
2071 TypeSpec::Map(m) => {
2072 type_reaches(target, &m.key, visited) || type_reaches(target, &m.value, visited)
2073 }
2074 _ => false,
2075 }
2076}
2077
2078fn emit_topic_type_support_for(
2079 out: &mut String,
2080 cpp_fqn: &str,
2081 type_name: &str,
2082 s: &StructDef,
2083 phase: TtsPhase,
2084) -> Result<(), CppGenError> {
2085 let ext = effective_extensibility(&s.name.text, &s.annotations);
2089 let is_keyed = s.members.iter().any(|m| has_key_annotation(&m.annotations));
2090
2091 if phase == TtsPhase::Decl {
2092 writeln!(out, "template <>").map_err(fmt_err)?;
2093 writeln!(out, "struct topic_type_support<{cpp_fqn}> {{").map_err(fmt_err)?;
2094 writeln!(
2095 out,
2096 " static const char* type_name() {{ return \"{type_name}\"; }}"
2097 )
2098 .map_err(fmt_err)?;
2099 writeln!(
2100 out,
2101 " static constexpr bool is_keyed() {{ return {}; }}",
2102 if is_keyed { "true" } else { "false" }
2103 )
2104 .map_err(fmt_err)?;
2105 let ext_lit = match ext {
2106 Extensibility::Final => "FINAL",
2107 Extensibility::Appendable => "APPENDABLE",
2108 Extensibility::Mutable => "MUTABLE",
2109 };
2110 writeln!(
2111 out,
2112 " static constexpr ::dds::topic::core::policy::DataRepresentationKind extensibility() {{ return ::dds::topic::core::policy::DataRepresentationKind::{ext_lit}; }}"
2113 )
2114 .map_err(fmt_err)?;
2115 emit_encode_fn(out, cpp_fqn, s, ext, false, TtsPhase::Decl)?;
2117 emit_encode_fn(out, cpp_fqn, s, ext, true, TtsPhase::Decl)?;
2118 emit_decode_fn(out, cpp_fqn, s, ext, TtsPhase::Decl)?;
2119 emit_key_hash_fn(out, cpp_fqn, s, is_keyed, TtsPhase::Decl)?;
2120 writeln!(out, "}};").map_err(fmt_err)?;
2121 writeln!(out).map_err(fmt_err)?;
2122 return Ok(());
2123 }
2124
2125 emit_encode_fn(out, cpp_fqn, s, ext, false, TtsPhase::Def)?;
2128 emit_encode_fn(out, cpp_fqn, s, ext, true, TtsPhase::Def)?;
2130 emit_decode_fn(out, cpp_fqn, s, ext, TtsPhase::Def)?;
2132 emit_key_hash_fn(out, cpp_fqn, s, is_keyed, TtsPhase::Def)?;
2134 writeln!(out).map_err(fmt_err)?;
2135 Ok(())
2136}
2137
2138fn switch_type_spec(s: &SwitchTypeSpec) -> TypeSpec {
2141 match s {
2142 SwitchTypeSpec::Integer(i) => TypeSpec::Primitive(PrimitiveType::Integer(*i)),
2143 SwitchTypeSpec::Char => TypeSpec::Primitive(PrimitiveType::Char),
2144 SwitchTypeSpec::Boolean => TypeSpec::Primitive(PrimitiveType::Boolean),
2145 SwitchTypeSpec::Octet => TypeSpec::Primitive(PrimitiveType::Octet),
2146 SwitchTypeSpec::Scoped(sc) => TypeSpec::Scoped(sc.clone()),
2147 }
2148}
2149
2150fn emit_union_topic_type_support(
2155 out: &mut String,
2156 cpp_fqn: &str,
2157 type_name: &str,
2158 u: &UnionDef,
2159 phase: TtsPhase,
2160) -> Result<(), CppGenError> {
2161 let disc_ts = switch_type_spec(&u.switch_type);
2162 let disc_cpp = switch_type_to_cpp(&u.switch_type)?;
2163 let ext = union_extensibility(&u.annotations);
2169 let has_dheader = !matches!(ext, Extensibility::Final);
2170 let kind = match ext {
2171 Extensibility::Final => "FINAL",
2172 Extensibility::Mutable => "MUTABLE",
2173 Extensibility::Appendable => "APPENDABLE",
2174 };
2175
2176 if phase == TtsPhase::Decl {
2177 writeln!(out, "template <>").map_err(fmt_err)?;
2178 writeln!(out, "struct topic_type_support<{cpp_fqn}> {{").map_err(fmt_err)?;
2179 writeln!(
2180 out,
2181 " static const char* type_name() {{ return \"{type_name}\"; }}"
2182 )
2183 .map_err(fmt_err)?;
2184 writeln!(
2185 out,
2186 " static constexpr bool is_keyed() {{ return false; }}"
2187 )
2188 .map_err(fmt_err)?;
2189 writeln!(
2190 out,
2191 " static constexpr ::dds::topic::core::policy::DataRepresentationKind extensibility() {{ return ::dds::topic::core::policy::DataRepresentationKind::{kind}; }}"
2192 )
2193 .map_err(fmt_err)?;
2194 writeln!(
2195 out,
2196 " static std::vector<uint8_t> encode(const {cpp_fqn}& zd_v);"
2197 )
2198 .map_err(fmt_err)?;
2199 writeln!(
2200 out,
2201 " static std::vector<uint8_t> encode(const {cpp_fqn}& zd_v, ::dds::topic::xcdr2::XcdrVersion zd_repr);"
2202 )
2203 .map_err(fmt_err)?;
2204 writeln!(
2205 out,
2206 " static std::vector<uint8_t> encode_be(const {cpp_fqn}& zd_v);"
2207 )
2208 .map_err(fmt_err)?;
2209 writeln!(
2210 out,
2211 " static std::vector<uint8_t> encode_be(const {cpp_fqn}& zd_v, ::dds::topic::xcdr2::XcdrVersion zd_repr);"
2212 )
2213 .map_err(fmt_err)?;
2214 writeln!(
2215 out,
2216 " static {cpp_fqn} decode(const uint8_t* zd_buf, size_t zd_len, ::dds::topic::xcdr2::XcdrVersion zd_repr, bool zd_be = false);"
2217 )
2218 .map_err(fmt_err)?;
2219 writeln!(
2220 out,
2221 " static std::array<uint8_t, 16> key_hash(const {cpp_fqn}& zd_v);"
2222 )
2223 .map_err(fmt_err)?;
2224 writeln!(out, "}};").map_err(fmt_err)?;
2225 writeln!(out).map_err(fmt_err)?;
2226 return Ok(());
2227 }
2228
2229 for be in [false, true] {
2231 let endian = if be { "be" } else { "le" };
2232 let beb = if be { "true" } else { "false" };
2233 if be {
2234 writeln!(
2235 out,
2236 "inline std::vector<uint8_t> topic_type_support<{cpp_fqn}>::encode_be(const {cpp_fqn}& zd_v) {{"
2237 )
2238 .map_err(fmt_err)?;
2239 writeln!(
2240 out,
2241 " return encode_be(zd_v, ::dds::topic::xcdr2::XcdrVersion::Xcdr2);"
2242 )
2243 .map_err(fmt_err)?;
2244 writeln!(out, " }}").map_err(fmt_err)?;
2245 writeln!(
2246 out,
2247 "inline std::vector<uint8_t> topic_type_support<{cpp_fqn}>::encode_be(const {cpp_fqn}& zd_v, ::dds::topic::xcdr2::XcdrVersion zd_repr) {{"
2248 )
2249 .map_err(fmt_err)?;
2250 } else {
2251 writeln!(
2252 out,
2253 "inline std::vector<uint8_t> topic_type_support<{cpp_fqn}>::encode(const {cpp_fqn}& zd_v) {{"
2254 )
2255 .map_err(fmt_err)?;
2256 writeln!(
2257 out,
2258 " return encode(zd_v, ::dds::topic::xcdr2::XcdrVersion::Xcdr2);"
2259 )
2260 .map_err(fmt_err)?;
2261 writeln!(out, " }}").map_err(fmt_err)?;
2262 writeln!(
2263 out,
2264 "inline std::vector<uint8_t> topic_type_support<{cpp_fqn}>::encode(const {cpp_fqn}& zd_v, ::dds::topic::xcdr2::XcdrVersion zd_repr) {{"
2265 )
2266 .map_err(fmt_err)?;
2267 }
2268 writeln!(out, " std::vector<uint8_t> zd_out;").map_err(fmt_err)?;
2269 writeln!(out, " (void)zd_v;").map_err(fmt_err)?;
2270 writeln!(
2273 out,
2274 " const size_t zd_max_align = ::dds::topic::xcdr2::xcdr_max_align(zd_repr);"
2275 )
2276 .map_err(fmt_err)?;
2277 writeln!(out, " (void)zd_max_align;").map_err(fmt_err)?;
2278 if has_dheader {
2281 writeln!(
2282 out,
2283 " size_t zd_dh = ::dds::topic::xcdr2::DHEADER_NONE; (void)zd_dh;"
2284 )
2285 .map_err(fmt_err)?;
2286 writeln!(
2287 out,
2288 " if (zd_repr != ::dds::topic::xcdr2::XcdrVersion::Xcdr1) {{ zd_dh = ::dds::topic::xcdr2::dheader_begin(zd_out); }}"
2289 )
2290 .map_err(fmt_err)?;
2291 }
2292 writeln!(out, " const size_t zd_origin = zd_out.size();").map_err(fmt_err)?;
2293 writeln!(out, " (void)zd_origin;").map_err(fmt_err)?;
2294 emit_value_write(out, &disc_ts, "zd_v._d()", endian, "zd_origin", " ")?;
2296 emit_union_branch_switch(out, u, &disc_cpp, false, endian)?;
2298 if has_dheader {
2299 writeln!(
2300 out,
2301 " ::dds::topic::xcdr2::dheader_end_r(zd_out, zd_dh, {beb}, zd_repr);"
2302 )
2303 .map_err(fmt_err)?;
2304 }
2305 writeln!(out, " return zd_out;").map_err(fmt_err)?;
2306 writeln!(out, " }}").map_err(fmt_err)?;
2307 }
2308
2309 writeln!(
2311 out,
2312 "inline {cpp_fqn} topic_type_support<{cpp_fqn}>::decode(const uint8_t* zd_buf, size_t zd_len, ::dds::topic::xcdr2::XcdrVersion zd_repr, bool zd_be) {{"
2313 )
2314 .map_err(fmt_err)?;
2315 writeln!(out, " size_t zd_pos = 0;").map_err(fmt_err)?;
2316 writeln!(out, " {cpp_fqn} zd_v;").map_err(fmt_err)?;
2317 writeln!(
2318 out,
2319 " const size_t zd_max_align = ::dds::topic::xcdr2::xcdr_max_align(zd_repr);"
2320 )
2321 .map_err(fmt_err)?;
2322 writeln!(
2323 out,
2324 " (void)zd_buf; (void)zd_len; (void)zd_pos; (void)zd_max_align; (void)zd_be;"
2325 )
2326 .map_err(fmt_err)?;
2327 if has_dheader {
2328 writeln!(out, " size_t zd_end = zd_len; (void)zd_end;").map_err(fmt_err)?;
2330 writeln!(
2331 out,
2332 " if (zd_repr != ::dds::topic::xcdr2::XcdrVersion::Xcdr1) {{ const auto zd_dh = ::dds::topic::xcdr2::dheader_read(zd_buf, zd_pos, zd_len, zd_be); zd_end = zd_pos + zd_dh; }}"
2333 )
2334 .map_err(fmt_err)?;
2335 writeln!(out, " const size_t zd_origin = zd_pos;").map_err(fmt_err)?;
2336 } else {
2337 writeln!(out, " const size_t zd_origin = zd_pos;").map_err(fmt_err)?;
2339 }
2340 writeln!(out, " {disc_cpp} zd_disc{{}};").map_err(fmt_err)?;
2341 emit_value_read(out, &disc_ts, "zd_disc =", "zd_origin", " ", false)?;
2342 writeln!(out, " zd_v._d(zd_disc);").map_err(fmt_err)?;
2343 emit_union_branch_switch(out, u, &disc_cpp, true, "le")?;
2344 if has_dheader {
2345 writeln!(
2346 out,
2347 " if (zd_repr != ::dds::topic::xcdr2::XcdrVersion::Xcdr1 && zd_pos < zd_end) zd_pos = zd_end;"
2348 )
2349 .map_err(fmt_err)?;
2350 }
2351 writeln!(out, " return zd_v;").map_err(fmt_err)?;
2352 writeln!(out, " }}").map_err(fmt_err)?;
2353
2354 writeln!(
2356 out,
2357 "inline std::array<uint8_t, 16> topic_type_support<{cpp_fqn}>::key_hash(const {cpp_fqn}& zd_v) {{"
2358 )
2359 .map_err(fmt_err)?;
2360 writeln!(out, " (void)zd_v;").map_err(fmt_err)?;
2361 writeln!(
2362 out,
2363 " return std::array<uint8_t, 16>{{{{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}}};"
2364 )
2365 .map_err(fmt_err)?;
2366 writeln!(out, " }}").map_err(fmt_err)?;
2367 writeln!(out).map_err(fmt_err)?;
2368 Ok(())
2369}
2370
2371fn render_case_label(expr: &ConstExpr, enum_switch: bool, disc_cpp: &str) -> String {
2376 if enum_switch {
2377 if let ConstExpr::Scoped(s) = expr {
2378 if s.parts.len() == 1 {
2379 return format!("{disc_cpp}::{}", s.parts[0].text);
2380 }
2381 }
2382 }
2383 const_expr_to_cpp(expr)
2384}
2385
2386fn emit_union_branch_switch(
2390 out: &mut String,
2391 u: &UnionDef,
2392 disc_cpp: &str,
2393 decode: bool,
2394 endian: &str,
2395) -> Result<(), CppGenError> {
2396 emit_union_branch_switch_at(out, u, disc_cpp, decode, endian, "zd_v", "zd_origin")
2397}
2398
2399fn emit_union_branch_switch_at(
2406 out: &mut String,
2407 u: &UnionDef,
2408 disc_cpp: &str,
2409 decode: bool,
2410 endian: &str,
2411 access: &str,
2412 origin: &str,
2413) -> Result<(), CppGenError> {
2414 let enum_switch = matches!(&u.switch_type, SwitchTypeSpec::Scoped(s) if scoped_is_enum(s));
2417 writeln!(
2418 out,
2419 " switch (static_cast<{disc_cpp}>({access}._d())) {{"
2420 )
2421 .map_err(fmt_err)?;
2422 let mut default_case: Option<&Case> = None;
2423 for c in &u.cases {
2424 let mut is_default = false;
2425 let mut had_label = false;
2426 for label in &c.labels {
2427 match label {
2428 CaseLabel::Default => is_default = true,
2429 CaseLabel::Value(expr) => {
2430 let val = render_case_label(expr, enum_switch, disc_cpp);
2431 writeln!(out, " case static_cast<{disc_cpp}>({val}):")
2432 .map_err(fmt_err)?;
2433 had_label = true;
2434 }
2435 }
2436 }
2437 if is_default {
2438 default_case = Some(c);
2439 continue;
2440 }
2441 if !had_label {
2442 continue;
2443 }
2444 writeln!(out, " {{").map_err(fmt_err)?;
2445 emit_union_branch_body(out, c, decode, endian, access, origin)?;
2446 writeln!(out, " break;").map_err(fmt_err)?;
2447 writeln!(out, " }}").map_err(fmt_err)?;
2448 }
2449 if let Some(c) = default_case {
2450 writeln!(out, " default: {{").map_err(fmt_err)?;
2451 emit_union_branch_body(out, c, decode, endian, access, origin)?;
2452 writeln!(out, " break;").map_err(fmt_err)?;
2453 writeln!(out, " }}").map_err(fmt_err)?;
2454 } else {
2455 writeln!(out, " default: break;").map_err(fmt_err)?;
2456 }
2457 writeln!(out, " }}").map_err(fmt_err)?;
2458 Ok(())
2459}
2460
2461fn emit_union_branch_body(
2466 out: &mut String,
2467 c: &Case,
2468 decode: bool,
2469 endian: &str,
2470 access: &str,
2471 origin: &str,
2472) -> Result<(), CppGenError> {
2473 let cpp_ty = type_for_declarator(&c.element.type_spec, &c.element.declarator)?;
2474 let ts = &c.element.type_spec;
2475 if decode {
2476 writeln!(out, " {cpp_ty} zd_bv{{}};").map_err(fmt_err)?;
2477 emit_value_read(out, ts, "zd_bv =", origin, " ", false)?;
2478 writeln!(out, " {access}.value() = zd_bv;").map_err(fmt_err)?;
2479 } else {
2480 writeln!(
2481 out,
2482 " const {cpp_ty}& zd_bv = std::get<{cpp_ty}>({access}.value());"
2483 )
2484 .map_err(fmt_err)?;
2485 emit_value_write(out, ts, "zd_bv", endian, origin, " ")?;
2486 }
2487 Ok(())
2488}
2489
2490fn emit_encode_fn(
2491 out: &mut String,
2492 cpp_fqn: &str,
2493 s: &StructDef,
2494 ext: Extensibility,
2495 be: bool,
2496 phase: TtsPhase,
2497) -> Result<(), CppGenError> {
2498 let endian_suffix = if be { "be" } else { "le" };
2500 let beb = if be { "true" } else { "false" };
2501
2502 if phase == TtsPhase::Decl {
2504 if be {
2505 writeln!(
2506 out,
2507 " static std::vector<uint8_t> encode_be(const {cpp_fqn}& zd_v);"
2508 )
2509 .map_err(fmt_err)?;
2510 writeln!(
2511 out,
2512 " static std::vector<uint8_t> encode_be(const {cpp_fqn}& zd_v, \
2513 ::dds::topic::xcdr2::XcdrVersion zd_repr);"
2514 )
2515 .map_err(fmt_err)?;
2516 } else {
2517 writeln!(
2518 out,
2519 " static std::vector<uint8_t> encode(const {cpp_fqn}& zd_v);"
2520 )
2521 .map_err(fmt_err)?;
2522 writeln!(
2523 out,
2524 " static std::vector<uint8_t> encode(const {cpp_fqn}& zd_v, \
2525 ::dds::topic::xcdr2::XcdrVersion zd_repr);"
2526 )
2527 .map_err(fmt_err)?;
2528 }
2529 return Ok(());
2530 }
2531
2532 if be {
2534 writeln!(
2537 out,
2538 "inline std::vector<uint8_t> topic_type_support<{cpp_fqn}>::encode_be(const {cpp_fqn}& zd_v) {{"
2539 )
2540 .map_err(fmt_err)?;
2541 writeln!(
2542 out,
2543 " return encode_be(zd_v, ::dds::topic::xcdr2::XcdrVersion::Xcdr2);"
2544 )
2545 .map_err(fmt_err)?;
2546 writeln!(out, " }}").map_err(fmt_err)?;
2547 writeln!(
2548 out,
2549 "inline std::vector<uint8_t> topic_type_support<{cpp_fqn}>::encode_be(const {cpp_fqn}& zd_v, \
2550 ::dds::topic::xcdr2::XcdrVersion zd_repr) {{"
2551 )
2552 .map_err(fmt_err)?;
2553 } else {
2554 writeln!(
2561 out,
2562 "inline std::vector<uint8_t> topic_type_support<{cpp_fqn}>::encode(const {cpp_fqn}& zd_v) {{"
2563 )
2564 .map_err(fmt_err)?;
2565 writeln!(
2566 out,
2567 " return encode(zd_v, ::dds::topic::xcdr2::XcdrVersion::Xcdr2);"
2568 )
2569 .map_err(fmt_err)?;
2570 writeln!(out, " }}").map_err(fmt_err)?;
2571 writeln!(
2572 out,
2573 "inline std::vector<uint8_t> topic_type_support<{cpp_fqn}>::encode(const {cpp_fqn}& zd_v, \
2574 ::dds::topic::xcdr2::XcdrVersion zd_repr) {{"
2575 )
2576 .map_err(fmt_err)?;
2577 }
2578 writeln!(out, " std::vector<uint8_t> zd_out;").map_err(fmt_err)?;
2579 writeln!(out, " (void)zd_v;").map_err(fmt_err)?;
2580 writeln!(
2584 out,
2585 " const size_t zd_max_align = ::dds::topic::xcdr2::xcdr_max_align(zd_repr);"
2586 )
2587 .map_err(fmt_err)?;
2588 writeln!(out, " (void)zd_max_align;").map_err(fmt_err)?;
2589
2590 match ext {
2591 Extensibility::Final => {
2592 writeln!(out, " const size_t zd_origin = 0;").map_err(fmt_err)?;
2595 writeln!(out, " (void)zd_origin;").map_err(fmt_err)?;
2596 for m in &s.members {
2597 emit_plain_member_encode(out, m, endian_suffix, "zd_origin")?;
2598 }
2599 }
2600 Extensibility::Appendable => {
2601 writeln!(
2607 out,
2608 " const bool zd_x1 = (zd_repr == ::dds::topic::xcdr2::XcdrVersion::Xcdr1);"
2609 )
2610 .map_err(fmt_err)?;
2611 writeln!(out, " size_t zd_dh = 0; (void)zd_dh;").map_err(fmt_err)?;
2612 writeln!(
2613 out,
2614 " if (!zd_x1) {{ zd_dh = ::dds::topic::xcdr2::dheader_begin(zd_out); }}"
2615 )
2616 .map_err(fmt_err)?;
2617 writeln!(out, " const size_t zd_origin = zd_out.size();").map_err(fmt_err)?;
2618 writeln!(out, " (void)zd_origin;").map_err(fmt_err)?;
2619 for m in &s.members {
2620 emit_plain_member_encode(out, m, endian_suffix, "zd_origin")?;
2621 }
2622 writeln!(
2623 out,
2624 " if (!zd_x1) {{ ::dds::topic::xcdr2::dheader_end(zd_out, zd_dh, {beb}); }}"
2625 )
2626 .map_err(fmt_err)?;
2627 }
2628 Extensibility::Mutable => {
2629 writeln!(
2634 out,
2635 " if (zd_repr == ::dds::topic::xcdr2::XcdrVersion::Xcdr1) {{"
2636 )
2637 .map_err(fmt_err)?;
2638 let mut zd_pl_id = 0u32;
2639 for m in &s.members {
2640 emit_pl_cdr1_member_encode(out, m, &mut zd_pl_id, endian_suffix)?;
2641 }
2642 writeln!(
2643 out,
2644 " ::dds::topic::xcdr2::pl_cdr1_write_sentinel(zd_out, {beb});"
2645 )
2646 .map_err(fmt_err)?;
2647 writeln!(out, " }} else {{").map_err(fmt_err)?;
2648 writeln!(
2649 out,
2650 " const auto zd_scope = ::dds::topic::xcdr2::mutable_begin(zd_out);"
2651 )
2652 .map_err(fmt_err)?;
2653 writeln!(out, " const size_t zd_origin = zd_scope.origin;").map_err(fmt_err)?;
2654 writeln!(out, " (void)zd_origin;").map_err(fmt_err)?;
2655 let mut zd_next_id = 0u32;
2656 for m in &s.members {
2657 emit_mutable_member_encode(out, m, endian_suffix, &mut zd_next_id)?;
2658 }
2659 writeln!(
2660 out,
2661 " ::dds::topic::xcdr2::mutable_end(zd_out, zd_scope, {beb});"
2662 )
2663 .map_err(fmt_err)?;
2664 writeln!(out, " }}").map_err(fmt_err)?;
2665 }
2666 }
2667
2668 writeln!(out, " return zd_out;").map_err(fmt_err)?;
2669 writeln!(out, " }}").map_err(fmt_err)?;
2670 Ok(())
2671}
2672
2673fn emit_plain_member_encode(
2676 out: &mut String,
2677 m: &Member,
2678 endian: &str,
2679 origin: &str,
2680) -> Result<(), CppGenError> {
2681 let m = &normalize_member(m);
2682 let beb = if endian == "be" { "true" } else { "false" };
2683 if !member_codegen_supported(m) {
2684 for decl in &m.declarators {
2685 let name = &decl.name().text;
2686 writeln!(
2687 out,
2688 " // xcdr2: @shared member '{name}' not supported (skip)"
2689 )
2690 .map_err(fmt_err)?;
2691 }
2692 return Ok(());
2693 }
2694 let is_optional = has_optional_annotation(&m.annotations);
2695 for decl in &m.declarators {
2696 let name = &decl.name().text;
2697 if let Declarator::Array(arr) = decl {
2702 let prim = matches!(m.type_spec, TypeSpec::Primitive(_));
2703 let leaf_1d = arr.sizes.len() == 1
2704 && matches!(m.type_spec, TypeSpec::Primitive(_) | TypeSpec::String(_));
2705 if leaf_1d {
2706 writeln!(out, " for (const auto& zd_ae : zd_v.{name}()) {{")
2707 .map_err(fmt_err)?;
2708 emit_value_write(out, &m.type_spec, "zd_ae", endian, origin, " ")?;
2709 writeln!(out, " }}").map_err(fmt_err)?;
2710 } else if prim && arr.sizes.len() >= 2 {
2711 let n = arr.sizes.len();
2719 let mut acc = format!("zd_v.{name}()");
2720 let mut ind = String::from(" ");
2721 for d in 0..n {
2722 let lv = format!("zd_a{d}");
2723 writeln!(out, "{ind}for (const auto& {lv} : {acc}) {{").map_err(fmt_err)?;
2724 acc = lv;
2725 ind.push_str(" ");
2726 }
2727 emit_value_write(out, &m.type_spec, &acc, endian, origin, &ind)?;
2728 for _ in 0..n {
2729 ind.truncate(ind.len() - 4);
2730 writeln!(out, "{ind}}}").map_err(fmt_err)?;
2731 }
2732 } else if matches!(&m.type_spec, TypeSpec::Scoped(s) if scoped_is_enum(s) || scoped_struct(s).is_some() || scoped_union(s).is_some())
2733 || (matches!(m.type_spec, TypeSpec::String(_)) && arr.sizes.len() >= 2)
2734 {
2735 let n = arr.sizes.len();
2743 writeln!(out, " {{").map_err(fmt_err)?;
2744 writeln!(
2745 out,
2746 " const auto zd_arr_dh = ::dds::topic::xcdr2::dheader_begin_r(zd_out, zd_repr);"
2747 )
2748 .map_err(fmt_err)?;
2749 let mut acc = format!("zd_v.{name}()");
2750 let mut ind = String::from(" ");
2751 for d in 0..n {
2752 let lv = format!("zd_a{d}");
2753 writeln!(out, "{ind}for (const auto& {lv} : {acc}) {{").map_err(fmt_err)?;
2754 acc = lv;
2755 ind.push_str(" ");
2756 }
2757 emit_value_write(out, &m.type_spec, &acc, endian, origin, &ind)?;
2758 for _ in 0..n {
2759 ind.truncate(ind.len() - 4);
2760 writeln!(out, "{ind}}}").map_err(fmt_err)?;
2761 }
2762 writeln!(
2763 out,
2764 " ::dds::topic::xcdr2::dheader_end_r(zd_out, zd_arr_dh, {beb}, zd_repr);"
2765 )
2766 .map_err(fmt_err)?;
2767 writeln!(out, " }}").map_err(fmt_err)?;
2768 } else {
2769 writeln!(
2770 out,
2771 " // xcdr2: array member '{name}' (multi-dim string-1D-only / unsupported elem) not supported (skip)"
2772 )
2773 .map_err(fmt_err)?;
2774 }
2775 continue;
2776 }
2777 if !typespec_supported(&m.type_spec) {
2778 writeln!(
2779 out,
2780 " // xcdr2: member '{name}' not supported (nested/enum/map; skip)"
2781 )
2782 .map_err(fmt_err)?;
2783 continue;
2784 }
2785 if is_optional {
2786 writeln!(out, " if (zd_v.{name}().has_value()) {{").map_err(fmt_err)?;
2788 writeln!(out, " zd_out.push_back(uint8_t{{1}});").map_err(fmt_err)?;
2789 emit_value_write(
2790 out,
2791 &m.type_spec,
2792 &format!("(*zd_v.{name}())"),
2793 endian,
2794 origin,
2795 " ",
2796 )?;
2797 writeln!(out, " }} else {{").map_err(fmt_err)?;
2798 writeln!(out, " zd_out.push_back(uint8_t{{0}});").map_err(fmt_err)?;
2799 writeln!(out, " }}").map_err(fmt_err)?;
2800 } else {
2801 emit_value_write(
2802 out,
2803 &m.type_spec,
2804 &format!("zd_v.{name}()"),
2805 endian,
2806 origin,
2807 " ",
2808 )?;
2809 }
2810 }
2811 Ok(())
2812}
2813
2814fn emit_value_write(
2818 out: &mut String,
2819 ts: &TypeSpec,
2820 access: &str,
2821 endian: &str,
2822 origin: &str,
2823 indent: &str,
2824) -> Result<(), CppGenError> {
2825 let pre = format!("{indent} ");
2826 let beb = if endian == "be" { "true" } else { "false" };
2827 match ts {
2828 TypeSpec::Primitive(PrimitiveType::Boolean) => {
2829 writeln!(
2830 out,
2831 "{pre}::dds::topic::xcdr2::write_bool(zd_out, {access});"
2832 )
2833 .map_err(fmt_err)?;
2834 }
2835 TypeSpec::Primitive(PrimitiveType::Octet) => {
2836 writeln!(out, "{pre}::dds::topic::xcdr2::write_u8(zd_out, {access});")
2837 .map_err(fmt_err)?;
2838 }
2839 TypeSpec::Primitive(p) => {
2840 let cpp_ty = primitive_to_cpp(*p);
2841 if endian == "be" {
2842 writeln!(
2844 out,
2845 "{pre}::dds::topic::xcdr2::write_be_origin<{cpp_ty}>(zd_out, {origin}, {access}, zd_max_align);"
2846 )
2847 .map_err(fmt_err)?;
2848 } else {
2849 writeln!(
2851 out,
2852 "{pre}::dds::topic::xcdr2::write_le_origin<{cpp_ty}>(zd_out, {origin}, {access}, zd_max_align);"
2853 )
2854 .map_err(fmt_err)?;
2855 }
2856 }
2857 TypeSpec::String(s) if !s.wide => {
2858 if let Some(b) = &s.bound {
2861 let bv = const_expr_to_cpp(b);
2862 writeln!(
2863 out,
2864 "{pre}if ({access}.size() > {bv}) throw std::length_error(\"bounded string length exceeds its IDL bound ({bv})\");"
2865 )
2866 .map_err(fmt_err)?;
2867 }
2868 if endian == "be" {
2869 writeln!(
2870 out,
2871 "{pre}::dds::topic::xcdr2::write_string_be(zd_out, {access});"
2872 )
2873 .map_err(fmt_err)?;
2874 } else {
2875 writeln!(
2876 out,
2877 "{pre}::dds::topic::xcdr2::write_string_origin(zd_out, {origin}, {access}, zd_max_align);"
2878 )
2879 .map_err(fmt_err)?;
2880 }
2881 }
2882 TypeSpec::String(s) if s.wide => {
2883 if let Some(b) = &s.bound {
2886 let bv = const_expr_to_cpp(b);
2887 writeln!(
2888 out,
2889 "{pre}if ({access}.size() > {bv}) throw std::length_error(\"bounded wstring length exceeds its IDL bound ({bv})\");"
2890 )
2891 .map_err(fmt_err)?;
2892 }
2893 if endian == "be" {
2894 writeln!(
2895 out,
2896 "{pre}::dds::topic::xcdr2::write_wstring_be(zd_out, {access});"
2897 )
2898 .map_err(fmt_err)?;
2899 } else {
2900 writeln!(
2901 out,
2902 "{pre}::dds::topic::xcdr2::write_wstring_origin(zd_out, {origin}, {access}, zd_max_align);"
2903 )
2904 .map_err(fmt_err)?;
2905 }
2906 }
2907 TypeSpec::Sequence(seq) => {
2908 if let Some(b) = &seq.bound {
2912 let bv = const_expr_to_cpp(b);
2913 writeln!(
2914 out,
2915 "{pre}if ({access}.size() > {bv}) throw std::length_error(\"bounded sequence length exceeds its IDL bound ({bv})\");"
2916 )
2917 .map_err(fmt_err)?;
2918 }
2919 if matches!(&*seq.elem, TypeSpec::Primitive(PrimitiveType::Octet)) {
2920 if endian == "be" {
2922 writeln!(out, "{pre}::dds::topic::xcdr2::write_be<uint32_t>(zd_out, static_cast<uint32_t>({access}.size()));").map_err(fmt_err)?;
2923 } else {
2924 writeln!(out, "{pre}::dds::topic::xcdr2::write_le_origin<uint32_t>(zd_out, {origin}, static_cast<uint32_t>({access}.size()), zd_max_align);").map_err(fmt_err)?;
2925 }
2926 writeln!(
2927 out,
2928 "{pre}zd_out.insert(zd_out.end(), {access}.begin(), {access}.end());"
2929 )
2930 .map_err(fmt_err)?;
2931 return Ok(());
2932 }
2933 let seq_non_primitive = !matches!(&*seq.elem, TypeSpec::Primitive(_));
2938 if seq_non_primitive {
2939 writeln!(out, "{pre}{{").map_err(fmt_err)?;
2940 writeln!(
2944 out,
2945 "{pre}::dds::topic::xcdr2::pad_to_from_origin(zd_out, {origin}, 4);"
2946 )
2947 .map_err(fmt_err)?;
2948 writeln!(
2949 out,
2950 "{pre}const auto zd_seq_dh = ::dds::topic::xcdr2::dheader_begin_r(zd_out, zd_repr);"
2951 )
2952 .map_err(fmt_err)?;
2953 }
2954 let count_call = if endian == "be" {
2955 format!(
2956 "{pre}::dds::topic::xcdr2::write_be<uint32_t>(zd_out, static_cast<uint32_t>({access}.size()));"
2957 )
2958 } else {
2959 format!(
2960 "{pre}::dds::topic::xcdr2::write_le_origin<uint32_t>(zd_out, {origin}, static_cast<uint32_t>({access}.size()), zd_max_align);"
2961 )
2962 };
2963 writeln!(out, "{count_call}").map_err(fmt_err)?;
2964 writeln!(out, "{pre}for (const auto& zd_e : {access}) {{").map_err(fmt_err)?;
2965 let elem_indent = format!("{pre} ");
2966 match &*seq.elem {
2967 TypeSpec::Primitive(PrimitiveType::Boolean) => {
2968 writeln!(
2969 out,
2970 "{elem_indent}::dds::topic::xcdr2::write_bool(zd_out, zd_e);"
2971 )
2972 .map_err(fmt_err)?;
2973 }
2974 TypeSpec::Primitive(PrimitiveType::Octet) => {
2975 writeln!(
2976 out,
2977 "{elem_indent}::dds::topic::xcdr2::write_u8(zd_out, zd_e);"
2978 )
2979 .map_err(fmt_err)?;
2980 }
2981 TypeSpec::Primitive(p) => {
2982 let cpp_ty = primitive_to_cpp(*p);
2983 if endian == "be" {
2984 writeln!(
2985 out,
2986 "{elem_indent}::dds::topic::xcdr2::write_be<{cpp_ty}>(zd_out, zd_e);"
2987 )
2988 .map_err(fmt_err)?;
2989 } else {
2990 writeln!(
2991 out,
2992 "{elem_indent}::dds::topic::xcdr2::write_le_origin<{cpp_ty}>(zd_out, {origin}, zd_e, zd_max_align);"
2993 )
2994 .map_err(fmt_err)?;
2995 }
2996 }
2997 TypeSpec::String(s) if !s.wide => {
2998 if endian == "be" {
2999 writeln!(
3000 out,
3001 "{elem_indent}::dds::topic::xcdr2::write_string_be(zd_out, zd_e);"
3002 )
3003 .map_err(fmt_err)?;
3004 } else {
3005 writeln!(
3006 out,
3007 "{elem_indent}::dds::topic::xcdr2::write_string_origin(zd_out, {origin}, zd_e, zd_max_align);"
3008 )
3009 .map_err(fmt_err)?;
3010 }
3011 }
3012 TypeSpec::String(_) => {
3014 emit_value_write(out, &seq.elem, "zd_e", endian, origin, &elem_indent)?;
3015 }
3016 TypeSpec::Scoped(sc) if scoped_is_enum(sc) || scoped_struct(sc).is_some() => {
3021 emit_value_write(out, &seq.elem, "zd_e", endian, origin, &elem_indent)?;
3022 }
3023 TypeSpec::Scoped(sc) if scoped_union(sc).is_some() => {
3026 emit_value_write(out, &seq.elem, "zd_e", endian, origin, &elem_indent)?;
3027 }
3028 TypeSpec::Sequence(_) => {
3031 emit_value_write(out, &seq.elem, "zd_e", endian, origin, &elem_indent)?;
3032 }
3033 TypeSpec::Map(_) => {
3036 emit_value_write(out, &seq.elem, "zd_e", endian, origin, &elem_indent)?;
3037 }
3038 _ => {
3039 writeln!(
3040 out,
3041 "{elem_indent}// xcdr2: nested sequence-element not supported"
3042 )
3043 .map_err(fmt_err)?;
3044 }
3045 }
3046 writeln!(out, "{pre}}}").map_err(fmt_err)?;
3047 if seq_non_primitive {
3048 writeln!(
3049 out,
3050 "{pre}::dds::topic::xcdr2::dheader_end_r(zd_out, zd_seq_dh, {beb}, zd_repr);"
3051 )
3052 .map_err(fmt_err)?;
3053 writeln!(out, "{pre}}}").map_err(fmt_err)?;
3054 }
3055 }
3056 TypeSpec::Map(m) => {
3062 if let Some(b) = &m.bound {
3063 let bv = const_expr_to_cpp(b);
3064 writeln!(
3065 out,
3066 "{pre}if ({access}.size() > {bv}) throw std::length_error(\"bounded map length exceeds its IDL bound ({bv})\");"
3067 )
3068 .map_err(fmt_err)?;
3069 }
3070 writeln!(out, "{pre}{{").map_err(fmt_err)?;
3071 writeln!(
3077 out,
3078 "{pre}::dds::topic::xcdr2::pad_to_from_origin(zd_out, {origin}, 4);"
3079 )
3080 .map_err(fmt_err)?;
3081 let map_dh = !map_pair_is_primitive(&m.key, &m.value);
3082 if map_dh {
3083 writeln!(
3084 out,
3085 "{pre}const auto zd_map_dh = ::dds::topic::xcdr2::dheader_begin_r(zd_out, zd_repr);"
3086 )
3087 .map_err(fmt_err)?;
3088 }
3089 if endian == "be" {
3090 writeln!(out, "{pre}::dds::topic::xcdr2::write_be<uint32_t>(zd_out, static_cast<uint32_t>({access}.size()));").map_err(fmt_err)?;
3091 } else {
3092 writeln!(out, "{pre}::dds::topic::xcdr2::write_le_origin<uint32_t>(zd_out, {origin}, static_cast<uint32_t>({access}.size()), zd_max_align);").map_err(fmt_err)?;
3093 }
3094 writeln!(out, "{pre}for (const auto& zd_kv : {access}) {{").map_err(fmt_err)?;
3095 let kv_indent = format!("{pre} ");
3096 emit_value_write(out, &m.key, "zd_kv.first", endian, origin, &kv_indent)?;
3097 emit_value_write(out, &m.value, "zd_kv.second", endian, origin, &kv_indent)?;
3098 writeln!(out, "{pre}}}").map_err(fmt_err)?;
3099 if map_dh {
3100 writeln!(
3101 out,
3102 "{pre}::dds::topic::xcdr2::dheader_end_r(zd_out, zd_map_dh, {beb}, zd_repr);"
3103 )
3104 .map_err(fmt_err)?;
3105 }
3106 writeln!(out, "{pre}}}").map_err(fmt_err)?;
3107 }
3108 TypeSpec::Scoped(s) if scoped_bitholder(s).is_some() => {
3113 let bytes = scoped_bitholder(s).unwrap_or(1);
3114 let holder = holder_uint_for_bytes(bytes);
3115 let is_bitset = scoped_is_bitset(s);
3116 let raw = if is_bitset {
3117 format!("static_cast<{holder}>({access}.value)")
3118 } else {
3119 format!("static_cast<{holder}>({access})")
3120 };
3121 if endian == "be" {
3122 writeln!(
3123 out,
3124 "{pre}::dds::topic::xcdr2::write_be_origin<{holder}>(zd_out, {origin}, {raw});"
3125 )
3126 .map_err(fmt_err)?;
3127 } else {
3128 writeln!(
3129 out,
3130 "{pre}::dds::topic::xcdr2::write_le_origin<{holder}>(zd_out, {origin}, {raw}, zd_max_align);"
3131 )
3132 .map_err(fmt_err)?;
3133 }
3134 }
3135 TypeSpec::Scoped(s) if scoped_is_enum(s) => {
3137 let ec = enum_wire_ctype(scoped_enum_bytes(s));
3139 if endian == "be" {
3140 writeln!(
3141 out,
3142 "{pre}::dds::topic::xcdr2::write_be<{ec}>(zd_out, static_cast<{ec}>({access}));"
3143 )
3144 .map_err(fmt_err)?;
3145 } else {
3146 writeln!(
3147 out,
3148 "{pre}::dds::topic::xcdr2::write_le_origin<{ec}>(zd_out, {origin}, static_cast<{ec}>({access}), zd_max_align);"
3149 )
3150 .map_err(fmt_err)?;
3151 }
3152 }
3153 TypeSpec::Scoped(sc) if scoped_struct(sc).is_some() => {
3158 let Some((def, ext)) = scoped_struct(sc) else {
3159 return Ok(());
3160 };
3161 match ext {
3162 Extensibility::Final => {
3163 for sm in &def.members {
3164 let sm_name = &sm.declarators[0].name().text;
3165 emit_value_write(
3166 out,
3167 &sm.type_spec,
3168 &format!("{access}.{sm_name}()"),
3169 endian,
3170 origin,
3171 &pre,
3172 )?;
3173 }
3174 }
3175 Extensibility::Appendable | Extensibility::Mutable => {
3176 let cpp = scoped_to_cpp(sc);
3177 let id = next_nest_id();
3178 writeln!(out, "{pre}{{").map_err(fmt_err)?;
3179 writeln!(
3180 out,
3181 "{pre} ::dds::topic::xcdr2::pad_to_from_origin(zd_out, {origin}, 4);"
3182 )
3183 .map_err(fmt_err)?;
3184 if endian == "be" {
3185 writeln!(
3186 out,
3187 "{pre} auto zd_nsb{id} = ::dds::topic::topic_type_support<{cpp}>::encode_be({access}, zd_repr);"
3188 )
3189 .map_err(fmt_err)?;
3190 } else {
3191 writeln!(
3192 out,
3193 "{pre} auto zd_nsb{id} = ::dds::topic::topic_type_support<{cpp}>::encode({access}, zd_repr);"
3194 )
3195 .map_err(fmt_err)?;
3196 }
3197 writeln!(
3198 out,
3199 "{pre} zd_out.insert(zd_out.end(), zd_nsb{id}.begin(), zd_nsb{id}.end());"
3200 )
3201 .map_err(fmt_err)?;
3202 writeln!(out, "{pre}}}").map_err(fmt_err)?;
3203 }
3204 }
3205 }
3206 TypeSpec::Scoped(sc) if scoped_union(sc).is_some() => {
3210 let Some(u) = scoped_union(sc) else {
3211 return Ok(());
3212 };
3213 match union_extensibility(&u.annotations) {
3214 Extensibility::Final => {
3220 let disc_ts = switch_type_spec(&u.switch_type);
3221 let disc_cpp = switch_type_to_cpp(&u.switch_type)?;
3222 emit_value_write(
3223 out,
3224 &disc_ts,
3225 &format!("{access}._d()"),
3226 endian,
3227 origin,
3228 &pre,
3229 )?;
3230 emit_union_branch_switch_at(
3231 out, &u, &disc_cpp, false, endian, access, origin,
3232 )?;
3233 }
3234 Extensibility::Appendable | Extensibility::Mutable => {
3238 let cpp = scoped_to_cpp(sc);
3239 let id = next_nest_id();
3240 writeln!(out, "{pre}{{").map_err(fmt_err)?;
3241 writeln!(
3242 out,
3243 "{pre} ::dds::topic::xcdr2::pad_to_from_origin(zd_out, {origin}, 4);"
3244 )
3245 .map_err(fmt_err)?;
3246 if endian == "be" {
3247 writeln!(
3248 out,
3249 "{pre} auto zd_nub{id} = ::dds::topic::topic_type_support<{cpp}>::encode_be({access}, zd_repr);"
3250 )
3251 .map_err(fmt_err)?;
3252 } else {
3253 writeln!(
3254 out,
3255 "{pre} auto zd_nub{id} = ::dds::topic::topic_type_support<{cpp}>::encode({access}, zd_repr);"
3256 )
3257 .map_err(fmt_err)?;
3258 }
3259 writeln!(
3260 out,
3261 "{pre} zd_out.insert(zd_out.end(), zd_nub{id}.begin(), zd_nub{id}.end());"
3262 )
3263 .map_err(fmt_err)?;
3264 writeln!(out, "{pre}}}").map_err(fmt_err)?;
3265 }
3266 }
3267 }
3268 TypeSpec::Fixed(_) => {
3269 writeln!(
3273 out,
3274 "{pre}{{ const auto& zd_bcd = {access}.bcd_bytes(); zd_out.insert(zd_out.end(), zd_bcd.begin(), zd_bcd.end()); }}"
3275 )
3276 .map_err(fmt_err)?;
3277 }
3278 _ => {
3279 writeln!(out, "{pre}// xcdr2: member type not supported (skip)").map_err(fmt_err)?;
3280 }
3281 }
3282 Ok(())
3283}
3284
3285fn union_extensibility(anns: &[Annotation]) -> Extensibility {
3290 struct_extensibility(anns)
3291}
3292
3293fn emit_mutable_member_encode(
3300 out: &mut String,
3301 m: &Member,
3302 endian: &str,
3303 next_id: &mut u32,
3304) -> Result<(), CppGenError> {
3305 let m = &normalize_member(m);
3306 if !member_codegen_supported(m) {
3307 for decl in &m.declarators {
3308 let name = &decl.name().text;
3309 *next_id += 1; writeln!(
3311 out,
3312 " // xcdr2: @shared member '{name}' not supported (skip)"
3313 )
3314 .map_err(fmt_err)?;
3315 }
3316 return Ok(());
3317 }
3318 let is_optional = has_optional_annotation(&m.annotations);
3319 let must_understand = has_named_annotation(&m.annotations, "must_understand");
3320 let id_override = find_uint_annotation(&m.annotations, "id");
3321 let mu_lit = if must_understand { "true" } else { "false" };
3322
3323 for (idx, decl) in m.declarators.iter().enumerate() {
3324 let name = &decl.name().text;
3325 let this_id = match id_override {
3330 Some(id) => id + idx as u32,
3331 None => *next_id,
3332 };
3333 *next_id = this_id + 1;
3334 if !matches!(decl, Declarator::Simple(_)) {
3335 writeln!(
3336 out,
3337 " // xcdr2: array member '{name}' not supported (skip)"
3338 )
3339 .map_err(fmt_err)?;
3340 continue;
3341 }
3342 if !typespec_supported(&m.type_spec) {
3343 writeln!(
3344 out,
3345 " // xcdr2: member '{name}' not supported (skip)"
3346 )
3347 .map_err(fmt_err)?;
3348 continue;
3349 }
3350 let id_expr = format!("0x{this_id:x}u");
3351 if is_optional {
3352 writeln!(out, " if (zd_v.{name}().has_value()) {{").map_err(fmt_err)?;
3354 emit_mutable_value_emit(
3355 out,
3356 &m.type_spec,
3357 &format!("(*zd_v.{name}())"),
3358 &id_expr,
3359 mu_lit,
3360 endian,
3361 " ",
3362 )?;
3363 writeln!(out, " }}").map_err(fmt_err)?;
3364 } else {
3365 emit_mutable_value_emit(
3366 out,
3367 &m.type_spec,
3368 &format!("zd_v.{name}()"),
3369 &id_expr,
3370 mu_lit,
3371 endian,
3372 " ",
3373 )?;
3374 }
3375 }
3376 Ok(())
3377}
3378
3379fn emit_pl_cdr1_member_encode(
3386 out: &mut String,
3387 m: &Member,
3388 next_id: &mut u32,
3389 endian: &str,
3390) -> Result<(), CppGenError> {
3391 let beb = if endian == "be" { "true" } else { "false" };
3392 let m = &normalize_member(m);
3393 if !member_codegen_supported(m) {
3394 for decl in &m.declarators {
3395 *next_id += 1;
3396 let name = &decl.name().text;
3397 writeln!(
3398 out,
3399 " // xcdr1: @shared member '{name}' not supported (skip)"
3400 )
3401 .map_err(fmt_err)?;
3402 }
3403 return Ok(());
3404 }
3405 let is_optional = has_optional_annotation(&m.annotations);
3406 let id_override = find_uint_annotation(&m.annotations, "id");
3407 for (idx, decl) in m.declarators.iter().enumerate() {
3408 let name = &decl.name().text;
3409 let this_id = match id_override {
3412 Some(id) => id + idx as u32,
3413 None => *next_id,
3414 };
3415 *next_id = this_id + 1;
3416 if !matches!(decl, Declarator::Simple(_)) {
3417 writeln!(
3418 out,
3419 " // xcdr1: array member '{name}' not supported (skip)"
3420 )
3421 .map_err(fmt_err)?;
3422 continue;
3423 }
3424 if !typespec_supported(&m.type_spec) {
3425 writeln!(
3426 out,
3427 " // xcdr1: member '{name}' not supported (skip)"
3428 )
3429 .map_err(fmt_err)?;
3430 continue;
3431 }
3432 let access = if is_optional {
3433 format!("(*zd_v.{name}())")
3434 } else {
3435 format!("zd_v.{name}()")
3436 };
3437 let ind = if is_optional {
3438 " "
3439 } else {
3440 " "
3441 };
3442 if is_optional {
3443 writeln!(out, " if (zd_v.{name}().has_value()) {{").map_err(fmt_err)?;
3447 }
3448 writeln!(out, "{ind}{{").map_err(fmt_err)?;
3449 writeln!(
3450 out,
3451 "{ind} auto zd_pm = ::dds::topic::xcdr2::pl_cdr1_member_begin(zd_out, 0x{this_id:x}u, {beb});"
3452 )
3453 .map_err(fmt_err)?;
3454 writeln!(
3455 out,
3456 "{ind} const size_t zd_origin = zd_pm.body_start; (void)zd_origin;"
3457 )
3458 .map_err(fmt_err)?;
3459 emit_value_write(out, &m.type_spec, &access, endian, "zd_origin", ind)?;
3460 writeln!(
3461 out,
3462 "{ind} ::dds::topic::xcdr2::pl_cdr1_member_end(zd_out, zd_pm, {beb});"
3463 )
3464 .map_err(fmt_err)?;
3465 writeln!(out, "{ind}}}").map_err(fmt_err)?;
3466 if is_optional {
3467 writeln!(out, " }}").map_err(fmt_err)?;
3468 }
3469 }
3470 Ok(())
3471}
3472
3473fn emit_mutable_value_emit(
3474 out: &mut String,
3475 ts: &TypeSpec,
3476 access: &str,
3477 id_expr: &str,
3478 mu_lit: &str,
3479 endian: &str,
3480 indent: &str,
3481) -> Result<(), CppGenError> {
3482 let beb = if endian == "be" { "true" } else { "false" };
3483 match ts {
3484 TypeSpec::Primitive(PrimitiveType::Boolean) => {
3485 writeln!(
3486 out,
3487 "{indent}::dds::topic::xcdr2::emheader_u8(zd_out, zd_origin, {id_expr}, {mu_lit}, static_cast<uint8_t>({access} ? 1 : 0), {beb});"
3488 )
3489 .map_err(fmt_err)?;
3490 }
3491 TypeSpec::Primitive(PrimitiveType::Octet) => {
3492 writeln!(
3493 out,
3494 "{indent}::dds::topic::xcdr2::emheader_u8(zd_out, zd_origin, {id_expr}, {mu_lit}, {access}, {beb});"
3495 )
3496 .map_err(fmt_err)?;
3497 }
3498 TypeSpec::Primitive(p) => {
3499 let cpp_ty = primitive_to_cpp(*p);
3500 let size = primitive_size(*p);
3501 let helper = match size {
3511 2 => Some("emheader_2"),
3512 4 => Some("emheader_4"),
3513 8 => Some("emheader_8"),
3514 _ => None,
3515 };
3516 match helper {
3517 Some(h) if endian != "be" => {
3518 writeln!(
3519 out,
3520 "{indent}::dds::topic::xcdr2::{h}<{cpp_ty}>(zd_out, zd_origin, {id_expr}, {mu_lit}, {access});"
3521 )
3522 .map_err(fmt_err)?;
3523 }
3524 Some(_) => {
3525 let lc = match size {
3528 2 => 1,
3529 4 => 2,
3530 _ => 3,
3531 };
3532 writeln!(
3533 out,
3534 "{indent}{{ ::dds::topic::xcdr2::pad_to_from_origin(zd_out, zd_origin, 4);"
3535 )
3536 .map_err(fmt_err)?;
3537 writeln!(
3538 out,
3539 "{indent} ::dds::topic::xcdr2::emheader_write(zd_out, ::dds::topic::xcdr2::emheader_make({lc}u, {id_expr}, {mu_lit}), {beb});"
3540 )
3541 .map_err(fmt_err)?;
3542 writeln!(
3543 out,
3544 "{indent} ::dds::topic::xcdr2::write_be_raw<{cpp_ty}>(zd_out, {access}); }}"
3545 )
3546 .map_err(fmt_err)?;
3547 }
3548 None => {
3549 writeln!(
3550 out,
3551 "{indent}// xcdr2: unexpected primitive size {size} (skip)"
3552 )
3553 .map_err(fmt_err)?;
3554 }
3555 }
3556 }
3557 TypeSpec::String(s) if !s.wide => {
3558 if let Some(b) = &s.bound {
3560 let bv = const_expr_to_cpp(b);
3561 writeln!(
3562 out,
3563 "{indent}if ({access}.size() > {bv}) throw std::length_error(\"bounded string length exceeds its IDL bound ({bv})\");"
3564 )
3565 .map_err(fmt_err)?;
3566 }
3567 writeln!(
3574 out,
3575 "{indent}{{ ::dds::topic::xcdr2::pad_to_from_origin(zd_out, zd_origin, 4);"
3576 )
3577 .map_err(fmt_err)?;
3578 writeln!(
3579 out,
3580 "{indent} ::dds::topic::xcdr2::emheader_write(zd_out, ::dds::topic::xcdr2::emheader_make(5u, {id_expr}, {mu_lit}), {beb});"
3581 )
3582 .map_err(fmt_err)?;
3583 writeln!(
3586 out,
3587 "{indent} {{ const auto zd_body_origin = zd_out.size(); (void)zd_body_origin;"
3588 )
3589 .map_err(fmt_err)?;
3590 if endian == "be" {
3591 writeln!(
3592 out,
3593 "{indent} ::dds::topic::xcdr2::write_string_be(zd_out, {access});"
3594 )
3595 .map_err(fmt_err)?;
3596 } else {
3597 writeln!(
3598 out,
3599 "{indent} ::dds::topic::xcdr2::write_string_origin(zd_out, zd_body_origin, {access}, zd_max_align);"
3600 )
3601 .map_err(fmt_err)?;
3602 }
3603 writeln!(out, "{indent} }} }}").map_err(fmt_err)?;
3604 }
3605 TypeSpec::String(s) if s.wide => {
3606 if let Some(b) = &s.bound {
3608 let bv = const_expr_to_cpp(b);
3609 writeln!(
3610 out,
3611 "{indent}if ({access}.size() > {bv}) throw std::length_error(\"bounded wstring length exceeds its IDL bound ({bv})\");"
3612 )
3613 .map_err(fmt_err)?;
3614 }
3615 writeln!(
3620 out,
3621 "{indent}{{ ::dds::topic::xcdr2::pad_to_from_origin(zd_out, zd_origin, 4);"
3622 )
3623 .map_err(fmt_err)?;
3624 writeln!(
3625 out,
3626 "{indent} ::dds::topic::xcdr2::emheader_write(zd_out, ::dds::topic::xcdr2::emheader_make(5u, {id_expr}, {mu_lit}), {beb});"
3627 )
3628 .map_err(fmt_err)?;
3629 writeln!(
3630 out,
3631 "{indent} {{ const auto zd_body_origin = zd_out.size(); (void)zd_body_origin;"
3632 )
3633 .map_err(fmt_err)?;
3634 if endian == "be" {
3635 writeln!(
3636 out,
3637 "{indent} ::dds::topic::xcdr2::write_wstring_be(zd_out, {access});"
3638 )
3639 .map_err(fmt_err)?;
3640 } else {
3641 writeln!(
3642 out,
3643 "{indent} ::dds::topic::xcdr2::write_wstring_origin(zd_out, zd_body_origin, {access}, zd_max_align);"
3644 )
3645 .map_err(fmt_err)?;
3646 }
3647 writeln!(out, "{indent} }} }}").map_err(fmt_err)?;
3648 }
3649 TypeSpec::Sequence(seq) => {
3650 if let Some(b) = &seq.bound {
3652 let bv = const_expr_to_cpp(b);
3653 writeln!(
3654 out,
3655 "{indent}if ({access}.size() > {bv}) throw std::length_error(\"bounded sequence length exceeds its IDL bound ({bv})\");"
3656 )
3657 .map_err(fmt_err)?;
3658 }
3659 let seq_inner_dh = !matches!(&*seq.elem, TypeSpec::Primitive(_));
3668 if seq_inner_dh {
3669 writeln!(
3672 out,
3673 "{indent}{{ ::dds::topic::xcdr2::pad_to_from_origin(zd_out, zd_origin, 4);"
3674 )
3675 .map_err(fmt_err)?;
3676 writeln!(
3677 out,
3678 "{indent} ::dds::topic::xcdr2::emheader_write(zd_out, ::dds::topic::xcdr2::emheader_make(5u, {id_expr}, {mu_lit}), {beb});"
3679 )
3680 .map_err(fmt_err)?;
3681 writeln!(
3682 out,
3683 "{indent} {{ const auto zd_body_origin = zd_out.size(); (void)zd_body_origin;"
3684 )
3685 .map_err(fmt_err)?;
3686 } else {
3687 writeln!(
3689 out,
3690 "{indent}{{ const auto zd_sub = ::dds::topic::xcdr2::emheader_nextint_begin(zd_out, zd_origin, {id_expr}, {mu_lit}, {beb});"
3691 )
3692 .map_err(fmt_err)?;
3693 writeln!(
3694 out,
3695 "{indent} {{ const auto zd_body_origin = zd_sub.body_start; (void)zd_body_origin;"
3696 )
3697 .map_err(fmt_err)?;
3698 }
3699 if seq_inner_dh {
3700 writeln!(
3701 out,
3702 "{indent} const auto zd_seq_dh = ::dds::topic::xcdr2::dheader_begin_r(zd_out, zd_repr);"
3703 )
3704 .map_err(fmt_err)?;
3705 }
3706 if endian == "be" {
3707 writeln!(
3708 out,
3709 "{indent} ::dds::topic::xcdr2::write_be<uint32_t>(zd_out, static_cast<uint32_t>({access}.size()));"
3710 )
3711 .map_err(fmt_err)?;
3712 } else {
3713 writeln!(
3714 out,
3715 "{indent} ::dds::topic::xcdr2::write_le_origin<uint32_t>(zd_out, zd_body_origin, static_cast<uint32_t>({access}.size()), zd_max_align);"
3716 )
3717 .map_err(fmt_err)?;
3718 }
3719 if matches!(&*seq.elem, TypeSpec::Primitive(PrimitiveType::Octet)) {
3720 writeln!(
3722 out,
3723 "{indent} zd_out.insert(zd_out.end(), {access}.begin(), {access}.end());"
3724 )
3725 .map_err(fmt_err)?;
3726 } else {
3727 writeln!(out, "{indent} for (const auto& zd_e : {access}) {{")
3728 .map_err(fmt_err)?;
3729 match &*seq.elem {
3730 TypeSpec::Primitive(PrimitiveType::Boolean) => {
3731 writeln!(
3732 out,
3733 "{indent} ::dds::topic::xcdr2::write_bool(zd_out, zd_e);"
3734 )
3735 .map_err(fmt_err)?;
3736 }
3737 TypeSpec::Primitive(PrimitiveType::Octet) => {
3738 writeln!(
3739 out,
3740 "{indent} ::dds::topic::xcdr2::write_u8(zd_out, zd_e);"
3741 )
3742 .map_err(fmt_err)?;
3743 }
3744 TypeSpec::Primitive(p) => {
3745 let cpp_ty = primitive_to_cpp(*p);
3746 if endian == "be" {
3747 writeln!(
3748 out,
3749 "{indent} ::dds::topic::xcdr2::write_be<{cpp_ty}>(zd_out, zd_e);"
3750 )
3751 .map_err(fmt_err)?;
3752 } else {
3753 writeln!(out, "{indent} ::dds::topic::xcdr2::write_le_origin<{cpp_ty}>(zd_out, zd_body_origin, zd_e, zd_max_align);").map_err(fmt_err)?;
3754 }
3755 }
3756 TypeSpec::String(s) if !s.wide => {
3757 if endian == "be" {
3758 writeln!(
3759 out,
3760 "{indent} ::dds::topic::xcdr2::write_string_be(zd_out, zd_e);"
3761 )
3762 .map_err(fmt_err)?;
3763 } else {
3764 writeln!(out, "{indent} ::dds::topic::xcdr2::write_string_origin(zd_out, zd_body_origin, zd_e, zd_max_align);").map_err(fmt_err)?;
3765 }
3766 }
3767 TypeSpec::String(_) => {
3771 emit_value_write(
3772 out,
3773 &seq.elem,
3774 "zd_e",
3775 endian,
3776 "zd_body_origin",
3777 &format!("{indent} "),
3778 )?;
3779 }
3780 TypeSpec::Scoped(sc)
3781 if scoped_is_enum(sc)
3782 || scoped_struct(sc).is_some()
3783 || scoped_union(sc).is_some() =>
3784 {
3785 emit_value_write(
3786 out,
3787 &seq.elem,
3788 "zd_e",
3789 endian,
3790 "zd_body_origin",
3791 &format!("{indent} "),
3792 )?;
3793 }
3794 TypeSpec::Sequence(_) | TypeSpec::Map(_) => {
3796 emit_value_write(
3797 out,
3798 &seq.elem,
3799 "zd_e",
3800 endian,
3801 "zd_body_origin",
3802 &format!("{indent} "),
3803 )?;
3804 }
3805 _ => {
3806 writeln!(
3807 out,
3808 "{indent} // xcdr2: nested seq-elem not supported"
3809 )
3810 .map_err(fmt_err)?;
3811 }
3812 }
3813 writeln!(out, "{indent} }}").map_err(fmt_err)?;
3814 }
3815 if seq_inner_dh {
3816 writeln!(
3817 out,
3818 "{indent} ::dds::topic::xcdr2::dheader_end_r(zd_out, zd_seq_dh, {beb}, zd_repr);"
3819 )
3820 .map_err(fmt_err)?;
3821 }
3822 writeln!(out, "{indent} }}").map_err(fmt_err)?;
3823 if seq_inner_dh {
3824 writeln!(out, "{indent}}}").map_err(fmt_err)?;
3826 } else {
3827 writeln!(
3828 out,
3829 "{indent} ::dds::topic::xcdr2::emheader_nextint_end(zd_out, zd_sub, {beb}); }}"
3830 )
3831 .map_err(fmt_err)?;
3832 }
3833 }
3834 TypeSpec::Scoped(s) if scoped_is_enum(s) => {
3836 let ec = enum_wire_ctype(scoped_enum_bytes(s));
3837 writeln!(
3838 out,
3839 "{indent}::dds::topic::xcdr2::emheader_4<{ec}>(zd_out, zd_origin, {id_expr}, {mu_lit}, static_cast<{ec}>({access}));"
3840 )
3841 .map_err(fmt_err)?;
3842 }
3843 TypeSpec::Scoped(sc) if scoped_struct(sc).is_some() => {
3853 let Some((def, ext)) = scoped_struct(sc) else {
3854 return Ok(());
3855 };
3856 match ext {
3857 Extensibility::Final => {
3858 writeln!(
3860 out,
3861 "{indent}{{ const auto zd_sub = ::dds::topic::xcdr2::emheader_nextint_begin(zd_out, zd_origin, {id_expr}, {mu_lit}, {beb});"
3862 )
3863 .map_err(fmt_err)?;
3864 writeln!(
3865 out,
3866 "{indent} {{ const auto zd_body_origin = zd_sub.body_start; (void)zd_body_origin;"
3867 )
3868 .map_err(fmt_err)?;
3869 for sm in &def.members {
3870 let sm_name = &sm.declarators[0].name().text;
3871 emit_value_write(
3872 out,
3873 &sm.type_spec,
3874 &format!("{access}.{sm_name}()"),
3875 endian,
3876 "zd_body_origin",
3877 &format!("{indent} "),
3878 )?;
3879 }
3880 writeln!(out, "{indent} }}").map_err(fmt_err)?;
3881 writeln!(
3882 out,
3883 "{indent} ::dds::topic::xcdr2::emheader_nextint_end(zd_out, zd_sub, {beb}); }}"
3884 )
3885 .map_err(fmt_err)?;
3886 }
3887 Extensibility::Appendable | Extensibility::Mutable => {
3888 let cpp = scoped_to_cpp(sc);
3891 let id = next_nest_id();
3892 writeln!(
3893 out,
3894 "{indent}{{ ::dds::topic::xcdr2::pad_to_from_origin(zd_out, zd_origin, 4);"
3895 )
3896 .map_err(fmt_err)?;
3897 writeln!(
3898 out,
3899 "{indent} ::dds::topic::xcdr2::emheader_write(zd_out, ::dds::topic::xcdr2::emheader_make(5u, {id_expr}, {mu_lit}), {beb});"
3900 )
3901 .map_err(fmt_err)?;
3902 if endian == "be" {
3903 writeln!(
3904 out,
3905 "{indent} auto zd_nsb{id} = ::dds::topic::topic_type_support<{cpp}>::encode_be({access}, zd_repr);"
3906 )
3907 .map_err(fmt_err)?;
3908 } else {
3909 writeln!(
3910 out,
3911 "{indent} auto zd_nsb{id} = ::dds::topic::topic_type_support<{cpp}>::encode({access}, zd_repr);"
3912 )
3913 .map_err(fmt_err)?;
3914 }
3915 writeln!(
3916 out,
3917 "{indent} zd_out.insert(zd_out.end(), zd_nsb{id}.begin(), zd_nsb{id}.end()); }}"
3918 )
3919 .map_err(fmt_err)?;
3920 }
3921 }
3922 }
3923 TypeSpec::Map(m) => {
3929 let map_inner_dh = !map_pair_is_primitive(&m.key, &m.value);
3936 if map_inner_dh {
3937 writeln!(
3938 out,
3939 "{indent}{{ ::dds::topic::xcdr2::pad_to_from_origin(zd_out, zd_origin, 4);"
3940 )
3941 .map_err(fmt_err)?;
3942 writeln!(
3943 out,
3944 "{indent} ::dds::topic::xcdr2::emheader_write(zd_out, ::dds::topic::xcdr2::emheader_make(5u, {id_expr}, {mu_lit}), {beb});"
3945 )
3946 .map_err(fmt_err)?;
3947 writeln!(
3948 out,
3949 "{indent} {{ const auto zd_body_origin = zd_out.size(); (void)zd_body_origin;"
3950 )
3951 .map_err(fmt_err)?;
3952 writeln!(
3953 out,
3954 "{indent} const auto zd_map_dh = ::dds::topic::xcdr2::dheader_begin_r(zd_out, zd_repr);"
3955 )
3956 .map_err(fmt_err)?;
3957 } else {
3958 writeln!(
3960 out,
3961 "{indent}{{ const auto zd_sub = ::dds::topic::xcdr2::emheader_nextint_begin(zd_out, zd_origin, {id_expr}, {mu_lit}, {beb});"
3962 )
3963 .map_err(fmt_err)?;
3964 writeln!(
3965 out,
3966 "{indent} {{ const auto zd_body_origin = zd_sub.body_start; (void)zd_body_origin;"
3967 )
3968 .map_err(fmt_err)?;
3969 }
3970 if endian == "be" {
3971 writeln!(out, "{indent} ::dds::topic::xcdr2::write_be<uint32_t>(zd_out, static_cast<uint32_t>({access}.size()));").map_err(fmt_err)?;
3972 } else {
3973 writeln!(out, "{indent} ::dds::topic::xcdr2::write_le_origin<uint32_t>(zd_out, zd_body_origin, static_cast<uint32_t>({access}.size()), zd_max_align);").map_err(fmt_err)?;
3974 }
3975 writeln!(out, "{indent} for (const auto& zd_kv : {access}) {{")
3976 .map_err(fmt_err)?;
3977 let kv_indent = format!("{indent} ");
3978 emit_value_write(
3979 out,
3980 &m.key,
3981 "zd_kv.first",
3982 endian,
3983 "zd_body_origin",
3984 &kv_indent,
3985 )?;
3986 emit_value_write(
3987 out,
3988 &m.value,
3989 "zd_kv.second",
3990 endian,
3991 "zd_body_origin",
3992 &kv_indent,
3993 )?;
3994 writeln!(out, "{indent} }}").map_err(fmt_err)?;
3995 if map_inner_dh {
3996 writeln!(
3997 out,
3998 "{indent} ::dds::topic::xcdr2::dheader_end_r(zd_out, zd_map_dh, {beb}, zd_repr);"
3999 )
4000 .map_err(fmt_err)?;
4001 writeln!(out, "{indent} }}").map_err(fmt_err)?;
4002 writeln!(out, "{indent}}}").map_err(fmt_err)?;
4004 } else {
4005 writeln!(out, "{indent} }}").map_err(fmt_err)?;
4006 writeln!(
4007 out,
4008 "{indent} ::dds::topic::xcdr2::emheader_nextint_end(zd_out, zd_sub, {beb}); }}"
4009 )
4010 .map_err(fmt_err)?;
4011 }
4012 }
4013 TypeSpec::Fixed(_) => {
4014 writeln!(
4018 out,
4019 "{indent}{{ const auto zd_sub = ::dds::topic::xcdr2::emheader_nextint_begin(zd_out, zd_origin, {id_expr}, {mu_lit}, {beb});"
4020 )
4021 .map_err(fmt_err)?;
4022 writeln!(
4023 out,
4024 "{indent} {{ const auto& zd_bcd = {access}.bcd_bytes(); zd_out.insert(zd_out.end(), zd_bcd.begin(), zd_bcd.end()); }}"
4025 )
4026 .map_err(fmt_err)?;
4027 writeln!(
4028 out,
4029 "{indent} ::dds::topic::xcdr2::emheader_nextint_end(zd_out, zd_sub, {beb}); }}"
4030 )
4031 .map_err(fmt_err)?;
4032 }
4033 _ => {
4034 writeln!(out, "{indent}// xcdr2: unsupported member type").map_err(fmt_err)?;
4035 }
4036 }
4037 Ok(())
4038}
4039
4040fn map_pair_is_primitive(key: &TypeSpec, value: &TypeSpec) -> bool {
4046 matches!(key, TypeSpec::Primitive(_)) && matches!(value, TypeSpec::Primitive(_))
4047}
4048
4049fn primitive_size(p: PrimitiveType) -> usize {
4050 use zerodds_idl::ast::{FloatingType, IntegerType};
4051 match p {
4052 PrimitiveType::Boolean => 1,
4053 PrimitiveType::Octet => 1,
4054 PrimitiveType::Char => 1,
4055 PrimitiveType::WideChar => 2,
4056 PrimitiveType::Integer(i) => match i {
4057 IntegerType::Int8 | IntegerType::UInt8 => 1,
4058 IntegerType::Short | IntegerType::UShort | IntegerType::Int16 | IntegerType::UInt16 => {
4059 2
4060 }
4061 IntegerType::Long | IntegerType::ULong | IntegerType::Int32 | IntegerType::UInt32 => 4,
4062 IntegerType::LongLong
4063 | IntegerType::ULongLong
4064 | IntegerType::Int64
4065 | IntegerType::UInt64 => 8,
4066 },
4067 PrimitiveType::Floating(f) => match f {
4068 FloatingType::Float => 4,
4069 FloatingType::Double => 8,
4070 FloatingType::LongDouble => 16,
4071 },
4072 }
4073}
4074
4075fn emit_decode_fn(
4076 out: &mut String,
4077 cpp_fqn: &str,
4078 s: &StructDef,
4079 ext: Extensibility,
4080 phase: TtsPhase,
4081) -> Result<(), CppGenError> {
4082 if phase == TtsPhase::Decl {
4083 writeln!(
4084 out,
4085 " static {cpp_fqn} decode(const uint8_t* zd_buf, size_t zd_len, \
4086 ::dds::topic::xcdr2::XcdrVersion zd_repr, bool zd_be = false);"
4087 )
4088 .map_err(fmt_err)?;
4089 return Ok(());
4090 }
4091 writeln!(
4092 out,
4093 "inline {cpp_fqn} topic_type_support<{cpp_fqn}>::decode(const uint8_t* zd_buf, size_t zd_len, \
4094 ::dds::topic::xcdr2::XcdrVersion zd_repr, bool zd_be) {{"
4095 )
4096 .map_err(fmt_err)?;
4097 writeln!(out, " size_t zd_pos = 0;").map_err(fmt_err)?;
4098 writeln!(out, " {cpp_fqn} zd_v;").map_err(fmt_err)?;
4099 writeln!(
4103 out,
4104 " const size_t zd_max_align = ::dds::topic::xcdr2::xcdr_max_align(zd_repr);"
4105 )
4106 .map_err(fmt_err)?;
4107 writeln!(
4108 out,
4109 " (void)zd_buf; (void)zd_len; (void)zd_pos; (void)zd_max_align; (void)zd_be;"
4110 )
4111 .map_err(fmt_err)?;
4112
4113 match ext {
4114 Extensibility::Final => {
4115 writeln!(out, " const size_t zd_origin = 0;").map_err(fmt_err)?;
4116 writeln!(out, " (void)zd_origin;").map_err(fmt_err)?;
4117 for m in &s.members {
4118 emit_plain_member_decode(out, m, "zd_origin")?;
4119 }
4120 }
4121 Extensibility::Appendable => {
4122 writeln!(
4126 out,
4127 " const bool zd_x1 = (zd_repr == ::dds::topic::xcdr2::XcdrVersion::Xcdr1);"
4128 )
4129 .map_err(fmt_err)?;
4130 writeln!(out, " size_t zd_end = zd_len;").map_err(fmt_err)?;
4131 writeln!(out, " if (!zd_x1) {{").map_err(fmt_err)?;
4132 writeln!(
4133 out,
4134 " const auto zd_dh = ::dds::topic::xcdr2::dheader_read(zd_buf, zd_pos, zd_len, zd_be);"
4135 )
4136 .map_err(fmt_err)?;
4137 writeln!(out, " zd_end = zd_pos + zd_dh;").map_err(fmt_err)?;
4138 writeln!(out, " }}").map_err(fmt_err)?;
4139 writeln!(out, " const size_t zd_origin = zd_pos;").map_err(fmt_err)?;
4140 writeln!(out, " (void)zd_end;").map_err(fmt_err)?;
4141 for m in &s.members {
4142 emit_plain_member_decode(out, m, "zd_origin")?;
4143 }
4144 writeln!(
4147 out,
4148 " if (!zd_x1 && zd_pos < zd_end) zd_pos = zd_end;"
4149 )
4150 .map_err(fmt_err)?;
4151 }
4152 Extensibility::Mutable => {
4153 writeln!(
4157 out,
4158 " if (zd_repr == ::dds::topic::xcdr2::XcdrVersion::Xcdr1) {{"
4159 )
4160 .map_err(fmt_err)?;
4161 writeln!(out, " while (zd_pos + 4 <= zd_len) {{").map_err(fmt_err)?;
4162 writeln!(
4163 out,
4164 " const auto zd_ph = ::dds::topic::xcdr2::pl_cdr1_read_header(zd_buf, zd_pos, zd_len, zd_be);"
4165 )
4166 .map_err(fmt_err)?;
4167 writeln!(out, " if (zd_ph.is_end) break;").map_err(fmt_err)?;
4168 writeln!(
4169 out,
4170 " const size_t zd_pl_origin = zd_pos; (void)zd_pl_origin;"
4171 )
4172 .map_err(fmt_err)?;
4173 writeln!(
4174 out,
4175 " const size_t zd_pl_end = zd_pos + zd_ph.body_len;"
4176 )
4177 .map_err(fmt_err)?;
4178 writeln!(out, " switch (zd_ph.member_id) {{").map_err(fmt_err)?;
4179 let mut zd_pl_id = 0u32;
4180 for m in &s.members {
4181 emit_pl_cdr1_member_decode_case(out, m, &mut zd_pl_id)?;
4182 }
4183 writeln!(out, " default: break;").map_err(fmt_err)?;
4184 writeln!(out, " }}").map_err(fmt_err)?;
4185 writeln!(
4186 out,
4187 " if (zd_pos < zd_pl_end) zd_pos = zd_pl_end;"
4188 )
4189 .map_err(fmt_err)?;
4190 writeln!(out, " ::dds::topic::xcdr2::pl_cdr1_skip_pad(zd_pos, zd_len, zd_ph.body_len);").map_err(fmt_err)?;
4191 writeln!(out, " }}").map_err(fmt_err)?;
4192 writeln!(out, " return zd_v;").map_err(fmt_err)?;
4193 writeln!(out, " }}").map_err(fmt_err)?;
4194 writeln!(
4195 out,
4196 " const auto zd_dh = ::dds::topic::xcdr2::dheader_read(zd_buf, zd_pos, zd_len, zd_be);"
4197 )
4198 .map_err(fmt_err)?;
4199 writeln!(out, " const size_t zd_origin = zd_pos;").map_err(fmt_err)?;
4200 writeln!(out, " const size_t zd_end = zd_origin + zd_dh;").map_err(fmt_err)?;
4201 writeln!(out, " while (zd_pos + 4 <= zd_end) {{").map_err(fmt_err)?;
4202 writeln!(
4203 out,
4204 " const auto zd_h = ::dds::topic::xcdr2::emheader_read(zd_buf, zd_pos, zd_len, zd_origin, zd_be);"
4205 )
4206 .map_err(fmt_err)?;
4207 writeln!(out, " switch (zd_h.member_id) {{").map_err(fmt_err)?;
4208 let mut zd_next_id = 0u32;
4209 for m in &s.members {
4210 emit_mutable_member_decode_case(out, m, &mut zd_next_id)?;
4211 }
4212 writeln!(out, " default: {{").map_err(fmt_err)?;
4213 writeln!(
4214 out,
4215 " // Unknown member: per-LC skip per XTypes 1.3"
4216 )
4217 .map_err(fmt_err)?;
4218 writeln!(
4219 out,
4220 " // §7.4.3.4.2 (LengthCode::body_len). LC0..3 are"
4221 )
4222 .map_err(fmt_err)?;
4223 writeln!(
4224 out,
4225 " // fixed 1/2/4/8-byte bodies WITHOUT NEXTINT; LC4/5 NEXTINT="
4226 )
4227 .map_err(fmt_err)?;
4228 writeln!(
4229 out,
4230 " // byte length; LC6/7 NEXTINT=element count (4 + 4n / 4 + 8n)."
4231 )
4232 .map_err(fmt_err)?;
4233 writeln!(
4234 out,
4235 " if (zd_h.lc == 0) {{ zd_pos += 1; }}"
4236 )
4237 .map_err(fmt_err)?;
4238 writeln!(
4239 out,
4240 " else if (zd_h.lc == 1) {{ zd_pos += 2; }}"
4241 )
4242 .map_err(fmt_err)?;
4243 writeln!(
4244 out,
4245 " else if (zd_h.lc == 2) {{ zd_pos += 4; }}"
4246 )
4247 .map_err(fmt_err)?;
4248 writeln!(
4249 out,
4250 " else if (zd_h.lc == 3) {{ zd_pos += 8; }}"
4251 )
4252 .map_err(fmt_err)?;
4253 writeln!(
4254 out,
4255 " else if (zd_h.lc == 4 || zd_h.lc == 5) {{ auto zd_n = ::dds::topic::xcdr2::emheader_nextint_read(zd_buf, zd_pos, zd_len, zd_be); zd_pos += zd_n; }}"
4256 )
4257 .map_err(fmt_err)?;
4258 writeln!(
4259 out,
4260 " else if (zd_h.lc == 6) {{ auto zd_c = ::dds::topic::xcdr2::emheader_nextint_read(zd_buf, zd_pos, zd_len, zd_be); zd_pos += 4 + 4 * static_cast<size_t>(zd_c); }}"
4261 )
4262 .map_err(fmt_err)?;
4263 writeln!(
4264 out,
4265 " else {{ auto zd_c = ::dds::topic::xcdr2::emheader_nextint_read(zd_buf, zd_pos, zd_len, zd_be); zd_pos += 4 + 8 * static_cast<size_t>(zd_c); }}"
4266 )
4267 .map_err(fmt_err)?;
4268 writeln!(out, " break;").map_err(fmt_err)?;
4269 writeln!(out, " }}").map_err(fmt_err)?;
4270 writeln!(out, " }}").map_err(fmt_err)?;
4271 writeln!(out, " }}").map_err(fmt_err)?;
4272 writeln!(out, " if (zd_pos < zd_end) zd_pos = zd_end;").map_err(fmt_err)?;
4273 }
4274 }
4275
4276 writeln!(out, " return zd_v;").map_err(fmt_err)?;
4277 writeln!(out, " }}").map_err(fmt_err)?;
4278 Ok(())
4279}
4280
4281fn emit_plain_member_decode(out: &mut String, m: &Member, origin: &str) -> Result<(), CppGenError> {
4282 let m = &normalize_member(m);
4283 if !member_codegen_supported(m) {
4284 for decl in &m.declarators {
4285 let name = &decl.name().text;
4286 writeln!(
4287 out,
4288 " // xcdr2: @shared member '{name}' not supported (skip)"
4289 )
4290 .map_err(fmt_err)?;
4291 }
4292 return Ok(());
4293 }
4294 let is_optional = has_optional_annotation(&m.annotations);
4295 for decl in &m.declarators {
4296 let name = &decl.name().text;
4297 if let Declarator::Array(arr) = decl {
4300 let prim = matches!(m.type_spec, TypeSpec::Primitive(_));
4301 let leaf_1d = arr.sizes.len() == 1
4302 && matches!(m.type_spec, TypeSpec::Primitive(_) | TypeSpec::String(_));
4303 let prim_read_expr = || -> String {
4304 match &m.type_spec {
4305 TypeSpec::Primitive(PrimitiveType::Boolean) => {
4306 "::dds::topic::xcdr2::read_bool(zd_buf, zd_pos, zd_len)".to_string()
4307 }
4308 TypeSpec::Primitive(PrimitiveType::Octet) => {
4309 "::dds::topic::xcdr2::read_u8(zd_buf, zd_pos, zd_len)".to_string()
4310 }
4311 TypeSpec::Primitive(p) => format!(
4312 "::dds::topic::xcdr2::read_le_origin<{}>(zd_buf, zd_pos, zd_len, {origin}, zd_max_align, zd_be)",
4313 primitive_to_cpp(*p)
4314 ),
4315 TypeSpec::String(s) if s.wide => format!(
4316 "::dds::topic::xcdr2::read_wstring_origin(zd_buf, zd_pos, zd_len, {origin}, zd_max_align, zd_be)"
4317 ),
4318 _ => format!(
4319 "::dds::topic::xcdr2::read_string_origin(zd_buf, zd_pos, zd_len, {origin}, zd_max_align, zd_be)"
4320 ),
4321 }
4322 };
4323 if leaf_1d {
4324 let read_expr = prim_read_expr();
4325 writeln!(out, " {{").map_err(fmt_err)?;
4326 writeln!(out, " auto zd_arr = zd_v.{name}();").map_err(fmt_err)?;
4327 writeln!(
4328 out,
4329 " for (auto& zd_ae : zd_arr) {{ zd_ae = {read_expr}; }}"
4330 )
4331 .map_err(fmt_err)?;
4332 writeln!(out, " zd_v.{name}(zd_arr);").map_err(fmt_err)?;
4333 writeln!(out, " }}").map_err(fmt_err)?;
4334 } else if prim && arr.sizes.len() >= 2 {
4335 let read_expr = prim_read_expr();
4339 let n = arr.sizes.len();
4340 writeln!(out, " {{").map_err(fmt_err)?;
4341 writeln!(out, " auto zd_arr = zd_v.{name}();").map_err(fmt_err)?;
4342 let mut acc = String::from("zd_arr");
4343 let mut ind = String::from(" ");
4344 for d in 0..n {
4345 let lv = format!("zd_a{d}");
4346 writeln!(out, "{ind}for (auto& {lv} : {acc}) {{").map_err(fmt_err)?;
4347 acc = lv;
4348 ind.push_str(" ");
4349 }
4350 writeln!(out, "{ind}{acc} = {read_expr};").map_err(fmt_err)?;
4351 for _ in 0..n {
4352 ind.truncate(ind.len() - 4);
4353 writeln!(out, "{ind}}}").map_err(fmt_err)?;
4354 }
4355 writeln!(out, " zd_v.{name}(zd_arr);").map_err(fmt_err)?;
4356 writeln!(out, " }}").map_err(fmt_err)?;
4357 } else if matches!(&m.type_spec, TypeSpec::Scoped(s) if scoped_is_enum(s) || scoped_struct(s).is_some() || scoped_union(s).is_some())
4358 || (matches!(m.type_spec, TypeSpec::String(_)) && arr.sizes.len() >= 2)
4359 {
4360 let n = arr.sizes.len();
4367 writeln!(out, " {{").map_err(fmt_err)?;
4368 writeln!(out, " const auto zd_arr_dh = ::dds::topic::xcdr2::dheader_read_r(zd_buf, zd_pos, zd_len, zd_be, zd_repr); (void)zd_arr_dh;").map_err(fmt_err)?;
4369 writeln!(out, " auto zd_arr = zd_v.{name}();").map_err(fmt_err)?;
4370 let mut acc = String::from("zd_arr");
4371 let mut ind = String::from(" ");
4372 for d in 0..n {
4373 let lv = format!("zd_a{d}");
4374 writeln!(out, "{ind}for (auto& {lv} : {acc}) {{").map_err(fmt_err)?;
4375 acc = lv;
4376 ind.push_str(" ");
4377 }
4378 emit_value_read(out, &m.type_spec, &format!("{acc} ="), origin, &ind, false)?;
4379 for _ in 0..n {
4380 ind.truncate(ind.len() - 4);
4381 writeln!(out, "{ind}}}").map_err(fmt_err)?;
4382 }
4383 writeln!(out, " zd_v.{name}(zd_arr);").map_err(fmt_err)?;
4384 writeln!(out, " }}").map_err(fmt_err)?;
4385 } else {
4386 writeln!(
4387 out,
4388 " // xcdr2: array member '{name}' (1-D string only / unsupported elem) not supported (skip)"
4389 )
4390 .map_err(fmt_err)?;
4391 }
4392 continue;
4393 }
4394 if !typespec_supported(&m.type_spec) {
4395 writeln!(
4396 out,
4397 " // xcdr2: member '{name}' not supported (skip)"
4398 )
4399 .map_err(fmt_err)?;
4400 continue;
4401 }
4402 if is_optional {
4403 writeln!(out, " {{").map_err(fmt_err)?;
4404 writeln!(
4405 out,
4406 " uint8_t zd_present = ::dds::topic::xcdr2::read_u8(zd_buf, zd_pos, zd_len);"
4407 )
4408 .map_err(fmt_err)?;
4409 writeln!(out, " if (zd_present) {{").map_err(fmt_err)?;
4410 emit_value_read(
4411 out,
4412 &m.type_spec,
4413 &format!("zd_v.{name}"),
4414 origin,
4415 " ",
4416 true,
4417 )?;
4418 writeln!(out, " }} else {{").map_err(fmt_err)?;
4419 writeln!(out, " zd_v.{name}(std::nullopt);").map_err(fmt_err)?;
4420 writeln!(out, " }}").map_err(fmt_err)?;
4421 writeln!(out, " }}").map_err(fmt_err)?;
4422 } else {
4423 emit_value_read(
4424 out,
4425 &m.type_spec,
4426 &format!("zd_v.{name}"),
4427 origin,
4428 " ",
4429 false,
4430 )?;
4431 }
4432 }
4433 Ok(())
4434}
4435
4436fn emit_value_read(
4438 out: &mut String,
4439 ts: &TypeSpec,
4440 setter: &str,
4441 origin: &str,
4442 indent: &str,
4443 is_opt: bool,
4444) -> Result<(), CppGenError> {
4445 let wrap_opt = |v: String| -> String {
4446 if is_opt {
4447 format!("std::optional<decltype({v})>({v})")
4448 } else {
4449 v
4450 }
4451 };
4452 let _ = wrap_opt;
4453 match ts {
4454 TypeSpec::Primitive(PrimitiveType::Boolean) => {
4455 writeln!(
4456 out,
4457 "{indent}{setter}(::dds::topic::xcdr2::read_bool(zd_buf, zd_pos, zd_len));"
4458 )
4459 .map_err(fmt_err)?;
4460 }
4461 TypeSpec::Primitive(PrimitiveType::Octet) => {
4462 writeln!(
4463 out,
4464 "{indent}{setter}(::dds::topic::xcdr2::read_u8(zd_buf, zd_pos, zd_len));"
4465 )
4466 .map_err(fmt_err)?;
4467 }
4468 TypeSpec::Primitive(p) => {
4469 let cpp_ty = primitive_to_cpp(*p);
4470 writeln!(
4471 out,
4472 "{indent}{setter}(::dds::topic::xcdr2::read_le_origin<{cpp_ty}>(zd_buf, zd_pos, zd_len, {origin}, zd_max_align, zd_be));"
4473 )
4474 .map_err(fmt_err)?;
4475 }
4476 TypeSpec::String(s) if !s.wide => {
4477 writeln!(
4478 out,
4479 "{indent}{setter}(::dds::topic::xcdr2::read_string_origin(zd_buf, zd_pos, zd_len, {origin}, zd_max_align, zd_be));"
4480 )
4481 .map_err(fmt_err)?;
4482 }
4483 TypeSpec::String(s) if s.wide => {
4484 writeln!(
4485 out,
4486 "{indent}{setter}(::dds::topic::xcdr2::read_wstring_origin(zd_buf, zd_pos, zd_len, {origin}, zd_max_align, zd_be));"
4487 )
4488 .map_err(fmt_err)?;
4489 }
4490 TypeSpec::Sequence(seq) => {
4491 if matches!(&*seq.elem, TypeSpec::Primitive(PrimitiveType::Octet)) {
4492 writeln!(out, "{indent}{{").map_err(fmt_err)?;
4494 writeln!(out, "{indent} auto zd_cnt = ::dds::topic::xcdr2::read_le_origin<uint32_t>(zd_buf, zd_pos, zd_len, {origin}, zd_max_align, zd_be);").map_err(fmt_err)?;
4495 writeln!(
4496 out,
4497 "{indent} ::dds::topic::xcdr2::check_avail(zd_pos, zd_cnt, zd_len);"
4498 )
4499 .map_err(fmt_err)?;
4500 writeln!(
4501 out,
4502 "{indent} std::vector<uint8_t> zd_seq(zd_buf + zd_pos, zd_buf + zd_pos + zd_cnt);"
4503 )
4504 .map_err(fmt_err)?;
4505 writeln!(out, "{indent} zd_pos += zd_cnt;").map_err(fmt_err)?;
4506 writeln!(out, "{indent} {setter}(std::move(zd_seq));").map_err(fmt_err)?;
4507 writeln!(out, "{indent}}}").map_err(fmt_err)?;
4508 return Ok(());
4509 }
4510 let elem_cpp_ty: String = match &*seq.elem {
4511 TypeSpec::Primitive(PrimitiveType::Boolean) => "bool".to_string(),
4512 TypeSpec::Primitive(p) => primitive_to_cpp(*p).to_string(),
4513 TypeSpec::String(s) if !s.wide => "std::string".to_string(),
4514 TypeSpec::String(_) => "std::wstring".to_string(),
4516 TypeSpec::Scoped(s) if scoped_is_enum(s) => scoped_to_cpp(s),
4519 TypeSpec::Scoped(s) if scoped_struct(s).is_some() => scoped_to_cpp(s),
4520 TypeSpec::Scoped(s) if scoped_union(s).is_some() => scoped_to_cpp(s),
4523 TypeSpec::Sequence(_) => typespec_to_cpp(&seq.elem)?,
4525 TypeSpec::Map(_) => typespec_to_cpp(&seq.elem)?,
4527 _ => {
4528 writeln!(
4529 out,
4530 "{indent}// xcdr2: nested seq-elem not supported (skip)"
4531 )
4532 .map_err(fmt_err)?;
4533 return Ok(());
4534 }
4535 };
4536 writeln!(out, "{indent}{{").map_err(fmt_err)?;
4537 if !matches!(&*seq.elem, TypeSpec::Primitive(_)) {
4539 writeln!(
4540 out,
4541 "{indent} ::dds::topic::xcdr2::skip_pad_from_origin(zd_pos, {origin}, 4);"
4542 )
4543 .map_err(fmt_err)?;
4544 writeln!(out, "{indent} const auto zd_seq_dh = ::dds::topic::xcdr2::dheader_read_r(zd_buf, zd_pos, zd_len, zd_be, zd_repr); (void)zd_seq_dh;").map_err(fmt_err)?;
4545 }
4546 writeln!(out, "{indent} auto zd_cnt = ::dds::topic::xcdr2::read_le_origin<uint32_t>(zd_buf, zd_pos, zd_len, {origin}, zd_max_align, zd_be);").map_err(fmt_err)?;
4547 writeln!(out, "{indent} std::vector<{elem_cpp_ty}> zd_seq;").map_err(fmt_err)?;
4548 writeln!(out, "{indent} zd_seq.reserve(zd_cnt);").map_err(fmt_err)?;
4549 writeln!(
4550 out,
4551 "{indent} for (uint32_t zd_i = 0; zd_i < zd_cnt; ++zd_i) {{"
4552 )
4553 .map_err(fmt_err)?;
4554 match &*seq.elem {
4555 TypeSpec::Primitive(PrimitiveType::Boolean) => {
4556 writeln!(out, "{indent} zd_seq.push_back(::dds::topic::xcdr2::read_bool(zd_buf, zd_pos, zd_len));").map_err(fmt_err)?;
4557 }
4558 TypeSpec::Primitive(PrimitiveType::Octet) => {
4559 writeln!(out, "{indent} zd_seq.push_back(::dds::topic::xcdr2::read_u8(zd_buf, zd_pos, zd_len));").map_err(fmt_err)?;
4560 }
4561 TypeSpec::Primitive(p) => {
4562 let cpp_ty = primitive_to_cpp(*p);
4563 writeln!(out, "{indent} zd_seq.push_back(::dds::topic::xcdr2::read_le_origin<{cpp_ty}>(zd_buf, zd_pos, zd_len, {origin}, zd_max_align, zd_be));").map_err(fmt_err)?;
4564 }
4565 TypeSpec::String(s) if !s.wide => {
4566 writeln!(out, "{indent} zd_seq.push_back(::dds::topic::xcdr2::read_string_origin(zd_buf, zd_pos, zd_len, {origin}, zd_max_align, zd_be));").map_err(fmt_err)?;
4567 }
4568 TypeSpec::String(_) => {
4570 writeln!(out, "{indent} zd_seq.push_back(::dds::topic::xcdr2::read_wstring_origin(zd_buf, zd_pos, zd_len, {origin}, zd_max_align, zd_be));").map_err(fmt_err)?;
4571 }
4572 TypeSpec::Scoped(s) if scoped_is_enum(s) => {
4574 let cpp_ty = scoped_to_cpp(s);
4575 let ec = enum_wire_ctype(scoped_enum_bytes(s));
4576 writeln!(out, "{indent} zd_seq.push_back(static_cast<{cpp_ty}>(::dds::topic::xcdr2::read_le_origin<{ec}>(zd_buf, zd_pos, zd_len, {origin}, zd_max_align, zd_be)));").map_err(fmt_err)?;
4577 }
4578 TypeSpec::Scoped(sc) if scoped_final_struct(sc).is_some() => {
4581 if let Some(def) = scoped_final_struct(sc) {
4582 let cpp_ty = scoped_to_cpp(sc);
4583 let var = format!("zd_se{}", next_nest_id());
4584 let binner = format!("{indent} ");
4585 writeln!(out, "{binner}{cpp_ty} {var}{{}};").map_err(fmt_err)?;
4586 for sm in &def.members {
4587 let sm_name = &sm.declarators[0].name().text;
4588 emit_value_read(
4589 out,
4590 &sm.type_spec,
4591 &format!("{var}.{sm_name}"),
4592 origin,
4593 &binner,
4594 false,
4595 )?;
4596 }
4597 writeln!(out, "{binner}zd_seq.push_back({var});").map_err(fmt_err)?;
4598 }
4599 }
4600 TypeSpec::Scoped(sc) if scoped_struct(sc).is_some() => {
4604 let cpp_ty = scoped_to_cpp(sc);
4605 let id = next_nest_id();
4606 let var = format!("zd_se{id}");
4607 let binner = format!("{indent} ");
4608 writeln!(
4609 out,
4610 "{binner}::dds::topic::xcdr2::skip_pad_from_origin(zd_pos, {origin}, 4);"
4611 )
4612 .map_err(fmt_err)?;
4613 emit_nested_span_decode(out, &cpp_ty, &var, id, &binner, true)?;
4614 writeln!(out, "{binner}zd_seq.push_back(std::move({var}));")
4615 .map_err(fmt_err)?;
4616 }
4617 TypeSpec::Scoped(sc) if scoped_union(sc).is_some() => {
4623 let Some(u) = scoped_union(sc) else {
4624 return Ok(());
4625 };
4626 let cpp_ty = scoped_to_cpp(sc);
4627 let id = next_nest_id();
4628 let var = format!("zd_se{id}");
4629 let binner = format!("{indent} ");
4630 match union_extensibility(&u.annotations) {
4631 Extensibility::Final => {
4635 let disc_ts = switch_type_spec(&u.switch_type);
4636 let disc_cpp = switch_type_to_cpp(&u.switch_type)?;
4637 writeln!(out, "{binner}{cpp_ty} {var};").map_err(fmt_err)?;
4638 writeln!(out, "{binner}{disc_cpp} zd_disc{id}{{}};")
4639 .map_err(fmt_err)?;
4640 emit_value_read(
4641 out,
4642 &disc_ts,
4643 &format!("zd_disc{id} ="),
4644 origin,
4645 &binner,
4646 false,
4647 )?;
4648 writeln!(out, "{binner}{var}._d(zd_disc{id});").map_err(fmt_err)?;
4649 emit_union_branch_switch_at(
4650 out, &u, &disc_cpp, true, "le", &var, origin,
4651 )?;
4652 writeln!(out, "{binner}zd_seq.push_back(std::move({var}));")
4653 .map_err(fmt_err)?;
4654 }
4655 Extensibility::Appendable | Extensibility::Mutable => {
4658 writeln!(
4659 out,
4660 "{binner}::dds::topic::xcdr2::skip_pad_from_origin(zd_pos, {origin}, 4);"
4661 )
4662 .map_err(fmt_err)?;
4663 emit_nested_span_decode(out, &cpp_ty, &var, id, &binner, true)?;
4664 writeln!(out, "{binner}zd_seq.push_back(std::move({var}));")
4665 .map_err(fmt_err)?;
4666 }
4667 }
4668 }
4669 TypeSpec::Sequence(_) => {
4672 let inner_ty = typespec_to_cpp(&seq.elem)?;
4673 let var = format!("zd_se{}", next_nest_id());
4674 let binner = format!("{indent} ");
4675 writeln!(out, "{binner}{inner_ty} {var}{{}};").map_err(fmt_err)?;
4676 emit_value_read(out, &seq.elem, &format!("{var} ="), origin, &binner, false)?;
4677 writeln!(out, "{binner}zd_seq.push_back(std::move({var}));")
4678 .map_err(fmt_err)?;
4679 }
4680 TypeSpec::Map(_) => {
4682 let inner_ty = typespec_to_cpp(&seq.elem)?;
4683 let var = format!("zd_se{}", next_nest_id());
4684 let binner = format!("{indent} ");
4685 writeln!(out, "{binner}{inner_ty} {var}{{}};").map_err(fmt_err)?;
4686 emit_value_read(out, &seq.elem, &format!("{var} ="), origin, &binner, false)?;
4687 writeln!(out, "{binner}zd_seq.push_back(std::move({var}));")
4688 .map_err(fmt_err)?;
4689 }
4690 _ => {}
4691 }
4692 writeln!(out, "{indent} }}").map_err(fmt_err)?;
4693 writeln!(out, "{indent} {setter}(std::move(zd_seq));").map_err(fmt_err)?;
4694 writeln!(out, "{indent}}}").map_err(fmt_err)?;
4695 }
4696 TypeSpec::Map(m) => {
4702 let k_ty = typespec_to_cpp(&m.key)?;
4703 let v_ty = typespec_to_cpp(&m.value)?;
4704 let id = next_nest_id();
4705 let mapv = format!("zd_map{id}");
4706 let kv = format!("zd_mk{id}");
4707 let vv = format!("zd_mv{id}");
4708 let inner = format!("{indent} ");
4709 let li = format!("{inner} ");
4710 writeln!(out, "{indent}{{").map_err(fmt_err)?;
4711 writeln!(
4714 out,
4715 "{inner}::dds::topic::xcdr2::skip_pad_from_origin(zd_pos, {origin}, 4);"
4716 )
4717 .map_err(fmt_err)?;
4718 if !map_pair_is_primitive(&m.key, &m.value) {
4719 writeln!(out, "{inner}const auto zd_map_dh = ::dds::topic::xcdr2::dheader_read_r(zd_buf, zd_pos, zd_len, zd_be, zd_repr); (void)zd_map_dh;").map_err(fmt_err)?;
4720 }
4721 writeln!(out, "{inner}auto zd_mcnt = ::dds::topic::xcdr2::read_le_origin<uint32_t>(zd_buf, zd_pos, zd_len, {origin}, zd_max_align, zd_be);").map_err(fmt_err)?;
4722 writeln!(out, "{inner}std::map<{k_ty}, {v_ty}> {mapv};").map_err(fmt_err)?;
4723 writeln!(
4724 out,
4725 "{inner}for (uint32_t zd_i = 0; zd_i < zd_mcnt; ++zd_i) {{"
4726 )
4727 .map_err(fmt_err)?;
4728 writeln!(out, "{li}{k_ty} {kv}{{}};").map_err(fmt_err)?;
4729 writeln!(out, "{li}{v_ty} {vv}{{}};").map_err(fmt_err)?;
4730 emit_value_read(out, &m.key, &format!("{kv} ="), origin, &li, false)?;
4731 emit_value_read(out, &m.value, &format!("{vv} ="), origin, &li, false)?;
4732 writeln!(out, "{li}{mapv}.emplace(std::move({kv}), std::move({vv}));")
4733 .map_err(fmt_err)?;
4734 writeln!(out, "{inner}}}").map_err(fmt_err)?;
4735 writeln!(out, "{inner}{setter}(std::move({mapv}));").map_err(fmt_err)?;
4736 writeln!(out, "{indent}}}").map_err(fmt_err)?;
4737 }
4738 TypeSpec::Scoped(s) if scoped_bitholder(s).is_some() => {
4742 let bytes = scoped_bitholder(s).unwrap_or(1);
4743 let holder = holder_uint_for_bytes(bytes);
4744 let cpp_ty = scoped_to_cpp(s);
4745 let read = format!(
4746 "::dds::topic::xcdr2::read_le_origin<{holder}>(zd_buf, zd_pos, zd_len, {origin}, zd_max_align, zd_be)"
4747 );
4748 let built = if scoped_is_bitset(s) {
4749 format!("{cpp_ty}{{ static_cast<uint64_t>({read}) }}")
4750 } else {
4751 format!("static_cast<{cpp_ty}>({read})")
4752 };
4753 writeln!(out, "{indent}{setter}({});", wrap_opt(built)).map_err(fmt_err)?;
4754 }
4755 TypeSpec::Scoped(s) if scoped_is_enum(s) => {
4757 let cpp_ty = scoped_to_cpp(s);
4758 let ec = enum_wire_ctype(scoped_enum_bytes(s));
4759 writeln!(
4760 out,
4761 "{indent}{setter}(static_cast<{cpp_ty}>(::dds::topic::xcdr2::read_le_origin<{ec}>(zd_buf, zd_pos, zd_len, {origin}, zd_max_align, zd_be)));"
4762 )
4763 .map_err(fmt_err)?;
4764 }
4765 TypeSpec::Scoped(sc) if scoped_struct(sc).is_some() => {
4770 let Some((def, ext)) = scoped_struct(sc) else {
4771 return Ok(());
4772 };
4773 let cpp_ty = scoped_to_cpp(sc);
4774 let id = next_nest_id();
4775 let var = format!("zd_ns{id}");
4776 let inner = format!("{indent} ");
4777 writeln!(out, "{indent}{{").map_err(fmt_err)?;
4778 writeln!(out, "{inner}{cpp_ty} {var}{{}};").map_err(fmt_err)?;
4779 match ext {
4780 Extensibility::Final => {
4781 for sm in &def.members {
4782 let sm_name = &sm.declarators[0].name().text;
4783 emit_value_read(
4784 out,
4785 &sm.type_spec,
4786 &format!("{var}.{sm_name}"),
4787 origin,
4788 &inner,
4789 false,
4790 )?;
4791 }
4792 }
4793 Extensibility::Appendable | Extensibility::Mutable => {
4794 writeln!(
4795 out,
4796 "{inner}::dds::topic::xcdr2::skip_pad_from_origin(zd_pos, {origin}, 4);"
4797 )
4798 .map_err(fmt_err)?;
4799 emit_nested_span_decode(out, &cpp_ty, &var, id, &inner, false)?;
4800 }
4801 }
4802 writeln!(out, "{inner}{setter}({var});").map_err(fmt_err)?;
4803 writeln!(out, "{indent}}}").map_err(fmt_err)?;
4804 }
4805 TypeSpec::Scoped(sc) if scoped_union(sc).is_some() => {
4809 let Some(u) = scoped_union(sc) else {
4810 return Ok(());
4811 };
4812 let cpp_ty = scoped_to_cpp(sc);
4813 let id = next_nest_id();
4814 let var = format!("zd_nu{id}");
4815 let inner = format!("{indent} ");
4816 writeln!(out, "{indent}{{").map_err(fmt_err)?;
4817 match union_extensibility(&u.annotations) {
4818 Extensibility::Final => {
4823 let disc_ts = switch_type_spec(&u.switch_type);
4824 let disc_cpp = switch_type_to_cpp(&u.switch_type)?;
4825 writeln!(out, "{inner}{cpp_ty} {var};").map_err(fmt_err)?;
4826 writeln!(out, "{inner}{disc_cpp} zd_disc{id}{{}};").map_err(fmt_err)?;
4827 emit_value_read(
4828 out,
4829 &disc_ts,
4830 &format!("zd_disc{id} ="),
4831 origin,
4832 &inner,
4833 false,
4834 )?;
4835 writeln!(out, "{inner}{var}._d(zd_disc{id});").map_err(fmt_err)?;
4836 emit_union_branch_switch_at(
4837 out, &u, &disc_cpp, true, "le", &var, origin,
4838 )?;
4839 writeln!(out, "{inner}{setter}({var});").map_err(fmt_err)?;
4840 }
4841 Extensibility::Appendable | Extensibility::Mutable => {
4844 writeln!(
4845 out,
4846 "{inner}::dds::topic::xcdr2::skip_pad_from_origin(zd_pos, {origin}, 4);"
4847 )
4848 .map_err(fmt_err)?;
4849 emit_nested_span_decode(out, &cpp_ty, &var, id, &inner, true)?;
4850 writeln!(out, "{inner}{setter}({var});").map_err(fmt_err)?;
4851 }
4852 }
4853 writeln!(out, "{indent}}}").map_err(fmt_err)?;
4854 }
4855 TypeSpec::Fixed(_) => {
4856 let cpp_ty = typespec_to_cpp(ts)?;
4859 writeln!(out, "{indent}{{").map_err(fmt_err)?;
4860 writeln!(
4861 out,
4862 "{indent} ::dds::topic::xcdr2::check_avail(zd_pos, {cpp_ty}::kByteCount, zd_len);"
4863 )
4864 .map_err(fmt_err)?;
4865 writeln!(out, "{indent} {cpp_ty} zd_fx(zd_buf + zd_pos);").map_err(fmt_err)?;
4866 writeln!(out, "{indent} zd_pos += {cpp_ty}::kByteCount;").map_err(fmt_err)?;
4867 writeln!(out, "{indent} {setter}(std::move(zd_fx));").map_err(fmt_err)?;
4868 writeln!(out, "{indent}}}").map_err(fmt_err)?;
4869 }
4870 _ => {}
4871 }
4872 Ok(())
4873}
4874
4875fn emit_pl_cdr1_member_decode_case(
4882 out: &mut String,
4883 m: &Member,
4884 next_id: &mut u32,
4885) -> Result<(), CppGenError> {
4886 let m = &normalize_member(m);
4887 if !member_codegen_supported(m) {
4888 for _ in &m.declarators {
4889 *next_id += 1;
4890 }
4891 return Ok(());
4892 }
4893 let id_override = find_uint_annotation(&m.annotations, "id");
4894 let is_optional = has_optional_annotation(&m.annotations);
4895 for (idx, decl) in m.declarators.iter().enumerate() {
4896 let name = &decl.name().text;
4897 let this_id = match id_override {
4898 Some(id) => id + idx as u32,
4899 None => *next_id,
4900 };
4901 *next_id = this_id + 1;
4902 if !matches!(decl, Declarator::Simple(_)) {
4903 continue;
4904 }
4905 if !typespec_supported(&m.type_spec) {
4906 continue;
4907 }
4908 writeln!(out, " case 0x{this_id:x}u: {{").map_err(fmt_err)?;
4909 emit_value_read(
4910 out,
4911 &m.type_spec,
4912 &format!("zd_v.{name}"),
4913 "zd_pl_origin",
4914 " ",
4915 is_optional,
4916 )?;
4917 writeln!(out, " break;").map_err(fmt_err)?;
4918 writeln!(out, " }}").map_err(fmt_err)?;
4919 }
4920 Ok(())
4921}
4922
4923fn emit_nested_span_decode(
4933 out: &mut String,
4934 cpp_ty: &str,
4935 var: &str,
4936 id: u32,
4937 indent: &str,
4938 declare_var: bool,
4939) -> Result<(), CppGenError> {
4940 writeln!(out, "{indent}const size_t zd_nss{id} = zd_pos;").map_err(fmt_err)?;
4941 if declare_var {
4942 writeln!(out, "{indent}{cpp_ty} {var};").map_err(fmt_err)?;
4943 }
4944 writeln!(
4945 out,
4946 "{indent}if (zd_repr == ::dds::topic::xcdr2::XcdrVersion::Xcdr1) {{"
4947 )
4948 .map_err(fmt_err)?;
4949 writeln!(
4950 out,
4951 "{indent} {var} = ::dds::topic::topic_type_support<{cpp_ty}>::decode(zd_buf + zd_nss{id}, zd_len - zd_nss{id}, zd_repr, zd_be);"
4952 )
4953 .map_err(fmt_err)?;
4954 writeln!(
4955 out,
4956 "{indent} zd_pos = zd_nss{id} + ::dds::topic::topic_type_support<{cpp_ty}>::encode({var}, zd_repr).size();"
4957 )
4958 .map_err(fmt_err)?;
4959 writeln!(out, "{indent}}} else {{").map_err(fmt_err)?;
4960 writeln!(out, "{indent} size_t zd_npk{id} = zd_pos;").map_err(fmt_err)?;
4961 writeln!(
4962 out,
4963 "{indent} const uint32_t zd_nl{id} = ::dds::topic::xcdr2::dheader_read(zd_buf, zd_npk{id}, zd_len, zd_be);"
4964 )
4965 .map_err(fmt_err)?;
4966 writeln!(
4967 out,
4968 "{indent} {var} = ::dds::topic::topic_type_support<{cpp_ty}>::decode(zd_buf + zd_nss{id}, 4u + zd_nl{id}, zd_repr, zd_be);"
4969 )
4970 .map_err(fmt_err)?;
4971 writeln!(out, "{indent} zd_pos = zd_nss{id} + 4u + zd_nl{id};").map_err(fmt_err)?;
4972 writeln!(out, "{indent}}}").map_err(fmt_err)?;
4973 Ok(())
4974}
4975
4976fn emit_mutable_member_decode_case(
4977 out: &mut String,
4978 m: &Member,
4979 next_id: &mut u32,
4980) -> Result<(), CppGenError> {
4981 let m = &normalize_member(m);
4982 if !member_codegen_supported(m) {
4983 for _ in &m.declarators {
4986 *next_id += 1;
4987 }
4988 return Ok(());
4989 }
4990 let id_override = find_uint_annotation(&m.annotations, "id");
4991 let is_optional = has_optional_annotation(&m.annotations);
4992 let _ = is_optional; for (idx, decl) in m.declarators.iter().enumerate() {
4994 let name = &decl.name().text;
4995 let this_id = match id_override {
4996 Some(id) => id + idx as u32,
4997 None => *next_id,
4998 };
4999 *next_id = this_id + 1;
5000 if !matches!(decl, Declarator::Simple(_)) {
5001 continue;
5002 }
5003 if !typespec_supported(&m.type_spec) {
5004 continue;
5005 }
5006 let id_expr = format!("0x{this_id:x}u");
5007 writeln!(out, " case {id_expr}: {{").map_err(fmt_err)?;
5008 match &m.type_spec {
5009 TypeSpec::Primitive(PrimitiveType::Boolean) => {
5010 writeln!(out, " uint8_t zd_b = ::dds::topic::xcdr2::read_u8(zd_buf, zd_pos, zd_len);").map_err(fmt_err)?;
5012 writeln!(
5013 out,
5014 " zd_v.{name}(static_cast<bool>(zd_b));"
5015 )
5016 .map_err(fmt_err)?;
5017 }
5018 TypeSpec::Primitive(PrimitiveType::Octet) => {
5019 writeln!(out, " zd_v.{name}(::dds::topic::xcdr2::read_u8(zd_buf, zd_pos, zd_len));").map_err(fmt_err)?;
5020 }
5021 TypeSpec::Primitive(p) => {
5022 let cpp_ty = primitive_to_cpp(*p);
5026 writeln!(out, " zd_v.{name}(::dds::topic::xcdr2::read_le_raw<{cpp_ty}>(zd_buf, zd_pos, zd_len, zd_be));").map_err(fmt_err)?;
5027 }
5028 TypeSpec::String(s) if !s.wide => {
5029 writeln!(out, " auto zd_body_origin = zd_pos;")
5033 .map_err(fmt_err)?;
5034 writeln!(out, " zd_v.{name}(::dds::topic::xcdr2::read_string_origin(zd_buf, zd_pos, zd_len, zd_body_origin, zd_max_align, zd_be));").map_err(fmt_err)?;
5035 }
5036 TypeSpec::String(s) if s.wide => {
5037 writeln!(out, " auto zd_body_origin = zd_pos;")
5039 .map_err(fmt_err)?;
5040 writeln!(out, " zd_v.{name}(::dds::topic::xcdr2::read_wstring_origin(zd_buf, zd_pos, zd_len, zd_body_origin, zd_max_align, zd_be));").map_err(fmt_err)?;
5041 }
5042 TypeSpec::Sequence(seq) => {
5043 if matches!(&*seq.elem, TypeSpec::Primitive(_)) {
5048 writeln!(out, " auto zd_n = ::dds::topic::xcdr2::emheader_nextint_read(zd_buf, zd_pos, zd_len, zd_be);").map_err(fmt_err)?;
5049 writeln!(out, " (void)zd_n;").map_err(fmt_err)?;
5050 }
5051 writeln!(out, " auto zd_body_origin = zd_pos;")
5052 .map_err(fmt_err)?;
5053 if !matches!(&*seq.elem, TypeSpec::Primitive(_)) {
5056 writeln!(out, " {{ const auto zd_seq_dh = ::dds::topic::xcdr2::dheader_read_r(zd_buf, zd_pos, zd_len, zd_be, zd_repr); (void)zd_seq_dh; }}").map_err(fmt_err)?;
5057 }
5058 writeln!(out, " auto zd_cnt = ::dds::topic::xcdr2::read_le_origin<uint32_t>(zd_buf, zd_pos, zd_len, zd_body_origin, zd_max_align, zd_be);").map_err(fmt_err)?;
5059 if matches!(&*seq.elem, TypeSpec::Primitive(PrimitiveType::Octet)) {
5060 writeln!(
5062 out,
5063 " ::dds::topic::xcdr2::check_avail(zd_pos, zd_cnt, zd_len);"
5064 )
5065 .map_err(fmt_err)?;
5066 writeln!(out, " std::vector<uint8_t> zd_seq(zd_buf + zd_pos, zd_buf + zd_pos + zd_cnt);").map_err(fmt_err)?;
5067 writeln!(out, " zd_pos += zd_cnt;").map_err(fmt_err)?;
5068 writeln!(out, " zd_v.{name}(std::move(zd_seq));")
5069 .map_err(fmt_err)?;
5070 } else {
5071 let elem_cpp_ty: String = match &*seq.elem {
5072 TypeSpec::Primitive(PrimitiveType::Boolean) => "bool".to_string(),
5073 TypeSpec::Primitive(p) => primitive_to_cpp(*p).to_string(),
5074 TypeSpec::String(s) if !s.wide => "std::string".to_string(),
5075 TypeSpec::String(_) => "std::wstring".to_string(),
5076 TypeSpec::Scoped(s) if scoped_is_enum(s) => scoped_to_cpp(s),
5077 TypeSpec::Scoped(s) if scoped_struct(s).is_some() => scoped_to_cpp(s),
5078 TypeSpec::Sequence(_) | TypeSpec::Map(_) => typespec_to_cpp(&seq.elem)?,
5079 _ => "uint8_t".to_string(),
5080 };
5081 writeln!(
5082 out,
5083 " std::vector<{elem_cpp_ty}> zd_seq;"
5084 )
5085 .map_err(fmt_err)?;
5086 writeln!(out, " zd_seq.reserve(zd_cnt);")
5087 .map_err(fmt_err)?;
5088 writeln!(
5089 out,
5090 " for (uint32_t zd_i = 0; zd_i < zd_cnt; ++zd_i) {{"
5091 )
5092 .map_err(fmt_err)?;
5093 match &*seq.elem {
5094 TypeSpec::Primitive(PrimitiveType::Boolean) => {
5095 writeln!(out, " zd_seq.push_back(::dds::topic::xcdr2::read_bool(zd_buf, zd_pos, zd_len));").map_err(fmt_err)?;
5096 }
5097 TypeSpec::Primitive(p) => {
5098 let cpp_ty = primitive_to_cpp(*p);
5099 writeln!(out, " zd_seq.push_back(::dds::topic::xcdr2::read_le_origin<{cpp_ty}>(zd_buf, zd_pos, zd_len, zd_body_origin, zd_max_align, zd_be));").map_err(fmt_err)?;
5100 }
5101 TypeSpec::String(s) if !s.wide => {
5102 writeln!(out, " zd_seq.push_back(::dds::topic::xcdr2::read_string_origin(zd_buf, zd_pos, zd_len, zd_body_origin, zd_max_align, zd_be));").map_err(fmt_err)?;
5103 }
5104 TypeSpec::String(_) => {
5105 writeln!(out, " zd_seq.push_back(::dds::topic::xcdr2::read_wstring_origin(zd_buf, zd_pos, zd_len, zd_body_origin, zd_max_align, zd_be));").map_err(fmt_err)?;
5106 }
5107 TypeSpec::Scoped(s) if scoped_is_enum(s) => {
5108 let cpp_ty = scoped_to_cpp(s);
5109 let ec = enum_wire_ctype(scoped_enum_bytes(s));
5110 writeln!(out, " zd_seq.push_back(static_cast<{cpp_ty}>(::dds::topic::xcdr2::read_le_origin<{ec}>(zd_buf, zd_pos, zd_len, zd_body_origin, zd_max_align, zd_be)));").map_err(fmt_err)?;
5111 }
5112 TypeSpec::Scoped(sc) if scoped_final_struct(sc).is_some() => {
5113 if let Some(def) = scoped_final_struct(sc) {
5114 let cpp_ty = scoped_to_cpp(sc);
5115 let var = format!("zd_se{}", next_nest_id());
5116 writeln!(out, " {cpp_ty} {var}{{}};")
5117 .map_err(fmt_err)?;
5118 for sm in &def.members {
5119 let sm_name = &sm.declarators[0].name().text;
5120 emit_value_read(
5121 out,
5122 &sm.type_spec,
5123 &format!("{var}.{sm_name}"),
5124 "zd_body_origin",
5125 " ",
5126 false,
5127 )?;
5128 }
5129 writeln!(out, " zd_seq.push_back({var});")
5130 .map_err(fmt_err)?;
5131 }
5132 }
5133 TypeSpec::Scoped(sc) if scoped_struct(sc).is_some() => {
5137 let cpp_ty = scoped_to_cpp(sc);
5138 let id = next_nest_id();
5139 let var = format!("zd_se{id}");
5140 writeln!(out, " ::dds::topic::xcdr2::skip_pad_from_origin(zd_pos, zd_body_origin, 4);").map_err(fmt_err)?;
5141 writeln!(
5142 out,
5143 " const size_t zd_nss{id} = zd_pos;"
5144 )
5145 .map_err(fmt_err)?;
5146 writeln!(out, " size_t zd_npk{id} = zd_pos;")
5147 .map_err(fmt_err)?;
5148 writeln!(out, " const uint32_t zd_nl{id} = ::dds::topic::xcdr2::dheader_read(zd_buf, zd_npk{id}, zd_len, zd_be);").map_err(fmt_err)?;
5149 writeln!(out, " {cpp_ty} {var} = ::dds::topic::topic_type_support<{cpp_ty}>::decode(zd_buf + zd_nss{id}, 4u + zd_nl{id}, zd_repr, zd_be);").map_err(fmt_err)?;
5150 writeln!(
5151 out,
5152 " zd_pos = zd_nss{id} + 4u + zd_nl{id};"
5153 )
5154 .map_err(fmt_err)?;
5155 writeln!(
5156 out,
5157 " zd_seq.push_back(std::move({var}));"
5158 )
5159 .map_err(fmt_err)?;
5160 }
5161 TypeSpec::Sequence(_) | TypeSpec::Map(_) => {
5163 let inner_ty = typespec_to_cpp(&seq.elem)?;
5164 let var = format!("zd_se{}", next_nest_id());
5165 writeln!(out, " {inner_ty} {var}{{}};")
5166 .map_err(fmt_err)?;
5167 emit_value_read(
5168 out,
5169 &seq.elem,
5170 &format!("{var} ="),
5171 "zd_body_origin",
5172 " ",
5173 false,
5174 )?;
5175 writeln!(
5176 out,
5177 " zd_seq.push_back(std::move({var}));"
5178 )
5179 .map_err(fmt_err)?;
5180 }
5181 _ => {}
5182 }
5183 writeln!(out, " }}").map_err(fmt_err)?;
5184 writeln!(out, " zd_v.{name}(std::move(zd_seq));")
5185 .map_err(fmt_err)?;
5186 }
5187 }
5188 TypeSpec::Scoped(s) if scoped_is_enum(s) => {
5190 let cpp_ty = scoped_to_cpp(s);
5191 let ec = enum_wire_ctype(scoped_enum_bytes(s));
5192 writeln!(out, " zd_v.{name}(static_cast<{cpp_ty}>(::dds::topic::xcdr2::read_le_raw<{ec}>(zd_buf, zd_pos, zd_len, zd_be)));").map_err(fmt_err)?;
5193 }
5194 TypeSpec::Scoped(sc) if scoped_struct(sc).is_some() => {
5200 if let Some((def, ext)) = scoped_struct(sc) {
5201 let cpp_ty = scoped_to_cpp(sc);
5202 let id = next_nest_id();
5203 let var = format!("zd_ns{id}");
5204 if matches!(ext, Extensibility::Final) {
5205 writeln!(out, " auto zd_n = ::dds::topic::xcdr2::emheader_nextint_read(zd_buf, zd_pos, zd_len, zd_be); (void)zd_n;").map_err(fmt_err)?;
5206 }
5207 writeln!(
5208 out,
5209 " auto zd_body_origin = zd_pos; (void)zd_body_origin;"
5210 )
5211 .map_err(fmt_err)?;
5212 writeln!(out, " {cpp_ty} {var}{{}};").map_err(fmt_err)?;
5213 match ext {
5214 Extensibility::Final => {
5215 for sm in &def.members {
5216 let sm_name = &sm.declarators[0].name().text;
5217 emit_value_read(
5218 out,
5219 &sm.type_spec,
5220 &format!("{var}.{sm_name}"),
5221 "zd_body_origin",
5222 " ",
5223 false,
5224 )?;
5225 }
5226 }
5227 Extensibility::Appendable | Extensibility::Mutable => {
5228 writeln!(out, " const size_t zd_nss{id} = zd_pos;")
5229 .map_err(fmt_err)?;
5230 writeln!(out, " size_t zd_npk{id} = zd_pos;")
5231 .map_err(fmt_err)?;
5232 writeln!(out, " const uint32_t zd_nl{id} = ::dds::topic::xcdr2::dheader_read(zd_buf, zd_npk{id}, zd_len, zd_be);").map_err(fmt_err)?;
5233 writeln!(out, " {var} = ::dds::topic::topic_type_support<{cpp_ty}>::decode(zd_buf + zd_nss{id}, 4u + zd_nl{id}, zd_repr, zd_be);").map_err(fmt_err)?;
5234 writeln!(
5235 out,
5236 " zd_pos = zd_nss{id} + 4u + zd_nl{id};"
5237 )
5238 .map_err(fmt_err)?;
5239 }
5240 }
5241 writeln!(out, " zd_v.{name}({var});").map_err(fmt_err)?;
5242 }
5243 }
5244 TypeSpec::Map(m) => {
5249 let k_ty = typespec_to_cpp(&m.key)?;
5250 let v_ty = typespec_to_cpp(&m.value)?;
5251 let id = next_nest_id();
5252 let mapv = format!("zd_map{id}");
5253 let kv = format!("zd_mk{id}");
5254 let vv = format!("zd_mv{id}");
5255 writeln!(out, " auto zd_body_origin = zd_pos;")
5256 .map_err(fmt_err)?;
5257 writeln!(out, " {{ const auto zd_map_dh = ::dds::topic::xcdr2::dheader_read_r(zd_buf, zd_pos, zd_len, zd_be, zd_repr); (void)zd_map_dh; }}").map_err(fmt_err)?;
5263 writeln!(out, " auto zd_mcnt = ::dds::topic::xcdr2::read_le_origin<uint32_t>(zd_buf, zd_pos, zd_len, zd_body_origin, zd_max_align, zd_be);").map_err(fmt_err)?;
5264 writeln!(out, " std::map<{k_ty}, {v_ty}> {mapv};")
5265 .map_err(fmt_err)?;
5266 writeln!(
5267 out,
5268 " for (uint32_t zd_i = 0; zd_i < zd_mcnt; ++zd_i) {{"
5269 )
5270 .map_err(fmt_err)?;
5271 writeln!(out, " {k_ty} {kv}{{}};").map_err(fmt_err)?;
5272 writeln!(out, " {v_ty} {vv}{{}};").map_err(fmt_err)?;
5273 emit_value_read(
5274 out,
5275 &m.key,
5276 &format!("{kv} ="),
5277 "zd_body_origin",
5278 " ",
5279 false,
5280 )?;
5281 emit_value_read(
5282 out,
5283 &m.value,
5284 &format!("{vv} ="),
5285 "zd_body_origin",
5286 " ",
5287 false,
5288 )?;
5289 writeln!(
5290 out,
5291 " {mapv}.emplace(std::move({kv}), std::move({vv}));"
5292 )
5293 .map_err(fmt_err)?;
5294 writeln!(out, " }}").map_err(fmt_err)?;
5295 writeln!(out, " zd_v.{name}(std::move({mapv}));")
5296 .map_err(fmt_err)?;
5297 }
5298 TypeSpec::Fixed(_) => {
5299 let cpp_ty = typespec_to_cpp(&m.type_spec)?;
5303 writeln!(out, " {{ auto zd_n = ::dds::topic::xcdr2::emheader_nextint_read(zd_buf, zd_pos, zd_len, zd_be); (void)zd_n; }}").map_err(fmt_err)?;
5304 writeln!(out, " ::dds::topic::xcdr2::check_avail(zd_pos, {cpp_ty}::kByteCount, zd_len);").map_err(fmt_err)?;
5305 writeln!(out, " {{ {cpp_ty} zd_fx(zd_buf + zd_pos); zd_pos += {cpp_ty}::kByteCount; zd_v.{name}(std::move(zd_fx)); }}").map_err(fmt_err)?;
5306 }
5307 _ => {}
5308 }
5309 writeln!(out, " break;").map_err(fmt_err)?;
5310 writeln!(out, " }}").map_err(fmt_err)?;
5311 }
5312 Ok(())
5313}
5314
5315fn emit_key_hash_fn(
5316 out: &mut String,
5317 cpp_fqn: &str,
5318 s: &StructDef,
5319 is_keyed: bool,
5320 phase: TtsPhase,
5321) -> Result<(), CppGenError> {
5322 if phase == TtsPhase::Decl {
5323 writeln!(
5324 out,
5325 " static std::array<uint8_t, 16> key_hash(const {cpp_fqn}& zd_v);"
5326 )
5327 .map_err(fmt_err)?;
5328 return Ok(());
5329 }
5330 writeln!(
5331 out,
5332 "inline std::array<uint8_t, 16> topic_type_support<{cpp_fqn}>::key_hash(const {cpp_fqn}& zd_v) {{"
5333 )
5334 .map_err(fmt_err)?;
5335 writeln!(out, " (void)zd_v;").map_err(fmt_err)?;
5336 if !is_keyed {
5337 writeln!(
5338 out,
5339 " return std::array<uint8_t, 16>{{{{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}}};"
5340 )
5341 .map_err(fmt_err)?;
5342 writeln!(out, " }}").map_err(fmt_err)?;
5343 return Ok(());
5344 }
5345 writeln!(out, " std::vector<uint8_t> zd_out;").map_err(fmt_err)?;
5346 writeln!(out, " const size_t zd_origin = 0;").map_err(fmt_err)?;
5347 writeln!(out, " (void)zd_origin;").map_err(fmt_err)?;
5348 writeln!(
5353 out,
5354 " const size_t zd_max_align = ::dds::topic::xcdr2::xcdr_max_align(::dds::topic::xcdr2::XcdrVersion::Xcdr1);"
5355 )
5356 .map_err(fmt_err)?;
5357 writeln!(out, " (void)zd_max_align;").map_err(fmt_err)?;
5358 for m in &s.members {
5359 if !has_key_annotation(&m.annotations) {
5360 continue;
5361 }
5362 emit_plain_member_encode(out, m, "be", "zd_origin")?;
5363 }
5364 writeln!(out, " std::array<uint8_t, 16> zd_h{{}};").map_err(fmt_err)?;
5366 writeln!(out, " if (zd_out.size() <= 16) {{").map_err(fmt_err)?;
5367 writeln!(
5368 out,
5369 " std::memcpy(zd_h.data(), zd_out.data(), zd_out.size());"
5370 )
5371 .map_err(fmt_err)?;
5372 writeln!(out, " return zd_h;").map_err(fmt_err)?;
5373 writeln!(out, " }}").map_err(fmt_err)?;
5374 writeln!(out, " return ::dds::topic::xcdr2_md5::md5(zd_out);").map_err(fmt_err)?;
5375 writeln!(out, " }}").map_err(fmt_err)?;
5376 Ok(())
5377}
5378
5379fn fmt_err(_: core::fmt::Error) -> CppGenError {
5384 CppGenError::Internal("string formatting failed".into())
5385}
5386
5387#[allow(dead_code)]
5388fn _ensure_used() {
5389 let _ = is_reserved("int");
5391}