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 if !probe_structs.is_empty() {
125 writeln!(&mut out, "#include \"dds/topic/TopicTraits.hpp\"").map_err(fmt_err)?;
131 writeln!(&mut out, "#include \"dds/topic/xcdr2.hpp\"").map_err(fmt_err)?;
132 writeln!(&mut out, "#include \"dds/topic/xcdr2_md5.hpp\"").map_err(fmt_err)?;
133 writeln!(&mut out).map_err(fmt_err)?;
134 }
135
136 for d in &spec.definitions {
138 emit_definition(&mut out, &mut ctx, d)?;
139 }
140
141 for d in &spec.definitions {
144 if let Some(anns) = top_level_annotations(d) {
145 emit_verbatim_at(&mut out, "", anns, PlacementKind::EndFile)?;
146 }
147 }
148
149 if let Some(prefix) = outer_prefix {
151 ctx.close_namespace(&mut out, prefix)?;
152 }
153
154 if !probe_structs.is_empty() {
158 emit_topic_type_support_specs(&mut out, opts, &probe_structs)?;
159 }
160
161 Ok(out)
162}
163
164fn top_level_annotations(d: &Definition) -> Option<&[Annotation]> {
167 match d {
168 Definition::Module(m) => Some(&m.annotations),
169 Definition::Type(TypeDecl::Constr(c)) => match c {
170 ConstrTypeDecl::Struct(StructDcl::Def(s)) => Some(&s.annotations),
171 ConstrTypeDecl::Union(UnionDcl::Def(u)) => Some(&u.annotations),
172 ConstrTypeDecl::Enum(e) => Some(&e.annotations),
173 _ => None,
174 },
175 Definition::Type(TypeDecl::Typedef(t)) => Some(&t.annotations),
176 Definition::Const(c) => Some(&c.annotations),
177 Definition::Except(e) => Some(&e.annotations),
178 _ => None,
179 }
180}
181
182fn write_header_preamble(
184 out: &mut String,
185 opts: &CppGenOptions,
186 includes: &Includes,
187) -> Result<(), CppGenError> {
188 writeln!(out, "// Generated by zerodds idl-cpp. Do not edit.").map_err(fmt_err)?;
189 writeln!(out, "#pragma once").map_err(fmt_err)?;
190 if let Some(guard) = opts.include_guard_prefix.as_deref() {
191 if !guard.is_empty() {
192 writeln!(out, "// guard-prefix: {guard}").map_err(fmt_err)?;
195 }
196 }
197 writeln!(out).map_err(fmt_err)?;
198 for h in &includes.headers {
199 writeln!(out, "#include {h}").map_err(fmt_err)?;
200 }
201 writeln!(out).map_err(fmt_err)?;
202 Ok(())
203}
204
205fn collect_includes(spec: &Specification, inc: &mut Includes) {
207 for d in &spec.definitions {
208 collect_in_def(d, inc);
209 }
210}
211
212fn collect_in_def(d: &Definition, inc: &mut Includes) {
214 match d {
215 Definition::Module(m) => {
216 for sub in &m.definitions {
217 collect_in_def(sub, inc);
218 }
219 }
220 Definition::Type(td) => collect_in_typedecl(td, inc),
221 Definition::Const(_) => {}
222 Definition::Except(e) => {
223 inc.add("<exception>");
224 for m in &e.members {
225 collect_in_typespec(&m.type_spec, inc);
226 for decl in &m.declarators {
227 if matches!(decl, Declarator::Array(_)) {
228 inc.add("<array>");
229 }
230 }
231 }
232 }
233 Definition::Interface(_)
234 | Definition::ValueBox(_)
235 | Definition::ValueForward(_)
236 | Definition::ValueDef(_)
237 | Definition::TypeId(_)
238 | Definition::TypePrefix(_)
239 | Definition::Import(_)
240 | Definition::Component(_)
241 | Definition::Home(_)
242 | Definition::Event(_)
243 | Definition::Porttype(_)
244 | Definition::Connector(_)
245 | Definition::TemplateModule(_)
246 | Definition::TemplateModuleInst(_)
247 | Definition::Annotation(_)
248 | Definition::VendorExtension(_) => {
249 }
252 }
253}
254
255fn collect_in_typedecl(td: &TypeDecl, inc: &mut Includes) {
256 match td {
257 TypeDecl::Constr(c) => match c {
258 ConstrTypeDecl::Struct(StructDcl::Def(s)) => {
259 for m in &s.members {
260 collect_in_typespec(&m.type_spec, inc);
261 for decl in &m.declarators {
262 if matches!(decl, Declarator::Array(_)) {
263 inc.add("<array>");
264 }
265 }
266 if has_optional_annotation(&m.annotations) {
267 inc.add("<optional>");
268 }
269 if has_shared_annotation(&m.annotations) {
270 inc.add("<memory>");
271 }
272 }
273 }
274 ConstrTypeDecl::Struct(StructDcl::Forward(_)) => {}
275 ConstrTypeDecl::Union(UnionDcl::Def(u)) => {
276 inc.add("<variant>");
277 for c in &u.cases {
278 collect_in_typespec(&c.element.type_spec, inc);
279 if matches!(c.element.declarator, Declarator::Array(_)) {
280 inc.add("<array>");
281 }
282 }
283 }
284 ConstrTypeDecl::Union(UnionDcl::Forward(_)) => {}
285 ConstrTypeDecl::Enum(_) | ConstrTypeDecl::Bitset(_) | ConstrTypeDecl::Bitmask(_) => {}
286 },
287 TypeDecl::Typedef(t) => {
288 collect_in_typespec(&t.type_spec, inc);
289 for decl in &t.declarators {
290 if matches!(decl, Declarator::Array(_)) {
291 inc.add("<array>");
292 }
293 }
294 }
295 TypeDecl::Native(_) => {}
297 }
298}
299
300fn collect_in_typespec(ts: &TypeSpec, inc: &mut Includes) {
302 match ts {
303 TypeSpec::Primitive(_) => {}
304 TypeSpec::Scoped(_) => {}
305 TypeSpec::Sequence(s) => {
306 inc.add("<vector>");
307 collect_in_typespec(&s.elem, inc);
308 }
309 TypeSpec::String(_) => {
310 inc.add("<string>");
312 }
313 TypeSpec::Map(m) => {
314 inc.add("<map>");
315 collect_in_typespec(&m.key, inc);
316 collect_in_typespec(&m.value, inc);
317 }
318 TypeSpec::Fixed(_) => {
319 inc.add("<cstdint>");
323 }
324 TypeSpec::Any => {
325 inc.add("<cstdint>");
327 }
328 }
329}
330
331struct EmitCtx<'o> {
333 opts: &'o CppGenOptions,
334 indent_level: usize,
335}
336
337impl<'o> EmitCtx<'o> {
338 fn new(opts: &'o CppGenOptions) -> Self {
339 Self {
340 opts,
341 indent_level: 0,
342 }
343 }
344
345 fn indent(&self) -> String {
346 " ".repeat(self.indent_level * self.opts.indent_width)
347 }
348
349 fn open_namespace(&mut self, out: &mut String, name: &str) -> Result<(), CppGenError> {
350 writeln!(out, "{}namespace {name} {{", self.indent()).map_err(fmt_err)?;
351 self.indent_level += 1;
352 Ok(())
353 }
354
355 fn close_namespace(&mut self, out: &mut String, name: &str) -> Result<(), CppGenError> {
356 self.indent_level = self.indent_level.saturating_sub(1);
357 writeln!(out, "{}}} // namespace {name}", self.indent()).map_err(fmt_err)?;
358 Ok(())
359 }
360}
361
362fn emit_definition(
364 out: &mut String,
365 ctx: &mut EmitCtx<'_>,
366 def: &Definition,
367) -> Result<(), CppGenError> {
368 match def {
369 Definition::Module(m) => emit_module(out, ctx, m),
370 Definition::Type(td) => emit_type_decl(out, ctx, td),
371 Definition::Const(c) => emit_const_decl(out, ctx, c),
372 Definition::Except(e) => emit_exception(out, ctx, e),
373 Definition::Interface(InterfaceDcl::Def(iface)) => {
374 emit_interface_stub(out, ctx, iface)
379 }
380 Definition::Interface(InterfaceDcl::Forward(f)) => {
381 check_identifier(&f.name.text)?;
382 writeln!(out, "{}class {};", ctx.indent(), f.name.text).map_err(fmt_err)?;
383 Ok(())
384 }
385 Definition::ValueDef(v) => emit_value_type(out, ctx, v),
386 Definition::ValueBox(_) | Definition::ValueForward(_) => {
387 Ok(())
391 }
392 Definition::TypeId(_)
393 | Definition::TypePrefix(_)
394 | Definition::Import(_)
395 | Definition::Component(_)
396 | Definition::Home(_)
397 | Definition::Event(_)
398 | Definition::Porttype(_)
399 | Definition::Connector(_)
400 | Definition::TemplateModule(_)
401 | Definition::TemplateModuleInst(_) => Err(CppGenError::UnsupportedConstruct {
402 construct: "corba/ccm/template construct".into(),
403 context: None,
404 }),
405 Definition::Annotation(_) => {
406 Ok(())
410 }
411 Definition::VendorExtension(v) => Err(CppGenError::UnsupportedConstruct {
412 construct: format!("vendor-extension:{}", v.production_name),
413 context: None,
414 }),
415 }
416}
417
418fn emit_module(out: &mut String, ctx: &mut EmitCtx<'_>, m: &ModuleDef) -> Result<(), CppGenError> {
420 check_identifier(&m.name.text)?;
421 ctx.open_namespace(out, &m.name.text)?;
422 for d in &m.definitions {
423 emit_definition(out, ctx, d)?;
424 }
425 ctx.close_namespace(out, &m.name.text)?;
426 Ok(())
427}
428
429fn emit_type_decl(
430 out: &mut String,
431 ctx: &mut EmitCtx<'_>,
432 td: &TypeDecl,
433) -> Result<(), CppGenError> {
434 match td {
435 TypeDecl::Constr(c) => match c {
436 ConstrTypeDecl::Struct(StructDcl::Def(s)) => emit_struct(out, ctx, s),
437 ConstrTypeDecl::Struct(StructDcl::Forward(f)) => {
438 check_identifier(&f.name.text)?;
439 writeln!(out, "{}class {};", ctx.indent(), f.name.text).map_err(fmt_err)?;
440 Ok(())
441 }
442 ConstrTypeDecl::Union(UnionDcl::Def(u)) => emit_union(out, ctx, u),
443 ConstrTypeDecl::Union(UnionDcl::Forward(f)) => {
444 check_identifier(&f.name.text)?;
445 writeln!(out, "{}class {};", ctx.indent(), f.name.text).map_err(fmt_err)?;
446 Ok(())
447 }
448 ConstrTypeDecl::Enum(e) => emit_enum(out, ctx, e),
449 ConstrTypeDecl::Bitset(b) => {
450 check_identifier(&b.name.text)?;
451 let ind = ctx.indent();
452 let inner = " ".repeat((ctx.indent_level + 1) * ctx.opts.indent_width);
453 emit_bitset(out, &ind, &inner, b)
454 }
455 ConstrTypeDecl::Bitmask(b) => {
456 check_identifier(&b.name.text)?;
457 let ind = ctx.indent();
458 let inner = " ".repeat((ctx.indent_level + 1) * ctx.opts.indent_width);
459 emit_bitmask(out, &ind, &inner, b)
460 }
461 },
462 TypeDecl::Typedef(t) => emit_typedef(out, ctx, t),
463 TypeDecl::Native(_) => Ok(()),
466 }
467}
468
469fn emit_struct(out: &mut String, ctx: &mut EmitCtx<'_>, s: &StructDef) -> Result<(), CppGenError> {
470 check_identifier(&s.name.text)?;
471 let ind = ctx.indent();
472
473 emit_verbatim_at(out, &ind, &s.annotations, PlacementKind::BeforeDeclaration)?;
476
477 if let Some(base) = &s.base {
479 let base_str = scoped_to_cpp(base);
480 writeln!(out, "{ind}class {} : public {} {{", s.name.text, base_str).map_err(fmt_err)?;
481 } else {
482 writeln!(out, "{ind}class {} {{", s.name.text).map_err(fmt_err)?;
483 }
484 writeln!(out, "{ind}public:").map_err(fmt_err)?;
485
486 let inner = " ".repeat((ctx.indent_level + 1) * ctx.opts.indent_width);
487
488 emit_verbatim_at(out, &inner, &s.annotations, PlacementKind::BeginDeclaration)?;
491
492 writeln!(out, "{inner}{}() = default;", s.name.text).map_err(fmt_err)?;
494 writeln!(out, "{inner}~{}() = default;", s.name.text).map_err(fmt_err)?;
495 writeln!(out).map_err(fmt_err)?;
496
497 writeln!(out, "{ind}private:").map_err(fmt_err)?;
499 for m in &s.members {
500 emit_struct_member_field(out, ctx, m)?;
501 }
502 writeln!(out).map_err(fmt_err)?;
503
504 writeln!(out, "{ind}public:").map_err(fmt_err)?;
506 for m in &s.members {
507 emit_struct_member_accessors(out, ctx, m)?;
508 }
509
510 emit_verbatim_at(out, &inner, &s.annotations, PlacementKind::EndDeclaration)?;
513
514 writeln!(out, "{ind}}};").map_err(fmt_err)?;
515
516 emit_verbatim_at(out, &ind, &s.annotations, PlacementKind::AfterDeclaration)?;
519
520 writeln!(out).map_err(fmt_err)?;
521 Ok(())
522}
523
524fn emit_struct_member_field(
525 out: &mut String,
526 ctx: &EmitCtx<'_>,
527 m: &Member,
528) -> Result<(), CppGenError> {
529 let inner = " ".repeat((ctx.indent_level + 1) * ctx.opts.indent_width);
530 let optional = has_optional_annotation(&m.annotations);
531 let shared = has_shared_annotation(&m.annotations);
532 for decl in &m.declarators {
533 let cpp_ty = type_for_declarator(&m.type_spec, decl)?;
534 let name = decl.name();
535 check_identifier(&name.text)?;
536 let key_marker = if has_key_annotation(&m.annotations) {
537 " // @key"
538 } else {
539 ""
540 };
541 let core_ty = if shared {
544 format!("std::shared_ptr<{cpp_ty}>")
545 } else {
546 cpp_ty
547 };
548 if optional {
549 writeln!(
550 out,
551 "{inner}std::optional<{core_ty}> {}_;{key_marker}",
552 name.text
553 )
554 .map_err(fmt_err)?;
555 } else {
556 writeln!(out, "{inner}{core_ty} {}_;{key_marker}", name.text).map_err(fmt_err)?;
557 }
558 }
559 Ok(())
560}
561
562fn emit_struct_member_accessors(
563 out: &mut String,
564 ctx: &EmitCtx<'_>,
565 m: &Member,
566) -> Result<(), CppGenError> {
567 let inner = " ".repeat((ctx.indent_level + 1) * ctx.opts.indent_width);
568 let optional = has_optional_annotation(&m.annotations);
569 let shared = has_shared_annotation(&m.annotations);
570 for decl in &m.declarators {
571 let cpp_ty = type_for_declarator(&m.type_spec, decl)?;
572 let name = &decl.name().text;
573 let core_ty = if shared {
574 format!("std::shared_ptr<{cpp_ty}>")
575 } else {
576 cpp_ty.clone()
577 };
578 let storage_ty = if optional {
579 format!("std::optional<{core_ty}>")
580 } else {
581 core_ty
582 };
583 writeln!(out, "{inner}{storage_ty}& {name}() {{ return {name}_; }}").map_err(fmt_err)?;
584 writeln!(
585 out,
586 "{inner}const {storage_ty}& {name}() const {{ return {name}_; }}"
587 )
588 .map_err(fmt_err)?;
589 writeln!(
590 out,
591 "{inner}void {name}(const {storage_ty}& value) {{ {name}_ = value; }}"
592 )
593 .map_err(fmt_err)?;
594 }
595 Ok(())
596}
597
598fn emit_union(out: &mut String, ctx: &mut EmitCtx<'_>, u: &UnionDef) -> Result<(), CppGenError> {
599 check_identifier(&u.name.text)?;
600 let ind = ctx.indent();
601 emit_verbatim_at(out, &ind, &u.annotations, PlacementKind::BeforeDeclaration)?;
602 writeln!(out, "{ind}class {} {{", u.name.text).map_err(fmt_err)?;
603 writeln!(out, "{ind}public:").map_err(fmt_err)?;
604 let inner = " ".repeat((ctx.indent_level + 1) * ctx.opts.indent_width);
605 emit_verbatim_at(out, &inner, &u.annotations, PlacementKind::BeginDeclaration)?;
606
607 let disc_ty = switch_type_to_cpp(&u.switch_type)?;
608
609 let mut variant_types: Vec<String> = Vec::new();
611 for c in &u.cases {
612 let cpp_ty = type_for_declarator(&c.element.type_spec, &c.element.declarator)?;
613 if !variant_types.iter().any(|t| t == &cpp_ty) {
614 variant_types.push(cpp_ty);
615 }
616 }
617 let variant_str = if variant_types.is_empty() {
618 "std::monostate".to_string()
619 } else {
620 variant_types.join(", ")
621 };
622
623 writeln!(
624 out,
625 "{inner}using value_type = std::variant<{variant_str}>;"
626 )
627 .map_err(fmt_err)?;
628 writeln!(out, "{inner}{}() = default;", u.name.text).map_err(fmt_err)?;
629 writeln!(out, "{inner}~{}() = default;", u.name.text).map_err(fmt_err)?;
630 writeln!(out).map_err(fmt_err)?;
631
632 writeln!(
634 out,
635 "{inner}{disc_ty} _d() const {{ return discriminator_; }}"
636 )
637 .map_err(fmt_err)?;
638 writeln!(out, "{inner}void _d({disc_ty} d) {{ discriminator_ = d; }}").map_err(fmt_err)?;
639 writeln!(out, "{inner}value_type& value() {{ return value_; }}").map_err(fmt_err)?;
640 writeln!(
641 out,
642 "{inner}const value_type& value() const {{ return value_; }}"
643 )
644 .map_err(fmt_err)?;
645 writeln!(out).map_err(fmt_err)?;
646
647 let mut has_default = false;
649 for c in &u.cases {
650 emit_union_case_comment(out, &inner, c, &mut has_default)?;
651 }
652 if !has_default {
653 writeln!(out, "{inner}// no explicit 'default:' branch").map_err(fmt_err)?;
654 }
655
656 writeln!(out).map_err(fmt_err)?;
657 writeln!(out, "{ind}private:").map_err(fmt_err)?;
658 writeln!(out, "{inner}{disc_ty} discriminator_{{}};").map_err(fmt_err)?;
659 writeln!(out, "{inner}value_type value_{{}};").map_err(fmt_err)?;
660 emit_verbatim_at(out, &inner, &u.annotations, PlacementKind::EndDeclaration)?;
661 writeln!(out, "{ind}}};").map_err(fmt_err)?;
662 emit_verbatim_at(out, &ind, &u.annotations, PlacementKind::AfterDeclaration)?;
663 writeln!(out).map_err(fmt_err)?;
664 Ok(())
665}
666
667fn emit_union_case_comment(
668 out: &mut String,
669 inner: &str,
670 c: &Case,
671 has_default: &mut bool,
672) -> Result<(), CppGenError> {
673 for label in &c.labels {
674 match label {
675 CaseLabel::Default => {
676 *has_default = true;
677 writeln!(
678 out,
679 "{inner}// case default -> {}",
680 declarator_name(&c.element.declarator)
681 )
682 .map_err(fmt_err)?;
683 }
684 CaseLabel::Value(expr) => {
685 let val = const_expr_to_cpp(expr);
686 writeln!(
687 out,
688 "{inner}// case {val} -> {}",
689 declarator_name(&c.element.declarator)
690 )
691 .map_err(fmt_err)?;
692 }
693 }
694 }
695 Ok(())
696}
697
698pub(crate) fn declarator_name(d: &Declarator) -> &str {
699 &d.name().text
700}
701
702fn emit_enum(out: &mut String, ctx: &mut EmitCtx<'_>, e: &EnumDef) -> Result<(), CppGenError> {
703 check_identifier(&e.name.text)?;
704 let ind = ctx.indent();
705 emit_verbatim_at(out, &ind, &e.annotations, PlacementKind::BeforeDeclaration)?;
706 writeln!(out, "{ind}enum class {} : int32_t {{", e.name.text).map_err(fmt_err)?;
707 let inner = " ".repeat((ctx.indent_level + 1) * ctx.opts.indent_width);
708 emit_verbatim_at(out, &inner, &e.annotations, PlacementKind::BeginDeclaration)?;
709 for en in &e.enumerators {
710 check_identifier(&en.name.text)?;
711 writeln!(out, "{inner}{},", en.name.text).map_err(fmt_err)?;
712 }
713 emit_verbatim_at(out, &inner, &e.annotations, PlacementKind::EndDeclaration)?;
714 writeln!(out, "{ind}}};").map_err(fmt_err)?;
715 emit_verbatim_at(out, &ind, &e.annotations, PlacementKind::AfterDeclaration)?;
716 writeln!(out).map_err(fmt_err)?;
717 Ok(())
718}
719
720fn emit_interface_stub(
721 out: &mut String,
722 ctx: &mut EmitCtx<'_>,
723 iface: &InterfaceDef,
724) -> Result<(), CppGenError> {
725 let name = &iface.name.text;
726 check_identifier(name)?;
727 let ind = ctx.indent();
728 let inner = " ".repeat((ctx.indent_level + 1) * ctx.opts.indent_width);
729
730 emit_verbatim_at(
731 out,
732 &ind,
733 &iface.annotations,
734 PlacementKind::BeforeDeclaration,
735 )?;
736
737 if iface.bases.is_empty() {
739 writeln!(out, "{ind}class {name} {{").map_err(fmt_err)?;
740 } else {
741 let bases: Vec<String> = iface
742 .bases
743 .iter()
744 .map(|b| format!("public virtual {}", scoped_to_cpp(b)))
745 .collect();
746 writeln!(out, "{ind}class {name} : {} {{", bases.join(", ")).map_err(fmt_err)?;
747 }
748 writeln!(out, "{ind}public:").map_err(fmt_err)?;
749 writeln!(out, "{inner}virtual ~{name}() = default;").map_err(fmt_err)?;
750
751 for export in &iface.exports {
752 match export {
753 Export::Op(op) => emit_interface_op(out, &inner, op)?,
754 Export::Attr(attr) => emit_interface_attr(out, &inner, attr)?,
755 Export::Type(td) => emit_type_decl(out, ctx, td)?,
756 Export::Const(c) => emit_const_decl(out, ctx, c)?,
757 Export::Except(e) => emit_exception(out, ctx, e)?,
758 }
759 }
760
761 writeln!(out, "{ind}}};").map_err(fmt_err)?;
762 emit_verbatim_at(
763 out,
764 &ind,
765 &iface.annotations,
766 PlacementKind::AfterDeclaration,
767 )?;
768 writeln!(out).map_err(fmt_err)?;
769 Ok(())
770}
771
772fn emit_interface_op(out: &mut String, inner: &str, op: &OpDecl) -> Result<(), CppGenError> {
773 check_identifier(&op.name.text)?;
774 let ret = match &op.return_type {
775 None => "void".to_string(),
776 Some(t) => typespec_to_cpp(t)?,
777 };
778 let params: Vec<String> = op
779 .params
780 .iter()
781 .map(|p| -> Result<String, CppGenError> {
782 let ty = typespec_to_cpp(&p.type_spec)?;
783 let qual = match p.attribute {
786 ParamAttribute::In => format!("const {ty}&"),
787 ParamAttribute::Out | ParamAttribute::InOut => format!("{ty}&"),
788 };
789 Ok(format!("{qual} {}", p.name.text))
790 })
791 .collect::<Result<_, _>>()?;
792 let raises_comment = if op.raises.is_empty() {
793 String::new()
794 } else {
795 let raises: Vec<String> = op.raises.iter().map(scoped_to_cpp).collect();
796 format!(" /* throws {} */", raises.join(", "))
797 };
798 writeln!(
799 out,
800 "{inner}virtual {ret} {}({}) = 0;{raises_comment}",
801 op.name.text,
802 params.join(", ")
803 )
804 .map_err(fmt_err)?;
805 Ok(())
806}
807
808fn emit_interface_attr(
809 out: &mut String,
810 inner: &str,
811 attr: &zerodds_idl::ast::AttrDecl,
812) -> Result<(), CppGenError> {
813 check_identifier(&attr.name.text)?;
814 let ty = typespec_to_cpp(&attr.type_spec)?;
815 writeln!(out, "{inner}virtual {ty} {}() const = 0;", attr.name.text).map_err(fmt_err)?;
817 if !attr.readonly {
819 writeln!(
820 out,
821 "{inner}virtual void {}(const {ty}& value) = 0;",
822 attr.name.text
823 )
824 .map_err(fmt_err)?;
825 }
826 Ok(())
827}
828
829fn emit_value_type(
830 out: &mut String,
831 ctx: &mut EmitCtx<'_>,
832 v: &ValueDef,
833) -> Result<(), CppGenError> {
834 let name = &v.name.text;
835 check_identifier(name)?;
836 let ind = ctx.indent();
837 let inner = " ".repeat((ctx.indent_level + 1) * ctx.opts.indent_width);
838
839 emit_verbatim_at(out, &ind, &v.annotations, PlacementKind::BeforeDeclaration)?;
840
841 let mut bases: Vec<String> = Vec::new();
845 if let Some(inh) = &v.inheritance {
846 for b in &inh.bases {
847 bases.push(format!("public virtual {}", scoped_to_cpp(b)));
848 }
849 for s in &inh.supports {
850 bases.push(format!("public virtual {}", scoped_to_cpp(s)));
851 }
852 }
853 if bases.is_empty() {
854 writeln!(out, "{ind}class {name} {{").map_err(fmt_err)?;
855 } else {
856 writeln!(out, "{ind}class {name} : {} {{", bases.join(", ")).map_err(fmt_err)?;
857 }
858 writeln!(out, "{ind}public:").map_err(fmt_err)?;
859 writeln!(out, "{inner}virtual ~{name}() = default;").map_err(fmt_err)?;
860
861 let mut has_protected = false;
863 for el in &v.elements {
864 match el {
865 ValueElement::State(s) if matches!(s.visibility, StateVisibility::Public) => {
866 let ty = typespec_to_cpp(&s.type_spec)?;
867 for d in &s.declarators {
868 let n = &d.name().text;
869 writeln!(out, "{inner}virtual const {ty}& {n}() const = 0;")
870 .map_err(fmt_err)?;
871 writeln!(out, "{inner}virtual void {n}(const {ty}& value) = 0;")
872 .map_err(fmt_err)?;
873 }
874 }
875 ValueElement::State(s) if matches!(s.visibility, StateVisibility::Private) => {
876 has_protected = true;
877 }
878 ValueElement::Export(Export::Op(op)) => emit_interface_op(out, &inner, op)?,
879 ValueElement::Export(Export::Attr(a)) => emit_interface_attr(out, &inner, a)?,
880 _ => {}
881 }
882 }
883
884 if has_protected {
886 writeln!(out, "{ind}protected:").map_err(fmt_err)?;
887 for el in &v.elements {
888 if let ValueElement::State(s) = el {
889 if matches!(s.visibility, StateVisibility::Private) {
890 let ty = typespec_to_cpp(&s.type_spec)?;
891 for d in &s.declarators {
892 let n = &d.name().text;
893 writeln!(out, "{inner}virtual const {ty}& {n}() const = 0;")
894 .map_err(fmt_err)?;
895 writeln!(out, "{inner}virtual void {n}(const {ty}& value) = 0;")
896 .map_err(fmt_err)?;
897 }
898 }
899 }
900 }
901 }
902
903 writeln!(out, "{ind}}};").map_err(fmt_err)?;
904
905 let factories: Vec<&zerodds_idl::ast::InitDcl> = v
907 .elements
908 .iter()
909 .filter_map(|e| {
910 if let ValueElement::Init(i) = e {
911 Some(i)
912 } else {
913 None
914 }
915 })
916 .collect();
917 if !factories.is_empty() {
918 writeln!(out, "{ind}class {name}_factory {{").map_err(fmt_err)?;
919 writeln!(out, "{ind}public:").map_err(fmt_err)?;
920 writeln!(out, "{inner}virtual ~{name}_factory() = default;").map_err(fmt_err)?;
921 for f in &factories {
922 check_identifier(&f.name.text)?;
923 let params: Vec<String> = f
924 .params
925 .iter()
926 .map(|p| -> Result<String, CppGenError> {
927 let ty = typespec_to_cpp(&p.type_spec)?;
928 let qual = match p.attribute {
929 ParamAttribute::In => format!("const {ty}&"),
930 ParamAttribute::Out | ParamAttribute::InOut => format!("{ty}&"),
931 };
932 Ok(format!("{qual} {}", p.name.text))
933 })
934 .collect::<Result<_, _>>()?;
935 writeln!(
936 out,
937 "{inner}virtual std::shared_ptr<{name}> {}({}) = 0;",
938 f.name.text,
939 params.join(", ")
940 )
941 .map_err(fmt_err)?;
942 }
943 writeln!(out, "{ind}}};").map_err(fmt_err)?;
944 }
945
946 emit_verbatim_at(out, &ind, &v.annotations, PlacementKind::AfterDeclaration)?;
947 writeln!(out).map_err(fmt_err)?;
948 Ok(())
949}
950
951fn emit_typedef(
952 out: &mut String,
953 ctx: &mut EmitCtx<'_>,
954 t: &TypedefDecl,
955) -> Result<(), CppGenError> {
956 let ind = ctx.indent();
957 emit_verbatim_at(out, &ind, &t.annotations, PlacementKind::BeforeDeclaration)?;
958 for decl in &t.declarators {
959 let alias = &decl.name().text;
960 check_identifier(alias)?;
961 let target_ty = type_for_declarator(&t.type_spec, decl)?;
962 writeln!(out, "{ind}using {alias} = {target_ty};").map_err(fmt_err)?;
963 }
964 emit_verbatim_at(out, &ind, &t.annotations, PlacementKind::AfterDeclaration)?;
965 writeln!(out).map_err(fmt_err)?;
966 Ok(())
967}
968
969fn emit_const_decl(
970 out: &mut String,
971 ctx: &mut EmitCtx<'_>,
972 c: &zerodds_idl::ast::ConstDecl,
973) -> Result<(), CppGenError> {
974 check_identifier(&c.name.text)?;
975 let ind = ctx.indent();
976 let cpp_ty = match &c.type_ {
977 zerodds_idl::ast::ConstType::Integer(i) => crate::type_map::integer_to_cpp(*i).to_string(),
978 zerodds_idl::ast::ConstType::Floating(f) => {
979 crate::type_map::floating_to_cpp(*f).to_string()
980 }
981 zerodds_idl::ast::ConstType::Boolean => "bool".into(),
982 zerodds_idl::ast::ConstType::Char => "char".into(),
983 zerodds_idl::ast::ConstType::WideChar => "wchar_t".into(),
984 zerodds_idl::ast::ConstType::Octet => "uint8_t".into(),
985 zerodds_idl::ast::ConstType::String { wide: false } => "std::string".into(),
986 zerodds_idl::ast::ConstType::String { wide: true } => "std::wstring".into(),
987 zerodds_idl::ast::ConstType::Scoped(s) => scoped_to_cpp(s),
988 zerodds_idl::ast::ConstType::Fixed => {
989 "::dds::core::Fixed<31, 0>".into()
993 }
994 };
995 let val = const_expr_to_cpp(&c.value);
996 writeln!(out, "{ind}constexpr {cpp_ty} {} = {val};", c.name.text).map_err(fmt_err)?;
997 Ok(())
998}
999
1000fn emit_exception(
1001 out: &mut String,
1002 ctx: &mut EmitCtx<'_>,
1003 e: &ExceptDecl,
1004) -> Result<(), CppGenError> {
1005 check_identifier(&e.name.text)?;
1006 let ind = ctx.indent();
1007 writeln!(out, "{ind}class {} : public std::exception {{", e.name.text).map_err(fmt_err)?;
1008 writeln!(out, "{ind}public:").map_err(fmt_err)?;
1009 let inner = " ".repeat((ctx.indent_level + 1) * ctx.opts.indent_width);
1010 writeln!(out, "{inner}{}() = default;", e.name.text).map_err(fmt_err)?;
1011 writeln!(out, "{inner}~{}() override = default;", e.name.text).map_err(fmt_err)?;
1012 writeln!(
1013 out,
1014 "{inner}const char* what() const noexcept override {{ return \"{}\"; }}",
1015 e.name.text
1016 )
1017 .map_err(fmt_err)?;
1018 writeln!(out).map_err(fmt_err)?;
1019 writeln!(out, "{ind}private:").map_err(fmt_err)?;
1020 for m in &e.members {
1021 for decl in &m.declarators {
1022 let cpp_ty = type_for_declarator(&m.type_spec, decl)?;
1023 let name = &decl.name().text;
1024 check_identifier(name)?;
1025 writeln!(out, "{inner}{cpp_ty} {name}_;").map_err(fmt_err)?;
1026 }
1027 }
1028 writeln!(out, "{ind}}};").map_err(fmt_err)?;
1029 writeln!(out).map_err(fmt_err)?;
1030 Ok(())
1031}
1032
1033pub(crate) fn type_for_declarator(ts: &TypeSpec, decl: &Declarator) -> Result<String, CppGenError> {
1040 let base = typespec_to_cpp(ts)?;
1041 match decl {
1042 Declarator::Simple(_) => Ok(base),
1043 Declarator::Array(arr) => {
1044 let mut out = base;
1047 for size in arr.sizes.iter().rev() {
1048 let n = const_expr_to_usize(size).unwrap_or_default();
1049 out = format!("std::array<{out}, {n}>");
1050 }
1051 Ok(out)
1052 }
1053 }
1054}
1055
1056pub(crate) fn typespec_to_cpp(ts: &TypeSpec) -> Result<String, CppGenError> {
1058 match ts {
1059 TypeSpec::Primitive(p) => Ok(primitive_to_cpp(*p).to_string()),
1060 TypeSpec::Scoped(s) => Ok(scoped_to_cpp(s)),
1061 TypeSpec::Sequence(s) => {
1062 let inner = typespec_to_cpp(&s.elem)?;
1063 Ok(format!("std::vector<{inner}>"))
1064 }
1065 TypeSpec::String(s) => {
1066 if s.wide {
1067 Ok("std::wstring".into())
1068 } else {
1069 Ok("std::string".into())
1070 }
1071 }
1072 TypeSpec::Map(m) => {
1073 let k = typespec_to_cpp(&m.key)?;
1074 let v = typespec_to_cpp(&m.value)?;
1075 Ok(format!("std::map<{k}, {v}>"))
1076 }
1077 TypeSpec::Fixed(f) => {
1078 let digits = const_expr_to_u32(&f.digits).unwrap_or(0);
1082 let scale = const_expr_to_u32(&f.scale).unwrap_or(0);
1083 Ok(format!("::dds::core::Fixed<{digits}, {scale}>"))
1084 }
1085 TypeSpec::Any => {
1086 Ok("::dds::core::Any".into())
1091 }
1092 }
1093}
1094
1095pub(crate) fn switch_type_to_cpp(s: &SwitchTypeSpec) -> Result<String, CppGenError> {
1096 Ok(match s {
1097 SwitchTypeSpec::Integer(i) => crate::type_map::integer_to_cpp(*i).to_string(),
1098 SwitchTypeSpec::Char => "char".into(),
1099 SwitchTypeSpec::Boolean => "bool".into(),
1100 SwitchTypeSpec::Octet => "uint8_t".into(),
1101 SwitchTypeSpec::Scoped(s) => scoped_to_cpp(s),
1102 })
1103}
1104
1105pub(crate) fn scoped_to_cpp(s: &ScopedName) -> String {
1106 if s.parts.len() == 1 {
1108 if let Some(mapped) = TIME_DURATION_TYPES
1109 .iter()
1110 .find(|(idl, _)| *idl == s.parts[0].text)
1111 .map(|(_, cpp)| *cpp)
1112 {
1113 return mapped.to_string();
1114 }
1115 }
1116 let parts: Vec<String> = s.parts.iter().map(|p| p.text.clone()).collect();
1117 let joined = parts.join("::");
1118 if s.absolute {
1119 format!("::{joined}")
1120 } else {
1121 joined
1122 }
1123}
1124
1125fn const_expr_to_u32(e: &ConstExpr) -> Option<u32> {
1130 if let ConstExpr::Literal(l) = e {
1131 if matches!(l.kind, LiteralKind::Integer) {
1132 return l.raw.parse::<u32>().ok();
1133 }
1134 }
1135 None
1136}
1137
1138pub(crate) fn const_expr_to_cpp(e: &ConstExpr) -> String {
1139 match e {
1140 ConstExpr::Literal(l) => literal_to_cpp(l),
1141 ConstExpr::Scoped(s) => scoped_to_cpp(s),
1142 ConstExpr::Unary { op, operand, .. } => {
1143 let prefix = match op {
1144 zerodds_idl::ast::UnaryOp::Plus => "+",
1145 zerodds_idl::ast::UnaryOp::Minus => "-",
1146 zerodds_idl::ast::UnaryOp::BitNot => "~",
1147 };
1148 format!("{prefix}{}", const_expr_to_cpp(operand))
1149 }
1150 ConstExpr::Binary { op, lhs, rhs, .. } => {
1151 let opstr = match op {
1152 zerodds_idl::ast::BinaryOp::Or => "|",
1153 zerodds_idl::ast::BinaryOp::Xor => "^",
1154 zerodds_idl::ast::BinaryOp::And => "&",
1155 zerodds_idl::ast::BinaryOp::Shl => "<<",
1156 zerodds_idl::ast::BinaryOp::Shr => ">>",
1157 zerodds_idl::ast::BinaryOp::Add => "+",
1158 zerodds_idl::ast::BinaryOp::Sub => "-",
1159 zerodds_idl::ast::BinaryOp::Mul => "*",
1160 zerodds_idl::ast::BinaryOp::Div => "/",
1161 zerodds_idl::ast::BinaryOp::Mod => "%",
1162 };
1163 format!(
1164 "({} {opstr} {})",
1165 const_expr_to_cpp(lhs),
1166 const_expr_to_cpp(rhs)
1167 )
1168 }
1169 }
1170}
1171
1172fn literal_to_cpp(l: &Literal) -> String {
1173 match l.kind {
1174 LiteralKind::Boolean => l.raw.clone(),
1175 LiteralKind::Integer | LiteralKind::Floating => l.raw.clone(),
1176 LiteralKind::Char => l.raw.clone(),
1177 LiteralKind::WideChar => l.raw.clone(),
1178 LiteralKind::String => l.raw.clone(),
1179 LiteralKind::WideString => l.raw.clone(),
1180 LiteralKind::Fixed => l.raw.clone(),
1181 }
1182}
1183
1184fn const_expr_to_usize(e: &ConstExpr) -> Option<usize> {
1185 match e {
1186 ConstExpr::Literal(l) if l.kind == LiteralKind::Integer => l.raw.parse::<usize>().ok(),
1187 _ => None,
1188 }
1189}
1190
1191fn has_key_annotation(anns: &[Annotation]) -> bool {
1196 has_named_annotation(anns, "key")
1197}
1198
1199fn has_optional_annotation(anns: &[Annotation]) -> bool {
1200 has_named_annotation(anns, "optional")
1201}
1202
1203fn has_shared_annotation(anns: &[Annotation]) -> bool {
1204 has_named_annotation(anns, "shared")
1205}
1206
1207fn has_named_annotation(anns: &[Annotation], name: &str) -> bool {
1208 anns.iter().any(|a| {
1209 a.name.parts.last().is_some_and(|p| p.text == name)
1210 && matches!(a.params, AnnotationParams::None | AnnotationParams::Empty)
1211 })
1212}
1213
1214fn find_uint_annotation(anns: &[Annotation], name: &str) -> Option<u32> {
1216 for a in anns {
1217 if a.name.parts.last().is_some_and(|p| p.text == name) {
1218 if let AnnotationParams::Single(expr) = &a.params {
1219 if let Some(v) = const_expr_as_u32(expr) {
1220 return Some(v);
1221 }
1222 }
1223 }
1224 }
1225 None
1226}
1227fn const_expr_as_u32(e: &ConstExpr) -> Option<u32> {
1231 match e {
1232 ConstExpr::Literal(Literal {
1233 kind: LiteralKind::Integer,
1234 raw,
1235 ..
1236 }) => parse_int_literal(raw).and_then(|v| u32::try_from(v).ok()),
1237 ConstExpr::Unary {
1238 op: zerodds_idl::ast::UnaryOp::Plus,
1239 operand,
1240 ..
1241 } => const_expr_as_u32(operand),
1242 _ => None,
1243 }
1244}
1245
1246fn parse_int_literal(raw: &str) -> Option<u64> {
1248 let s = raw.trim_end_matches(|c: char| matches!(c, 'l' | 'L' | 'u' | 'U'));
1249 if let Some(hex) = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")) {
1250 u64::from_str_radix(hex, 16).ok()
1251 } else if s.len() > 1 && s.starts_with('0') {
1252 u64::from_str_radix(&s[1..], 8).ok()
1253 } else {
1254 s.parse::<u64>().ok()
1255 }
1256}
1257
1258#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1260enum Extensibility {
1261 Final,
1262 Appendable,
1263 Mutable,
1264}
1265
1266fn struct_extensibility(anns: &[Annotation]) -> Extensibility {
1267 if has_named_annotation(anns, "final") {
1268 Extensibility::Final
1269 } else if has_named_annotation(anns, "mutable") {
1270 Extensibility::Mutable
1271 } else if has_named_annotation(anns, "appendable") {
1272 Extensibility::Appendable
1273 } else {
1274 Extensibility::Appendable
1276 }
1277}
1278
1279fn collect_inheritance_edges(
1287 defs: &[Definition],
1288 parents: &mut std::collections::HashMap<String, String>,
1289 prefix: &str,
1290) {
1291 for d in defs {
1292 match d {
1293 Definition::Module(m) => {
1294 let new_prefix = if prefix.is_empty() {
1295 m.name.text.clone()
1296 } else {
1297 format!("{prefix}::{}", m.name.text)
1298 };
1299 collect_inheritance_edges(&m.definitions, parents, &new_prefix);
1300 }
1301 Definition::Type(TypeDecl::Constr(ConstrTypeDecl::Struct(StructDcl::Def(s)))) => {
1302 let key = if prefix.is_empty() {
1303 s.name.text.clone()
1304 } else {
1305 format!("{prefix}::{}", s.name.text)
1306 };
1307 if let Some(b) = &s.base {
1308 let base_str = b
1309 .parts
1310 .iter()
1311 .map(|p| p.text.clone())
1312 .collect::<Vec<_>>()
1313 .join("::");
1314 parents.insert(key, base_str);
1315 }
1316 }
1317 _ => {}
1318 }
1319 }
1320}
1321
1322fn detect_inheritance_cycles(spec: &Specification) -> Result<(), CppGenError> {
1323 use std::collections::HashMap;
1324
1325 let mut parents: HashMap<String, String> = HashMap::new();
1326 collect_inheritance_edges(&spec.definitions, &mut parents, "");
1327
1328 for start in parents.keys() {
1330 let mut current = start.clone();
1331 let mut visited: BTreeSet<String> = BTreeSet::new();
1332 visited.insert(current.clone());
1333 while let Some(p) = parents.get(¤t) {
1334 let resolved = parents
1336 .keys()
1337 .find(|k| *k == p || k.ends_with(&format!("::{p}")))
1338 .cloned()
1339 .unwrap_or_else(|| p.clone());
1340 if visited.contains(&resolved) {
1341 return Err(CppGenError::InheritanceCycle {
1342 type_name: short_name(&resolved),
1343 });
1344 }
1345 visited.insert(resolved.clone());
1346 if resolved == current {
1348 return Err(CppGenError::InheritanceCycle {
1349 type_name: short_name(&resolved),
1350 });
1351 }
1352 current = resolved;
1353 if !parents.contains_key(¤t) {
1354 break;
1355 }
1356 }
1357 }
1358 Ok(())
1359}
1360
1361fn short_name(s: &str) -> String {
1362 s.rsplit("::").next().unwrap_or(s).to_string()
1363}
1364
1365fn collect_topic_structs<'a>(
1388 defs: &'a [Definition],
1389 prefix: &str,
1390 out: &mut Vec<(String, &'a StructDef)>,
1391) {
1392 for d in defs {
1393 match d {
1394 Definition::Module(m) => {
1395 let np = if prefix.is_empty() {
1396 m.name.text.clone()
1397 } else {
1398 format!("{prefix}::{}", m.name.text)
1399 };
1400 collect_topic_structs(&m.definitions, &np, out);
1401 }
1402 Definition::Type(TypeDecl::Constr(ConstrTypeDecl::Struct(StructDcl::Def(s)))) => {
1403 let fqn = if prefix.is_empty() {
1404 s.name.text.clone()
1405 } else {
1406 format!("{prefix}::{}", s.name.text)
1407 };
1408 out.push((fqn, s));
1409 }
1410 _ => {}
1411 }
1412 }
1413}
1414
1415fn emit_topic_type_support_specs(
1416 out: &mut String,
1417 opts: &CppGenOptions,
1418 structs: &[(String, &StructDef)],
1419) -> Result<(), CppGenError> {
1420 writeln!(out).map_err(fmt_err)?;
1421 writeln!(
1422 out,
1423 "// DDS-PSM-Cxx topic_type_support<T> -- auto-generiert (XCDR2 Wire, XTypes 1.3 7.4)."
1424 )
1425 .map_err(fmt_err)?;
1426 writeln!(out, "namespace dds {{").map_err(fmt_err)?;
1427 writeln!(out, "namespace topic {{").map_err(fmt_err)?;
1428 writeln!(out).map_err(fmt_err)?;
1429
1430 let user_prefix = opts
1431 .namespace_prefix
1432 .as_deref()
1433 .filter(|p| !p.is_empty())
1434 .unwrap_or("");
1435 for (fqn, s) in structs {
1436 let cpp_fqn = if user_prefix.is_empty() {
1437 format!("::{fqn}")
1438 } else {
1439 format!("::{user_prefix}::{fqn}")
1440 };
1441 emit_topic_type_support_for(out, &cpp_fqn, fqn, s)?;
1442 }
1443
1444 writeln!(out, "}} // namespace topic").map_err(fmt_err)?;
1445 writeln!(out, "}} // namespace dds").map_err(fmt_err)?;
1446 Ok(())
1447}
1448
1449fn member_codegen_supported(m: &Member) -> bool {
1453 !has_shared_annotation(&m.annotations)
1454}
1455
1456fn typespec_supported(ts: &TypeSpec) -> bool {
1460 match ts {
1461 TypeSpec::Primitive(_) => true,
1462 TypeSpec::String(_) => true,
1464 TypeSpec::Sequence(seq) => match &*seq.elem {
1470 TypeSpec::Primitive(_) => true,
1471 TypeSpec::String(_) => true,
1472 TypeSpec::Scoped(s) => scoped_is_enum(s) || scoped_struct(s).is_some(),
1473 TypeSpec::Sequence(_) => typespec_supported(&seq.elem),
1474 TypeSpec::Map(m) => typespec_supported(&m.key) && typespec_supported(&m.value),
1475 _ => false,
1476 },
1477 TypeSpec::Scoped(s) => scoped_is_enum(s) || scoped_struct(s).is_some(),
1483 TypeSpec::Map(m) => typespec_supported(&m.key) && typespec_supported(&m.value),
1486 _ => false,
1487 }
1488}
1489
1490thread_local! {
1498 static ENUM_NAMES: RefCell<BTreeSet<String>> = const { RefCell::new(BTreeSet::new()) };
1499 static STRUCT_NAMES: RefCell<BTreeSet<String>> = const { RefCell::new(BTreeSet::new()) };
1500 static STRUCT_DEFS: RefCell<BTreeMap<String, StructDef>> = const { RefCell::new(BTreeMap::new()) };
1501 static NEST_CTR: core::cell::Cell<u32> = const { core::cell::Cell::new(0) };
1504}
1505
1506fn next_nest_id() -> u32 {
1507 NEST_CTR.with(|c| {
1508 let v = c.get();
1509 c.set(v.wrapping_add(1));
1510 v
1511 })
1512}
1513
1514fn set_type_registry(spec: &Specification) {
1515 let mut enums = BTreeSet::new();
1516 let mut structs = BTreeSet::new();
1517 let mut defs = BTreeMap::new();
1518 collect_type_names(&spec.definitions, &mut enums, &mut structs, &mut defs);
1519 ENUM_NAMES.with(|r| *r.borrow_mut() = enums);
1520 STRUCT_NAMES.with(|r| *r.borrow_mut() = structs);
1521 STRUCT_DEFS.with(|r| *r.borrow_mut() = defs);
1522}
1523
1524fn collect_type_names(
1526 defs: &[Definition],
1527 enums: &mut BTreeSet<String>,
1528 structs: &mut BTreeSet<String>,
1529 struct_defs: &mut BTreeMap<String, StructDef>,
1530) {
1531 for d in defs {
1532 match d {
1533 Definition::Module(m) => {
1534 collect_type_names(&m.definitions, enums, structs, struct_defs)
1535 }
1536 Definition::Type(TypeDecl::Constr(ConstrTypeDecl::Enum(e))) => {
1537 enums.insert(e.name.text.clone());
1538 }
1539 Definition::Type(TypeDecl::Constr(ConstrTypeDecl::Struct(StructDcl::Def(s)))) => {
1540 structs.insert(s.name.text.clone());
1541 struct_defs.insert(s.name.text.clone(), s.clone());
1542 }
1543 _ => {}
1544 }
1545 }
1546}
1547
1548fn scoped_is_enum(s: &ScopedName) -> bool {
1550 let Some(last) = s.parts.last().map(|p| p.text.clone()) else {
1551 return false;
1552 };
1553 let is_enum = ENUM_NAMES.with(|r| r.borrow().contains(&last));
1554 let is_struct = STRUCT_NAMES.with(|r| r.borrow().contains(&last));
1555 is_enum && !is_struct
1556}
1557
1558fn scoped_final_struct(s: &ScopedName) -> Option<StructDef> {
1565 match scoped_struct(s) {
1566 Some((def, Extensibility::Final)) => Some(def),
1567 _ => None,
1568 }
1569}
1570
1571fn scoped_struct(s: &ScopedName) -> Option<(StructDef, Extensibility)> {
1584 let last = s.parts.last()?.text.clone();
1585 let def = STRUCT_DEFS.with(|r| r.borrow().get(&last).cloned())?;
1586 let ext = struct_extensibility(&def.annotations);
1587 let all_encodable = def.members.iter().all(|m| {
1588 m.declarators.len() == 1
1589 && matches!(m.declarators.first(), Some(Declarator::Simple(_)))
1590 && typespec_supported(&m.type_spec)
1591 });
1592 if all_encodable {
1593 Some((def, ext))
1594 } else {
1595 None
1596 }
1597}
1598
1599fn emit_topic_type_support_for(
1600 out: &mut String,
1601 cpp_fqn: &str,
1602 type_name: &str,
1603 s: &StructDef,
1604) -> Result<(), CppGenError> {
1605 let ext = struct_extensibility(&s.annotations);
1606
1607 writeln!(out, "template <>").map_err(fmt_err)?;
1608 writeln!(out, "struct topic_type_support<{cpp_fqn}> {{").map_err(fmt_err)?;
1609 writeln!(
1610 out,
1611 " static const char* type_name() {{ return \"{type_name}\"; }}"
1612 )
1613 .map_err(fmt_err)?;
1614
1615 let is_keyed = s.members.iter().any(|m| has_key_annotation(&m.annotations));
1617 writeln!(
1618 out,
1619 " static constexpr bool is_keyed() {{ return {}; }}",
1620 if is_keyed { "true" } else { "false" }
1621 )
1622 .map_err(fmt_err)?;
1623
1624 let ext_lit = match ext {
1626 Extensibility::Final => "FINAL",
1627 Extensibility::Appendable => "APPENDABLE",
1628 Extensibility::Mutable => "MUTABLE",
1629 };
1630 writeln!(
1631 out,
1632 " static constexpr ::dds::topic::core::policy::DataRepresentationKind extensibility() {{ return ::dds::topic::core::policy::DataRepresentationKind::{ext_lit}; }}"
1633 )
1634 .map_err(fmt_err)?;
1635
1636 emit_encode_fn(out, cpp_fqn, s, ext, false)?;
1638 emit_encode_fn(out, cpp_fqn, s, ext, true)?;
1640 emit_decode_fn(out, cpp_fqn, s, ext)?;
1642 emit_key_hash_fn(out, cpp_fqn, s, is_keyed)?;
1644
1645 writeln!(out, "}};").map_err(fmt_err)?;
1646 writeln!(out).map_err(fmt_err)?;
1647 Ok(())
1648}
1649
1650fn emit_encode_fn(
1651 out: &mut String,
1652 cpp_fqn: &str,
1653 s: &StructDef,
1654 ext: Extensibility,
1655 be: bool,
1656) -> Result<(), CppGenError> {
1657 let endian_suffix = if be { "be" } else { "le" };
1659
1660 if be {
1661 writeln!(
1662 out,
1663 " static std::vector<uint8_t> encode_be(const {cpp_fqn}& __v) {{"
1664 )
1665 .map_err(fmt_err)?;
1666 } else {
1667 writeln!(
1674 out,
1675 " static std::vector<uint8_t> encode(const {cpp_fqn}& __v) {{"
1676 )
1677 .map_err(fmt_err)?;
1678 writeln!(
1679 out,
1680 " return encode(__v, ::dds::topic::xcdr2::XcdrVersion::Xcdr2);"
1681 )
1682 .map_err(fmt_err)?;
1683 writeln!(out, " }}").map_err(fmt_err)?;
1684 writeln!(
1685 out,
1686 " static std::vector<uint8_t> encode(const {cpp_fqn}& __v, \
1687 ::dds::topic::xcdr2::XcdrVersion __repr) {{"
1688 )
1689 .map_err(fmt_err)?;
1690 }
1691 writeln!(out, " std::vector<uint8_t> __out;").map_err(fmt_err)?;
1692 writeln!(out, " (void)__v;").map_err(fmt_err)?;
1693 if !be {
1694 writeln!(
1695 out,
1696 " const size_t __max_align = ::dds::topic::xcdr2::xcdr_max_align(__repr);"
1697 )
1698 .map_err(fmt_err)?;
1699 writeln!(out, " (void)__max_align;").map_err(fmt_err)?;
1700 }
1701
1702 match ext {
1703 Extensibility::Final => {
1704 writeln!(out, " const size_t __origin = 0;").map_err(fmt_err)?;
1707 writeln!(out, " (void)__origin;").map_err(fmt_err)?;
1708 for m in &s.members {
1709 emit_plain_member_encode(out, m, endian_suffix, "__origin")?;
1710 }
1711 }
1712 Extensibility::Appendable => {
1713 writeln!(
1714 out,
1715 " const auto __dh = ::dds::topic::xcdr2::dheader_begin(__out);"
1716 )
1717 .map_err(fmt_err)?;
1718 writeln!(out, " const size_t __origin = __out.size();").map_err(fmt_err)?;
1719 writeln!(out, " (void)__origin;").map_err(fmt_err)?;
1720 for m in &s.members {
1721 emit_plain_member_encode(out, m, endian_suffix, "__origin")?;
1722 }
1723 writeln!(
1724 out,
1725 " ::dds::topic::xcdr2::dheader_end(__out, __dh);"
1726 )
1727 .map_err(fmt_err)?;
1728 }
1729 Extensibility::Mutable => {
1730 writeln!(
1731 out,
1732 " const auto __scope = ::dds::topic::xcdr2::mutable_begin(__out);"
1733 )
1734 .map_err(fmt_err)?;
1735 writeln!(out, " const size_t __origin = __scope.origin;").map_err(fmt_err)?;
1736 for m in &s.members {
1737 emit_mutable_member_encode(out, m, endian_suffix)?;
1738 }
1739 writeln!(
1740 out,
1741 " ::dds::topic::xcdr2::mutable_end(__out, __scope);"
1742 )
1743 .map_err(fmt_err)?;
1744 }
1745 }
1746
1747 writeln!(out, " return __out;").map_err(fmt_err)?;
1748 writeln!(out, " }}").map_err(fmt_err)?;
1749 Ok(())
1750}
1751
1752fn emit_plain_member_encode(
1755 out: &mut String,
1756 m: &Member,
1757 endian: &str,
1758 origin: &str,
1759) -> Result<(), CppGenError> {
1760 if !member_codegen_supported(m) {
1761 for decl in &m.declarators {
1762 let name = &decl.name().text;
1763 writeln!(
1764 out,
1765 " // xcdr2: @shared member '{name}' not supported (skip)"
1766 )
1767 .map_err(fmt_err)?;
1768 }
1769 return Ok(());
1770 }
1771 let is_optional = has_optional_annotation(&m.annotations);
1772 for decl in &m.declarators {
1773 let name = &decl.name().text;
1774 if let Declarator::Array(arr) = decl {
1779 let prim = matches!(m.type_spec, TypeSpec::Primitive(_));
1780 let leaf_1d = arr.sizes.len() == 1
1781 && matches!(m.type_spec, TypeSpec::Primitive(_) | TypeSpec::String(_));
1782 if leaf_1d {
1783 writeln!(out, " for (const auto& __ae : __v.{name}()) {{")
1784 .map_err(fmt_err)?;
1785 emit_value_write(out, &m.type_spec, "__ae", endian, origin, " ")?;
1786 writeln!(out, " }}").map_err(fmt_err)?;
1787 } else if prim && arr.sizes.len() >= 2 {
1788 let n = arr.sizes.len();
1792 let mut acc = format!("__v.{name}()");
1793 let mut ind = String::from(" ");
1794 for d in 0..n {
1795 let lv = format!("__a{d}");
1796 writeln!(out, "{ind}for (const auto& {lv} : {acc}) {{").map_err(fmt_err)?;
1797 acc = lv;
1798 ind.push_str(" ");
1799 }
1800 emit_value_write(out, &m.type_spec, &acc, endian, origin, &ind)?;
1801 for _ in 0..n {
1802 ind.truncate(ind.len() - 4);
1803 writeln!(out, "{ind}}}").map_err(fmt_err)?;
1804 }
1805 } else if matches!(&m.type_spec, TypeSpec::Scoped(s) if scoped_is_enum(s) || scoped_final_struct(s).is_some())
1806 || (matches!(m.type_spec, TypeSpec::String(_)) && arr.sizes.len() >= 2)
1807 {
1808 let n = arr.sizes.len();
1813 writeln!(out, " {{").map_err(fmt_err)?;
1814 writeln!(
1815 out,
1816 " const auto __arr_dh = ::dds::topic::xcdr2::dheader_begin(__out);"
1817 )
1818 .map_err(fmt_err)?;
1819 let mut acc = format!("__v.{name}()");
1820 let mut ind = String::from(" ");
1821 for d in 0..n {
1822 let lv = format!("__a{d}");
1823 writeln!(out, "{ind}for (const auto& {lv} : {acc}) {{").map_err(fmt_err)?;
1824 acc = lv;
1825 ind.push_str(" ");
1826 }
1827 emit_value_write(out, &m.type_spec, &acc, endian, origin, &ind)?;
1828 for _ in 0..n {
1829 ind.truncate(ind.len() - 4);
1830 writeln!(out, "{ind}}}").map_err(fmt_err)?;
1831 }
1832 writeln!(
1833 out,
1834 " ::dds::topic::xcdr2::dheader_end(__out, __arr_dh);"
1835 )
1836 .map_err(fmt_err)?;
1837 writeln!(out, " }}").map_err(fmt_err)?;
1838 } else {
1839 writeln!(
1840 out,
1841 " // xcdr2: array member '{name}' (multi-dim string-1D-only / unsupported elem) not supported (skip)"
1842 )
1843 .map_err(fmt_err)?;
1844 }
1845 continue;
1846 }
1847 if !typespec_supported(&m.type_spec) {
1848 writeln!(
1849 out,
1850 " // xcdr2: member '{name}' not supported (nested/enum/map/fixed; skip)"
1851 )
1852 .map_err(fmt_err)?;
1853 continue;
1854 }
1855 if is_optional {
1856 writeln!(out, " if (__v.{name}().has_value()) {{").map_err(fmt_err)?;
1858 writeln!(out, " __out.push_back(uint8_t{{1}});").map_err(fmt_err)?;
1859 emit_value_write(
1860 out,
1861 &m.type_spec,
1862 &format!("(*__v.{name}())"),
1863 endian,
1864 origin,
1865 " ",
1866 )?;
1867 writeln!(out, " }} else {{").map_err(fmt_err)?;
1868 writeln!(out, " __out.push_back(uint8_t{{0}});").map_err(fmt_err)?;
1869 writeln!(out, " }}").map_err(fmt_err)?;
1870 } else {
1871 emit_value_write(
1872 out,
1873 &m.type_spec,
1874 &format!("__v.{name}()"),
1875 endian,
1876 origin,
1877 " ",
1878 )?;
1879 }
1880 }
1881 Ok(())
1882}
1883
1884fn emit_value_write(
1888 out: &mut String,
1889 ts: &TypeSpec,
1890 access: &str,
1891 endian: &str,
1892 origin: &str,
1893 indent: &str,
1894) -> Result<(), CppGenError> {
1895 let pre = format!("{indent} ");
1896 match ts {
1897 TypeSpec::Primitive(PrimitiveType::Boolean) => {
1898 writeln!(
1899 out,
1900 "{pre}::dds::topic::xcdr2::write_bool(__out, {access});"
1901 )
1902 .map_err(fmt_err)?;
1903 }
1904 TypeSpec::Primitive(PrimitiveType::Octet) => {
1905 writeln!(out, "{pre}::dds::topic::xcdr2::write_u8(__out, {access});")
1906 .map_err(fmt_err)?;
1907 }
1908 TypeSpec::Primitive(p) => {
1909 let cpp_ty = primitive_to_cpp(*p);
1910 if endian == "be" {
1911 writeln!(
1912 out,
1913 "{pre}::dds::topic::xcdr2::write_be_origin<{cpp_ty}>(__out, {origin}, {access});"
1914 )
1915 .map_err(fmt_err)?;
1916 } else {
1917 writeln!(
1919 out,
1920 "{pre}::dds::topic::xcdr2::write_le_origin<{cpp_ty}>(__out, {origin}, {access}, __max_align);"
1921 )
1922 .map_err(fmt_err)?;
1923 }
1924 }
1925 TypeSpec::String(s) if !s.wide => {
1926 if let Some(b) = &s.bound {
1929 let bv = const_expr_to_cpp(b);
1930 writeln!(
1931 out,
1932 "{pre}if ({access}.size() > {bv}) throw std::length_error(\"bounded string length exceeds its IDL bound ({bv})\");"
1933 )
1934 .map_err(fmt_err)?;
1935 }
1936 if endian == "be" {
1937 writeln!(
1938 out,
1939 "{pre}::dds::topic::xcdr2::write_string_be(__out, {access});"
1940 )
1941 .map_err(fmt_err)?;
1942 } else {
1943 writeln!(
1944 out,
1945 "{pre}::dds::topic::xcdr2::write_string_origin(__out, {origin}, {access}, __max_align);"
1946 )
1947 .map_err(fmt_err)?;
1948 }
1949 }
1950 TypeSpec::String(s) if s.wide => {
1951 if let Some(b) = &s.bound {
1954 let bv = const_expr_to_cpp(b);
1955 writeln!(
1956 out,
1957 "{pre}if ({access}.size() > {bv}) throw std::length_error(\"bounded wstring length exceeds its IDL bound ({bv})\");"
1958 )
1959 .map_err(fmt_err)?;
1960 }
1961 if endian == "be" {
1962 writeln!(
1963 out,
1964 "{pre}::dds::topic::xcdr2::write_wstring_be(__out, {access});"
1965 )
1966 .map_err(fmt_err)?;
1967 } else {
1968 writeln!(
1969 out,
1970 "{pre}::dds::topic::xcdr2::write_wstring_origin(__out, {origin}, {access}, __max_align);"
1971 )
1972 .map_err(fmt_err)?;
1973 }
1974 }
1975 TypeSpec::Sequence(seq) => {
1976 if let Some(b) = &seq.bound {
1980 let bv = const_expr_to_cpp(b);
1981 writeln!(
1982 out,
1983 "{pre}if ({access}.size() > {bv}) throw std::length_error(\"bounded sequence length exceeds its IDL bound ({bv})\");"
1984 )
1985 .map_err(fmt_err)?;
1986 }
1987 if matches!(&*seq.elem, TypeSpec::Primitive(PrimitiveType::Octet)) {
1988 if endian == "be" {
1990 writeln!(out, "{pre}::dds::topic::xcdr2::write_be<uint32_t>(__out, static_cast<uint32_t>({access}.size()));").map_err(fmt_err)?;
1991 } else {
1992 writeln!(out, "{pre}::dds::topic::xcdr2::write_le_origin<uint32_t>(__out, {origin}, static_cast<uint32_t>({access}.size()), __max_align);").map_err(fmt_err)?;
1993 }
1994 writeln!(
1995 out,
1996 "{pre}__out.insert(__out.end(), {access}.begin(), {access}.end());"
1997 )
1998 .map_err(fmt_err)?;
1999 return Ok(());
2000 }
2001 let seq_non_primitive = !matches!(&*seq.elem, TypeSpec::Primitive(_));
2006 if seq_non_primitive {
2007 writeln!(out, "{pre}{{").map_err(fmt_err)?;
2008 writeln!(
2009 out,
2010 "{pre}const auto __seq_dh = ::dds::topic::xcdr2::dheader_begin(__out);"
2011 )
2012 .map_err(fmt_err)?;
2013 }
2014 let count_call = if endian == "be" {
2015 format!(
2016 "{pre}::dds::topic::xcdr2::write_be<uint32_t>(__out, static_cast<uint32_t>({access}.size()));"
2017 )
2018 } else {
2019 format!(
2020 "{pre}::dds::topic::xcdr2::write_le_origin<uint32_t>(__out, {origin}, static_cast<uint32_t>({access}.size()), __max_align);"
2021 )
2022 };
2023 writeln!(out, "{count_call}").map_err(fmt_err)?;
2024 writeln!(out, "{pre}for (const auto& __e : {access}) {{").map_err(fmt_err)?;
2025 let elem_indent = format!("{pre} ");
2026 match &*seq.elem {
2027 TypeSpec::Primitive(PrimitiveType::Boolean) => {
2028 writeln!(
2029 out,
2030 "{elem_indent}::dds::topic::xcdr2::write_bool(__out, __e);"
2031 )
2032 .map_err(fmt_err)?;
2033 }
2034 TypeSpec::Primitive(PrimitiveType::Octet) => {
2035 writeln!(
2036 out,
2037 "{elem_indent}::dds::topic::xcdr2::write_u8(__out, __e);"
2038 )
2039 .map_err(fmt_err)?;
2040 }
2041 TypeSpec::Primitive(p) => {
2042 let cpp_ty = primitive_to_cpp(*p);
2043 if endian == "be" {
2044 writeln!(
2045 out,
2046 "{elem_indent}::dds::topic::xcdr2::write_be<{cpp_ty}>(__out, __e);"
2047 )
2048 .map_err(fmt_err)?;
2049 } else {
2050 writeln!(
2051 out,
2052 "{elem_indent}::dds::topic::xcdr2::write_le_origin<{cpp_ty}>(__out, {origin}, __e, __max_align);"
2053 )
2054 .map_err(fmt_err)?;
2055 }
2056 }
2057 TypeSpec::String(s) if !s.wide => {
2058 if endian == "be" {
2059 writeln!(
2060 out,
2061 "{elem_indent}::dds::topic::xcdr2::write_string_be(__out, __e);"
2062 )
2063 .map_err(fmt_err)?;
2064 } else {
2065 writeln!(
2066 out,
2067 "{elem_indent}::dds::topic::xcdr2::write_string_origin(__out, {origin}, __e, __max_align);"
2068 )
2069 .map_err(fmt_err)?;
2070 }
2071 }
2072 TypeSpec::String(_) => {
2074 emit_value_write(out, &seq.elem, "__e", endian, origin, &elem_indent)?;
2075 }
2076 TypeSpec::Scoped(sc) if scoped_is_enum(sc) || scoped_struct(sc).is_some() => {
2081 emit_value_write(out, &seq.elem, "__e", endian, origin, &elem_indent)?;
2082 }
2083 TypeSpec::Sequence(_) => {
2086 emit_value_write(out, &seq.elem, "__e", endian, origin, &elem_indent)?;
2087 }
2088 TypeSpec::Map(_) => {
2091 emit_value_write(out, &seq.elem, "__e", endian, origin, &elem_indent)?;
2092 }
2093 _ => {
2094 writeln!(
2095 out,
2096 "{elem_indent}// xcdr2: nested sequence-element not supported"
2097 )
2098 .map_err(fmt_err)?;
2099 }
2100 }
2101 writeln!(out, "{pre}}}").map_err(fmt_err)?;
2102 if seq_non_primitive {
2103 writeln!(
2104 out,
2105 "{pre}::dds::topic::xcdr2::dheader_end(__out, __seq_dh);"
2106 )
2107 .map_err(fmt_err)?;
2108 writeln!(out, "{pre}}}").map_err(fmt_err)?;
2109 }
2110 }
2111 TypeSpec::Map(m) => {
2117 if let Some(b) = &m.bound {
2118 let bv = const_expr_to_cpp(b);
2119 writeln!(
2120 out,
2121 "{pre}if ({access}.size() > {bv}) throw std::length_error(\"bounded map length exceeds its IDL bound ({bv})\");"
2122 )
2123 .map_err(fmt_err)?;
2124 }
2125 writeln!(out, "{pre}{{").map_err(fmt_err)?;
2126 writeln!(
2127 out,
2128 "{pre}const auto __map_dh = ::dds::topic::xcdr2::dheader_begin(__out);"
2129 )
2130 .map_err(fmt_err)?;
2131 if endian == "be" {
2132 writeln!(out, "{pre}::dds::topic::xcdr2::write_be<uint32_t>(__out, static_cast<uint32_t>({access}.size()));").map_err(fmt_err)?;
2133 } else {
2134 writeln!(out, "{pre}::dds::topic::xcdr2::write_le_origin<uint32_t>(__out, {origin}, static_cast<uint32_t>({access}.size()), __max_align);").map_err(fmt_err)?;
2135 }
2136 writeln!(out, "{pre}for (const auto& __kv : {access}) {{").map_err(fmt_err)?;
2137 let kv_indent = format!("{pre} ");
2138 emit_value_write(out, &m.key, "__kv.first", endian, origin, &kv_indent)?;
2139 emit_value_write(out, &m.value, "__kv.second", endian, origin, &kv_indent)?;
2140 writeln!(out, "{pre}}}").map_err(fmt_err)?;
2141 writeln!(
2142 out,
2143 "{pre}::dds::topic::xcdr2::dheader_end(__out, __map_dh);"
2144 )
2145 .map_err(fmt_err)?;
2146 writeln!(out, "{pre}}}").map_err(fmt_err)?;
2147 }
2148 TypeSpec::Scoped(s) if scoped_is_enum(s) => {
2150 if endian == "be" {
2151 writeln!(
2152 out,
2153 "{pre}::dds::topic::xcdr2::write_be<int32_t>(__out, static_cast<int32_t>({access}));"
2154 )
2155 .map_err(fmt_err)?;
2156 } else {
2157 writeln!(
2158 out,
2159 "{pre}::dds::topic::xcdr2::write_le_origin<int32_t>(__out, {origin}, static_cast<int32_t>({access}), __max_align);"
2160 )
2161 .map_err(fmt_err)?;
2162 }
2163 }
2164 TypeSpec::Scoped(sc) if scoped_struct(sc).is_some() => {
2169 let Some((def, ext)) = scoped_struct(sc) else {
2170 return Ok(());
2171 };
2172 match ext {
2173 Extensibility::Final => {
2174 for sm in &def.members {
2175 let sm_name = &sm.declarators[0].name().text;
2176 emit_value_write(
2177 out,
2178 &sm.type_spec,
2179 &format!("{access}.{sm_name}()"),
2180 endian,
2181 origin,
2182 &pre,
2183 )?;
2184 }
2185 }
2186 Extensibility::Appendable | Extensibility::Mutable => {
2187 let cpp = scoped_to_cpp(sc);
2188 let id = next_nest_id();
2189 writeln!(out, "{pre}{{").map_err(fmt_err)?;
2190 writeln!(
2191 out,
2192 "{pre} ::dds::topic::xcdr2::pad_to_from_origin(__out, {origin}, 4);"
2193 )
2194 .map_err(fmt_err)?;
2195 if endian == "be" {
2196 writeln!(
2197 out,
2198 "{pre} auto __nsb{id} = ::dds::topic::topic_type_support<{cpp}>::encode_be({access});"
2199 )
2200 .map_err(fmt_err)?;
2201 } else {
2202 writeln!(
2203 out,
2204 "{pre} auto __nsb{id} = ::dds::topic::topic_type_support<{cpp}>::encode({access}, __repr);"
2205 )
2206 .map_err(fmt_err)?;
2207 }
2208 writeln!(
2209 out,
2210 "{pre} __out.insert(__out.end(), __nsb{id}.begin(), __nsb{id}.end());"
2211 )
2212 .map_err(fmt_err)?;
2213 writeln!(out, "{pre}}}").map_err(fmt_err)?;
2214 }
2215 }
2216 }
2217 _ => {
2218 writeln!(out, "{pre}// xcdr2: member type not supported (skip)").map_err(fmt_err)?;
2219 }
2220 }
2221 Ok(())
2222}
2223
2224fn emit_mutable_member_encode(
2226 out: &mut String,
2227 m: &Member,
2228 endian: &str,
2229) -> Result<(), CppGenError> {
2230 if !member_codegen_supported(m) {
2231 for decl in &m.declarators {
2232 let name = &decl.name().text;
2233 writeln!(
2234 out,
2235 " // xcdr2: @shared member '{name}' not supported (skip)"
2236 )
2237 .map_err(fmt_err)?;
2238 }
2239 return Ok(());
2240 }
2241 let is_optional = has_optional_annotation(&m.annotations);
2242 let must_understand = has_named_annotation(&m.annotations, "must_understand");
2243 let id_override = find_uint_annotation(&m.annotations, "id");
2244 let mu_lit = if must_understand { "true" } else { "false" };
2245
2246 for (idx, decl) in m.declarators.iter().enumerate() {
2247 let name = &decl.name().text;
2248 if !matches!(decl, Declarator::Simple(_)) {
2249 writeln!(
2250 out,
2251 " // xcdr2: array member '{name}' not supported (skip)"
2252 )
2253 .map_err(fmt_err)?;
2254 continue;
2255 }
2256 if !typespec_supported(&m.type_spec) {
2257 writeln!(
2258 out,
2259 " // xcdr2: member '{name}' not supported (skip)"
2260 )
2261 .map_err(fmt_err)?;
2262 continue;
2263 }
2264 let _ = idx;
2268 let id_expr = match id_override {
2269 Some(id) => id.to_string(),
2270 None => format!("0x{:x}u", auto_id_for(name)),
2271 };
2272 if is_optional {
2273 writeln!(out, " if (__v.{name}().has_value()) {{").map_err(fmt_err)?;
2275 emit_mutable_value_emit(
2276 out,
2277 &m.type_spec,
2278 &format!("(*__v.{name}())"),
2279 &id_expr,
2280 mu_lit,
2281 endian,
2282 " ",
2283 )?;
2284 writeln!(out, " }}").map_err(fmt_err)?;
2285 } else {
2286 emit_mutable_value_emit(
2287 out,
2288 &m.type_spec,
2289 &format!("__v.{name}()"),
2290 &id_expr,
2291 mu_lit,
2292 endian,
2293 " ",
2294 )?;
2295 }
2296 }
2297 Ok(())
2298}
2299
2300fn auto_id_for(name: &str) -> u32 {
2304 let mut h: u32 = 0x811C9DC5;
2306 for b in name.as_bytes() {
2307 h ^= u32::from(*b);
2308 h = h.wrapping_mul(0x01000193);
2309 }
2310 h & 0x0FFF_FFFF
2311}
2312
2313fn emit_mutable_value_emit(
2314 out: &mut String,
2315 ts: &TypeSpec,
2316 access: &str,
2317 id_expr: &str,
2318 mu_lit: &str,
2319 endian: &str,
2320 indent: &str,
2321) -> Result<(), CppGenError> {
2322 match ts {
2323 TypeSpec::Primitive(PrimitiveType::Boolean) => {
2324 writeln!(
2325 out,
2326 "{indent}::dds::topic::xcdr2::emheader_u8(__out, __origin, {id_expr}, {mu_lit}, static_cast<uint8_t>({access} ? 1 : 0));"
2327 )
2328 .map_err(fmt_err)?;
2329 }
2330 TypeSpec::Primitive(PrimitiveType::Octet) => {
2331 writeln!(
2332 out,
2333 "{indent}::dds::topic::xcdr2::emheader_u8(__out, __origin, {id_expr}, {mu_lit}, {access});"
2334 )
2335 .map_err(fmt_err)?;
2336 }
2337 TypeSpec::Primitive(p) => {
2338 let cpp_ty = primitive_to_cpp(*p);
2339 let size = primitive_size(*p);
2341 match size {
2342 2 => {
2343 writeln!(
2344 out,
2345 "{indent}::dds::topic::xcdr2::emheader_2<{cpp_ty}>(__out, __origin, {id_expr}, {mu_lit}, {access});"
2346 )
2347 .map_err(fmt_err)?;
2348 }
2349 4 => {
2350 writeln!(
2351 out,
2352 "{indent}::dds::topic::xcdr2::emheader_4<{cpp_ty}>(__out, __origin, {id_expr}, {mu_lit}, {access});"
2353 )
2354 .map_err(fmt_err)?;
2355 }
2356 8 => {
2357 writeln!(
2358 out,
2359 "{indent}::dds::topic::xcdr2::emheader_8<{cpp_ty}>(__out, __origin, {id_expr}, {mu_lit}, {access});"
2360 )
2361 .map_err(fmt_err)?;
2362 }
2363 _ => {
2364 writeln!(
2365 out,
2366 "{indent}// xcdr2: unexpected primitive size {size} (skip)"
2367 )
2368 .map_err(fmt_err)?;
2369 }
2370 }
2371 }
2372 TypeSpec::String(s) if !s.wide => {
2373 if let Some(b) = &s.bound {
2375 let bv = const_expr_to_cpp(b);
2376 writeln!(
2377 out,
2378 "{indent}if ({access}.size() > {bv}) throw std::length_error(\"bounded string length exceeds its IDL bound ({bv})\");"
2379 )
2380 .map_err(fmt_err)?;
2381 }
2382 writeln!(
2384 out,
2385 "{indent}{{ const auto __sub = ::dds::topic::xcdr2::emheader_nextint_begin(__out, __origin, {id_expr}, {mu_lit});"
2386 )
2387 .map_err(fmt_err)?;
2388 let body_endian = if endian == "be" { "be" } else { "le" };
2391 let _ = body_endian;
2392 writeln!(
2393 out,
2394 "{indent} {{ const auto __body_origin = __sub.body_start; (void)__body_origin;"
2395 )
2396 .map_err(fmt_err)?;
2397 if endian == "be" {
2398 writeln!(
2399 out,
2400 "{indent} ::dds::topic::xcdr2::write_string_be(__out, {access});"
2401 )
2402 .map_err(fmt_err)?;
2403 } else {
2404 writeln!(
2405 out,
2406 "{indent} ::dds::topic::xcdr2::write_string_origin(__out, __body_origin, {access}, __max_align);"
2407 )
2408 .map_err(fmt_err)?;
2409 }
2410 writeln!(out, "{indent} }}").map_err(fmt_err)?;
2411 writeln!(
2412 out,
2413 "{indent} ::dds::topic::xcdr2::emheader_nextint_end(__out, __sub); }}"
2414 )
2415 .map_err(fmt_err)?;
2416 }
2417 TypeSpec::String(s) if s.wide => {
2418 if let Some(b) = &s.bound {
2420 let bv = const_expr_to_cpp(b);
2421 writeln!(
2422 out,
2423 "{indent}if ({access}.size() > {bv}) throw std::length_error(\"bounded wstring length exceeds its IDL bound ({bv})\");"
2424 )
2425 .map_err(fmt_err)?;
2426 }
2427 writeln!(
2429 out,
2430 "{indent}{{ const auto __sub = ::dds::topic::xcdr2::emheader_nextint_begin(__out, __origin, {id_expr}, {mu_lit});"
2431 )
2432 .map_err(fmt_err)?;
2433 writeln!(
2434 out,
2435 "{indent} {{ const auto __body_origin = __sub.body_start; (void)__body_origin;"
2436 )
2437 .map_err(fmt_err)?;
2438 if endian == "be" {
2439 writeln!(
2440 out,
2441 "{indent} ::dds::topic::xcdr2::write_wstring_be(__out, {access});"
2442 )
2443 .map_err(fmt_err)?;
2444 } else {
2445 writeln!(
2446 out,
2447 "{indent} ::dds::topic::xcdr2::write_wstring_origin(__out, __body_origin, {access}, __max_align);"
2448 )
2449 .map_err(fmt_err)?;
2450 }
2451 writeln!(out, "{indent} }}").map_err(fmt_err)?;
2452 writeln!(
2453 out,
2454 "{indent} ::dds::topic::xcdr2::emheader_nextint_end(__out, __sub); }}"
2455 )
2456 .map_err(fmt_err)?;
2457 }
2458 TypeSpec::Sequence(seq) => {
2459 if let Some(b) = &seq.bound {
2461 let bv = const_expr_to_cpp(b);
2462 writeln!(
2463 out,
2464 "{indent}if ({access}.size() > {bv}) throw std::length_error(\"bounded sequence length exceeds its IDL bound ({bv})\");"
2465 )
2466 .map_err(fmt_err)?;
2467 }
2468 writeln!(
2469 out,
2470 "{indent}{{ const auto __sub = ::dds::topic::xcdr2::emheader_nextint_begin(__out, __origin, {id_expr}, {mu_lit});"
2471 )
2472 .map_err(fmt_err)?;
2473 writeln!(
2474 out,
2475 "{indent} {{ const auto __body_origin = __sub.body_start; (void)__body_origin;"
2476 )
2477 .map_err(fmt_err)?;
2478 let seq_inner_dh = !matches!(&*seq.elem, TypeSpec::Primitive(_));
2483 if seq_inner_dh {
2484 writeln!(
2485 out,
2486 "{indent} const auto __seq_dh = ::dds::topic::xcdr2::dheader_begin(__out);"
2487 )
2488 .map_err(fmt_err)?;
2489 }
2490 if endian == "be" {
2491 writeln!(
2492 out,
2493 "{indent} ::dds::topic::xcdr2::write_be<uint32_t>(__out, static_cast<uint32_t>({access}.size()));"
2494 )
2495 .map_err(fmt_err)?;
2496 } else {
2497 writeln!(
2498 out,
2499 "{indent} ::dds::topic::xcdr2::write_le_origin<uint32_t>(__out, __body_origin, static_cast<uint32_t>({access}.size()), __max_align);"
2500 )
2501 .map_err(fmt_err)?;
2502 }
2503 if matches!(&*seq.elem, TypeSpec::Primitive(PrimitiveType::Octet)) {
2504 writeln!(
2506 out,
2507 "{indent} __out.insert(__out.end(), {access}.begin(), {access}.end());"
2508 )
2509 .map_err(fmt_err)?;
2510 } else {
2511 writeln!(out, "{indent} for (const auto& __e : {access}) {{")
2512 .map_err(fmt_err)?;
2513 match &*seq.elem {
2514 TypeSpec::Primitive(PrimitiveType::Boolean) => {
2515 writeln!(
2516 out,
2517 "{indent} ::dds::topic::xcdr2::write_bool(__out, __e);"
2518 )
2519 .map_err(fmt_err)?;
2520 }
2521 TypeSpec::Primitive(PrimitiveType::Octet) => {
2522 writeln!(
2523 out,
2524 "{indent} ::dds::topic::xcdr2::write_u8(__out, __e);"
2525 )
2526 .map_err(fmt_err)?;
2527 }
2528 TypeSpec::Primitive(p) => {
2529 let cpp_ty = primitive_to_cpp(*p);
2530 if endian == "be" {
2531 writeln!(
2532 out,
2533 "{indent} ::dds::topic::xcdr2::write_be<{cpp_ty}>(__out, __e);"
2534 )
2535 .map_err(fmt_err)?;
2536 } else {
2537 writeln!(out, "{indent} ::dds::topic::xcdr2::write_le_origin<{cpp_ty}>(__out, __body_origin, __e, __max_align);").map_err(fmt_err)?;
2538 }
2539 }
2540 TypeSpec::String(s) if !s.wide => {
2541 if endian == "be" {
2542 writeln!(
2543 out,
2544 "{indent} ::dds::topic::xcdr2::write_string_be(__out, __e);"
2545 )
2546 .map_err(fmt_err)?;
2547 } else {
2548 writeln!(out, "{indent} ::dds::topic::xcdr2::write_string_origin(__out, __body_origin, __e, __max_align);").map_err(fmt_err)?;
2549 }
2550 }
2551 TypeSpec::String(_) => {
2555 emit_value_write(
2556 out,
2557 &seq.elem,
2558 "__e",
2559 endian,
2560 "__body_origin",
2561 &format!("{indent} "),
2562 )?;
2563 }
2564 TypeSpec::Scoped(sc) if scoped_is_enum(sc) || scoped_struct(sc).is_some() => {
2565 emit_value_write(
2566 out,
2567 &seq.elem,
2568 "__e",
2569 endian,
2570 "__body_origin",
2571 &format!("{indent} "),
2572 )?;
2573 }
2574 TypeSpec::Sequence(_) | TypeSpec::Map(_) => {
2576 emit_value_write(
2577 out,
2578 &seq.elem,
2579 "__e",
2580 endian,
2581 "__body_origin",
2582 &format!("{indent} "),
2583 )?;
2584 }
2585 _ => {
2586 writeln!(
2587 out,
2588 "{indent} // xcdr2: nested seq-elem not supported"
2589 )
2590 .map_err(fmt_err)?;
2591 }
2592 }
2593 writeln!(out, "{indent} }}").map_err(fmt_err)?;
2594 }
2595 if seq_inner_dh {
2596 writeln!(
2597 out,
2598 "{indent} ::dds::topic::xcdr2::dheader_end(__out, __seq_dh);"
2599 )
2600 .map_err(fmt_err)?;
2601 }
2602 writeln!(out, "{indent} }}").map_err(fmt_err)?;
2603 writeln!(
2604 out,
2605 "{indent} ::dds::topic::xcdr2::emheader_nextint_end(__out, __sub); }}"
2606 )
2607 .map_err(fmt_err)?;
2608 }
2609 TypeSpec::Scoped(s) if scoped_is_enum(s) => {
2611 writeln!(
2612 out,
2613 "{indent}::dds::topic::xcdr2::emheader_4<int32_t>(__out, __origin, {id_expr}, {mu_lit}, static_cast<int32_t>({access}));"
2614 )
2615 .map_err(fmt_err)?;
2616 }
2617 TypeSpec::Scoped(sc) if scoped_struct(sc).is_some() => {
2623 let Some((def, ext)) = scoped_struct(sc) else {
2624 return Ok(());
2625 };
2626 writeln!(
2627 out,
2628 "{indent}{{ const auto __sub = ::dds::topic::xcdr2::emheader_nextint_begin(__out, __origin, {id_expr}, {mu_lit});"
2629 )
2630 .map_err(fmt_err)?;
2631 match ext {
2632 Extensibility::Final => {
2633 writeln!(
2634 out,
2635 "{indent} {{ const auto __body_origin = __sub.body_start; (void)__body_origin;"
2636 )
2637 .map_err(fmt_err)?;
2638 for sm in &def.members {
2639 let sm_name = &sm.declarators[0].name().text;
2640 emit_value_write(
2641 out,
2642 &sm.type_spec,
2643 &format!("{access}.{sm_name}()"),
2644 endian,
2645 "__body_origin",
2646 &format!("{indent} "),
2647 )?;
2648 }
2649 writeln!(out, "{indent} }}").map_err(fmt_err)?;
2650 }
2651 Extensibility::Appendable | Extensibility::Mutable => {
2652 let cpp = scoped_to_cpp(sc);
2653 let id = next_nest_id();
2654 writeln!(
2655 out,
2656 "{indent} {{ ::dds::topic::xcdr2::pad_to_from_origin(__out, __sub.body_start, 4);"
2657 )
2658 .map_err(fmt_err)?;
2659 if endian == "be" {
2660 writeln!(
2661 out,
2662 "{indent} auto __nsb{id} = ::dds::topic::topic_type_support<{cpp}>::encode_be({access});"
2663 )
2664 .map_err(fmt_err)?;
2665 } else {
2666 writeln!(
2667 out,
2668 "{indent} auto __nsb{id} = ::dds::topic::topic_type_support<{cpp}>::encode({access}, __repr);"
2669 )
2670 .map_err(fmt_err)?;
2671 }
2672 writeln!(
2673 out,
2674 "{indent} __out.insert(__out.end(), __nsb{id}.begin(), __nsb{id}.end()); }}"
2675 )
2676 .map_err(fmt_err)?;
2677 }
2678 }
2679 writeln!(
2680 out,
2681 "{indent} ::dds::topic::xcdr2::emheader_nextint_end(__out, __sub); }}"
2682 )
2683 .map_err(fmt_err)?;
2684 }
2685 TypeSpec::Map(m) => {
2691 writeln!(
2692 out,
2693 "{indent}{{ const auto __sub = ::dds::topic::xcdr2::emheader_nextint_begin(__out, __origin, {id_expr}, {mu_lit});"
2694 )
2695 .map_err(fmt_err)?;
2696 writeln!(
2697 out,
2698 "{indent} {{ const auto __body_origin = __sub.body_start; (void)__body_origin;"
2699 )
2700 .map_err(fmt_err)?;
2701 writeln!(
2702 out,
2703 "{indent} const auto __map_dh = ::dds::topic::xcdr2::dheader_begin(__out);"
2704 )
2705 .map_err(fmt_err)?;
2706 if endian == "be" {
2707 writeln!(out, "{indent} ::dds::topic::xcdr2::write_be<uint32_t>(__out, static_cast<uint32_t>({access}.size()));").map_err(fmt_err)?;
2708 } else {
2709 writeln!(out, "{indent} ::dds::topic::xcdr2::write_le_origin<uint32_t>(__out, __body_origin, static_cast<uint32_t>({access}.size()), __max_align);").map_err(fmt_err)?;
2710 }
2711 writeln!(out, "{indent} for (const auto& __kv : {access}) {{").map_err(fmt_err)?;
2712 let kv_indent = format!("{indent} ");
2713 emit_value_write(
2714 out,
2715 &m.key,
2716 "__kv.first",
2717 endian,
2718 "__body_origin",
2719 &kv_indent,
2720 )?;
2721 emit_value_write(
2722 out,
2723 &m.value,
2724 "__kv.second",
2725 endian,
2726 "__body_origin",
2727 &kv_indent,
2728 )?;
2729 writeln!(out, "{indent} }}").map_err(fmt_err)?;
2730 writeln!(
2731 out,
2732 "{indent} ::dds::topic::xcdr2::dheader_end(__out, __map_dh);"
2733 )
2734 .map_err(fmt_err)?;
2735 writeln!(out, "{indent} }}").map_err(fmt_err)?;
2736 writeln!(
2737 out,
2738 "{indent} ::dds::topic::xcdr2::emheader_nextint_end(__out, __sub); }}"
2739 )
2740 .map_err(fmt_err)?;
2741 }
2742 _ => {
2743 writeln!(out, "{indent}// xcdr2: unsupported member type").map_err(fmt_err)?;
2744 }
2745 }
2746 Ok(())
2747}
2748
2749fn primitive_size(p: PrimitiveType) -> usize {
2750 use zerodds_idl::ast::{FloatingType, IntegerType};
2751 match p {
2752 PrimitiveType::Boolean => 1,
2753 PrimitiveType::Octet => 1,
2754 PrimitiveType::Char => 1,
2755 PrimitiveType::WideChar => 2,
2756 PrimitiveType::Integer(i) => match i {
2757 IntegerType::Int8 | IntegerType::UInt8 => 1,
2758 IntegerType::Short | IntegerType::UShort | IntegerType::Int16 | IntegerType::UInt16 => {
2759 2
2760 }
2761 IntegerType::Long | IntegerType::ULong | IntegerType::Int32 | IntegerType::UInt32 => 4,
2762 IntegerType::LongLong
2763 | IntegerType::ULongLong
2764 | IntegerType::Int64
2765 | IntegerType::UInt64 => 8,
2766 },
2767 PrimitiveType::Floating(f) => match f {
2768 FloatingType::Float => 4,
2769 FloatingType::Double => 8,
2770 FloatingType::LongDouble => 16,
2771 },
2772 }
2773}
2774
2775fn emit_decode_fn(
2776 out: &mut String,
2777 cpp_fqn: &str,
2778 s: &StructDef,
2779 ext: Extensibility,
2780) -> Result<(), CppGenError> {
2781 writeln!(
2782 out,
2783 " static {cpp_fqn} decode(const uint8_t* __buf, size_t __len, \
2784 ::dds::topic::xcdr2::XcdrVersion __repr) {{"
2785 )
2786 .map_err(fmt_err)?;
2787 writeln!(out, " size_t __pos = 0;").map_err(fmt_err)?;
2788 writeln!(out, " {cpp_fqn} __v;").map_err(fmt_err)?;
2789 writeln!(
2792 out,
2793 " const size_t __max_align = ::dds::topic::xcdr2::xcdr_max_align(__repr);"
2794 )
2795 .map_err(fmt_err)?;
2796 writeln!(
2797 out,
2798 " (void)__buf; (void)__len; (void)__pos; (void)__max_align;"
2799 )
2800 .map_err(fmt_err)?;
2801
2802 match ext {
2803 Extensibility::Final => {
2804 writeln!(out, " const size_t __origin = 0;").map_err(fmt_err)?;
2805 writeln!(out, " (void)__origin;").map_err(fmt_err)?;
2806 for m in &s.members {
2807 emit_plain_member_decode(out, m, "__origin")?;
2808 }
2809 }
2810 Extensibility::Appendable => {
2811 writeln!(
2812 out,
2813 " const auto __dh = ::dds::topic::xcdr2::dheader_read(__buf, __pos, __len);"
2814 )
2815 .map_err(fmt_err)?;
2816 writeln!(out, " const size_t __origin = __pos;").map_err(fmt_err)?;
2817 writeln!(out, " const size_t __end = __origin + __dh;").map_err(fmt_err)?;
2818 writeln!(out, " (void)__end;").map_err(fmt_err)?;
2819 for m in &s.members {
2820 emit_plain_member_decode(out, m, "__origin")?;
2821 }
2822 writeln!(out, " if (__pos < __end) __pos = __end;").map_err(fmt_err)?;
2824 }
2825 Extensibility::Mutable => {
2826 writeln!(
2827 out,
2828 " const auto __dh = ::dds::topic::xcdr2::dheader_read(__buf, __pos, __len);"
2829 )
2830 .map_err(fmt_err)?;
2831 writeln!(out, " const size_t __origin = __pos;").map_err(fmt_err)?;
2832 writeln!(out, " const size_t __end = __origin + __dh;").map_err(fmt_err)?;
2833 writeln!(out, " while (__pos + 4 <= __end) {{").map_err(fmt_err)?;
2834 writeln!(
2835 out,
2836 " const auto __h = ::dds::topic::xcdr2::emheader_read(__buf, __pos, __len, __origin);"
2837 )
2838 .map_err(fmt_err)?;
2839 writeln!(out, " switch (__h.member_id) {{").map_err(fmt_err)?;
2840 for m in &s.members {
2841 emit_mutable_member_decode_case(out, m)?;
2842 }
2843 writeln!(out, " default: {{").map_err(fmt_err)?;
2844 writeln!(
2845 out,
2846 " // Unknown member: per-LC skip per XTypes 1.3"
2847 )
2848 .map_err(fmt_err)?;
2849 writeln!(
2850 out,
2851 " // §7.4.3.4.2 (LengthCode::body_len). LC0..3 are"
2852 )
2853 .map_err(fmt_err)?;
2854 writeln!(
2855 out,
2856 " // fixed 1/2/4/8-byte bodies WITHOUT NEXTINT; LC4/5 NEXTINT="
2857 )
2858 .map_err(fmt_err)?;
2859 writeln!(
2860 out,
2861 " // byte length; LC6/7 NEXTINT=element count (4 + 4n / 4 + 8n)."
2862 )
2863 .map_err(fmt_err)?;
2864 writeln!(
2865 out,
2866 " if (__h.lc == 0) {{ __pos += 1; }}"
2867 )
2868 .map_err(fmt_err)?;
2869 writeln!(
2870 out,
2871 " else if (__h.lc == 1) {{ __pos += 2; }}"
2872 )
2873 .map_err(fmt_err)?;
2874 writeln!(
2875 out,
2876 " else if (__h.lc == 2) {{ __pos += 4; }}"
2877 )
2878 .map_err(fmt_err)?;
2879 writeln!(
2880 out,
2881 " else if (__h.lc == 3) {{ __pos += 8; }}"
2882 )
2883 .map_err(fmt_err)?;
2884 writeln!(
2885 out,
2886 " else if (__h.lc == 4 || __h.lc == 5) {{ auto __n = ::dds::topic::xcdr2::emheader_nextint_read(__buf, __pos, __len); __pos += __n; }}"
2887 )
2888 .map_err(fmt_err)?;
2889 writeln!(
2890 out,
2891 " else if (__h.lc == 6) {{ auto __c = ::dds::topic::xcdr2::emheader_nextint_read(__buf, __pos, __len); __pos += 4 + 4 * static_cast<size_t>(__c); }}"
2892 )
2893 .map_err(fmt_err)?;
2894 writeln!(
2895 out,
2896 " else {{ auto __c = ::dds::topic::xcdr2::emheader_nextint_read(__buf, __pos, __len); __pos += 4 + 8 * static_cast<size_t>(__c); }}"
2897 )
2898 .map_err(fmt_err)?;
2899 writeln!(out, " break;").map_err(fmt_err)?;
2900 writeln!(out, " }}").map_err(fmt_err)?;
2901 writeln!(out, " }}").map_err(fmt_err)?;
2902 writeln!(out, " }}").map_err(fmt_err)?;
2903 writeln!(out, " if (__pos < __end) __pos = __end;").map_err(fmt_err)?;
2904 }
2905 }
2906
2907 writeln!(out, " return __v;").map_err(fmt_err)?;
2908 writeln!(out, " }}").map_err(fmt_err)?;
2909 Ok(())
2910}
2911
2912fn emit_plain_member_decode(out: &mut String, m: &Member, origin: &str) -> Result<(), CppGenError> {
2913 if !member_codegen_supported(m) {
2914 for decl in &m.declarators {
2915 let name = &decl.name().text;
2916 writeln!(
2917 out,
2918 " // xcdr2: @shared member '{name}' not supported (skip)"
2919 )
2920 .map_err(fmt_err)?;
2921 }
2922 return Ok(());
2923 }
2924 let is_optional = has_optional_annotation(&m.annotations);
2925 for decl in &m.declarators {
2926 let name = &decl.name().text;
2927 if let Declarator::Array(arr) = decl {
2930 let prim = matches!(m.type_spec, TypeSpec::Primitive(_));
2931 let leaf_1d = arr.sizes.len() == 1
2932 && matches!(m.type_spec, TypeSpec::Primitive(_) | TypeSpec::String(_));
2933 let prim_read_expr = || -> String {
2934 match &m.type_spec {
2935 TypeSpec::Primitive(PrimitiveType::Boolean) => {
2936 "::dds::topic::xcdr2::read_bool(__buf, __pos, __len)".to_string()
2937 }
2938 TypeSpec::Primitive(PrimitiveType::Octet) => {
2939 "::dds::topic::xcdr2::read_u8(__buf, __pos, __len)".to_string()
2940 }
2941 TypeSpec::Primitive(p) => format!(
2942 "::dds::topic::xcdr2::read_le_origin<{}>(__buf, __pos, __len, {origin}, __max_align)",
2943 primitive_to_cpp(*p)
2944 ),
2945 TypeSpec::String(s) if s.wide => format!(
2946 "::dds::topic::xcdr2::read_wstring_origin(__buf, __pos, __len, {origin}, __max_align)"
2947 ),
2948 _ => format!(
2949 "::dds::topic::xcdr2::read_string_origin(__buf, __pos, __len, {origin}, __max_align)"
2950 ),
2951 }
2952 };
2953 if leaf_1d {
2954 let read_expr = prim_read_expr();
2955 writeln!(out, " {{").map_err(fmt_err)?;
2956 writeln!(out, " auto __arr = __v.{name}();").map_err(fmt_err)?;
2957 writeln!(
2958 out,
2959 " for (auto& __ae : __arr) {{ __ae = {read_expr}; }}"
2960 )
2961 .map_err(fmt_err)?;
2962 writeln!(out, " __v.{name}(__arr);").map_err(fmt_err)?;
2963 writeln!(out, " }}").map_err(fmt_err)?;
2964 } else if prim && arr.sizes.len() >= 2 {
2965 let read_expr = prim_read_expr();
2968 let n = arr.sizes.len();
2969 writeln!(out, " {{").map_err(fmt_err)?;
2970 writeln!(out, " auto __arr = __v.{name}();").map_err(fmt_err)?;
2971 let mut acc = String::from("__arr");
2972 let mut ind = String::from(" ");
2973 for d in 0..n {
2974 let lv = format!("__a{d}");
2975 writeln!(out, "{ind}for (auto& {lv} : {acc}) {{").map_err(fmt_err)?;
2976 acc = lv;
2977 ind.push_str(" ");
2978 }
2979 writeln!(out, "{ind}{acc} = {read_expr};").map_err(fmt_err)?;
2980 for _ in 0..n {
2981 ind.truncate(ind.len() - 4);
2982 writeln!(out, "{ind}}}").map_err(fmt_err)?;
2983 }
2984 writeln!(out, " __v.{name}(__arr);").map_err(fmt_err)?;
2985 writeln!(out, " }}").map_err(fmt_err)?;
2986 } else if matches!(&m.type_spec, TypeSpec::Scoped(s) if scoped_is_enum(s) || scoped_final_struct(s).is_some())
2987 || (matches!(m.type_spec, TypeSpec::String(_)) && arr.sizes.len() >= 2)
2988 {
2989 let n = arr.sizes.len();
2993 writeln!(out, " {{").map_err(fmt_err)?;
2994 writeln!(out, " const auto __arr_dh = ::dds::topic::xcdr2::dheader_read(__buf, __pos, __len); (void)__arr_dh;").map_err(fmt_err)?;
2995 writeln!(out, " auto __arr = __v.{name}();").map_err(fmt_err)?;
2996 let mut acc = String::from("__arr");
2997 let mut ind = String::from(" ");
2998 for d in 0..n {
2999 let lv = format!("__a{d}");
3000 writeln!(out, "{ind}for (auto& {lv} : {acc}) {{").map_err(fmt_err)?;
3001 acc = lv;
3002 ind.push_str(" ");
3003 }
3004 emit_value_read(out, &m.type_spec, &format!("{acc} ="), origin, &ind, false)?;
3005 for _ in 0..n {
3006 ind.truncate(ind.len() - 4);
3007 writeln!(out, "{ind}}}").map_err(fmt_err)?;
3008 }
3009 writeln!(out, " __v.{name}(__arr);").map_err(fmt_err)?;
3010 writeln!(out, " }}").map_err(fmt_err)?;
3011 } else {
3012 writeln!(
3013 out,
3014 " // xcdr2: array member '{name}' (1-D string only / unsupported elem) not supported (skip)"
3015 )
3016 .map_err(fmt_err)?;
3017 }
3018 continue;
3019 }
3020 if !typespec_supported(&m.type_spec) {
3021 writeln!(
3022 out,
3023 " // xcdr2: member '{name}' not supported (skip)"
3024 )
3025 .map_err(fmt_err)?;
3026 continue;
3027 }
3028 if is_optional {
3029 writeln!(out, " {{").map_err(fmt_err)?;
3030 writeln!(
3031 out,
3032 " uint8_t __present = ::dds::topic::xcdr2::read_u8(__buf, __pos, __len);"
3033 )
3034 .map_err(fmt_err)?;
3035 writeln!(out, " if (__present) {{").map_err(fmt_err)?;
3036 emit_value_read(
3037 out,
3038 &m.type_spec,
3039 &format!("__v.{name}"),
3040 origin,
3041 " ",
3042 true,
3043 )?;
3044 writeln!(out, " }} else {{").map_err(fmt_err)?;
3045 writeln!(out, " __v.{name}(std::nullopt);").map_err(fmt_err)?;
3046 writeln!(out, " }}").map_err(fmt_err)?;
3047 writeln!(out, " }}").map_err(fmt_err)?;
3048 } else {
3049 emit_value_read(
3050 out,
3051 &m.type_spec,
3052 &format!("__v.{name}"),
3053 origin,
3054 " ",
3055 false,
3056 )?;
3057 }
3058 }
3059 Ok(())
3060}
3061
3062fn emit_value_read(
3064 out: &mut String,
3065 ts: &TypeSpec,
3066 setter: &str,
3067 origin: &str,
3068 indent: &str,
3069 is_opt: bool,
3070) -> Result<(), CppGenError> {
3071 let wrap_opt = |v: String| -> String {
3072 if is_opt {
3073 format!("std::optional<decltype({v})>({v})")
3074 } else {
3075 v
3076 }
3077 };
3078 let _ = wrap_opt;
3079 match ts {
3080 TypeSpec::Primitive(PrimitiveType::Boolean) => {
3081 writeln!(
3082 out,
3083 "{indent}{setter}(::dds::topic::xcdr2::read_bool(__buf, __pos, __len));"
3084 )
3085 .map_err(fmt_err)?;
3086 }
3087 TypeSpec::Primitive(PrimitiveType::Octet) => {
3088 writeln!(
3089 out,
3090 "{indent}{setter}(::dds::topic::xcdr2::read_u8(__buf, __pos, __len));"
3091 )
3092 .map_err(fmt_err)?;
3093 }
3094 TypeSpec::Primitive(p) => {
3095 let cpp_ty = primitive_to_cpp(*p);
3096 writeln!(
3097 out,
3098 "{indent}{setter}(::dds::topic::xcdr2::read_le_origin<{cpp_ty}>(__buf, __pos, __len, {origin}, __max_align));"
3099 )
3100 .map_err(fmt_err)?;
3101 }
3102 TypeSpec::String(s) if !s.wide => {
3103 writeln!(
3104 out,
3105 "{indent}{setter}(::dds::topic::xcdr2::read_string_origin(__buf, __pos, __len, {origin}, __max_align));"
3106 )
3107 .map_err(fmt_err)?;
3108 }
3109 TypeSpec::String(s) if s.wide => {
3110 writeln!(
3111 out,
3112 "{indent}{setter}(::dds::topic::xcdr2::read_wstring_origin(__buf, __pos, __len, {origin}, __max_align));"
3113 )
3114 .map_err(fmt_err)?;
3115 }
3116 TypeSpec::Sequence(seq) => {
3117 if matches!(&*seq.elem, TypeSpec::Primitive(PrimitiveType::Octet)) {
3118 writeln!(out, "{indent}{{").map_err(fmt_err)?;
3120 writeln!(out, "{indent} auto __cnt = ::dds::topic::xcdr2::read_le_origin<uint32_t>(__buf, __pos, __len, {origin}, __max_align);").map_err(fmt_err)?;
3121 writeln!(
3122 out,
3123 "{indent} ::dds::topic::xcdr2::check_avail(__pos, __cnt, __len);"
3124 )
3125 .map_err(fmt_err)?;
3126 writeln!(
3127 out,
3128 "{indent} std::vector<uint8_t> __seq(__buf + __pos, __buf + __pos + __cnt);"
3129 )
3130 .map_err(fmt_err)?;
3131 writeln!(out, "{indent} __pos += __cnt;").map_err(fmt_err)?;
3132 writeln!(out, "{indent} {setter}(std::move(__seq));").map_err(fmt_err)?;
3133 writeln!(out, "{indent}}}").map_err(fmt_err)?;
3134 return Ok(());
3135 }
3136 let elem_cpp_ty: String = match &*seq.elem {
3137 TypeSpec::Primitive(PrimitiveType::Boolean) => "bool".to_string(),
3138 TypeSpec::Primitive(p) => primitive_to_cpp(*p).to_string(),
3139 TypeSpec::String(s) if !s.wide => "std::string".to_string(),
3140 TypeSpec::String(_) => "std::wstring".to_string(),
3142 TypeSpec::Scoped(s) if scoped_is_enum(s) => scoped_to_cpp(s),
3145 TypeSpec::Scoped(s) if scoped_struct(s).is_some() => scoped_to_cpp(s),
3146 TypeSpec::Sequence(_) => typespec_to_cpp(&seq.elem)?,
3148 TypeSpec::Map(_) => typespec_to_cpp(&seq.elem)?,
3150 _ => {
3151 writeln!(
3152 out,
3153 "{indent}// xcdr2: nested seq-elem not supported (skip)"
3154 )
3155 .map_err(fmt_err)?;
3156 return Ok(());
3157 }
3158 };
3159 writeln!(out, "{indent}{{").map_err(fmt_err)?;
3160 if !matches!(&*seq.elem, TypeSpec::Primitive(_)) {
3162 writeln!(out, "{indent} const auto __seq_dh = ::dds::topic::xcdr2::dheader_read(__buf, __pos, __len); (void)__seq_dh;").map_err(fmt_err)?;
3163 }
3164 writeln!(out, "{indent} auto __cnt = ::dds::topic::xcdr2::read_le_origin<uint32_t>(__buf, __pos, __len, {origin}, __max_align);").map_err(fmt_err)?;
3165 writeln!(out, "{indent} std::vector<{elem_cpp_ty}> __seq;").map_err(fmt_err)?;
3166 writeln!(out, "{indent} __seq.reserve(__cnt);").map_err(fmt_err)?;
3167 writeln!(
3168 out,
3169 "{indent} for (uint32_t __i = 0; __i < __cnt; ++__i) {{"
3170 )
3171 .map_err(fmt_err)?;
3172 match &*seq.elem {
3173 TypeSpec::Primitive(PrimitiveType::Boolean) => {
3174 writeln!(out, "{indent} __seq.push_back(::dds::topic::xcdr2::read_bool(__buf, __pos, __len));").map_err(fmt_err)?;
3175 }
3176 TypeSpec::Primitive(PrimitiveType::Octet) => {
3177 writeln!(out, "{indent} __seq.push_back(::dds::topic::xcdr2::read_u8(__buf, __pos, __len));").map_err(fmt_err)?;
3178 }
3179 TypeSpec::Primitive(p) => {
3180 let cpp_ty = primitive_to_cpp(*p);
3181 writeln!(out, "{indent} __seq.push_back(::dds::topic::xcdr2::read_le_origin<{cpp_ty}>(__buf, __pos, __len, {origin}, __max_align));").map_err(fmt_err)?;
3182 }
3183 TypeSpec::String(s) if !s.wide => {
3184 writeln!(out, "{indent} __seq.push_back(::dds::topic::xcdr2::read_string_origin(__buf, __pos, __len, {origin}, __max_align));").map_err(fmt_err)?;
3185 }
3186 TypeSpec::String(_) => {
3188 writeln!(out, "{indent} __seq.push_back(::dds::topic::xcdr2::read_wstring_origin(__buf, __pos, __len, {origin}, __max_align));").map_err(fmt_err)?;
3189 }
3190 TypeSpec::Scoped(s) if scoped_is_enum(s) => {
3192 let cpp_ty = scoped_to_cpp(s);
3193 writeln!(out, "{indent} __seq.push_back(static_cast<{cpp_ty}>(::dds::topic::xcdr2::read_le_origin<int32_t>(__buf, __pos, __len, {origin}, __max_align)));").map_err(fmt_err)?;
3194 }
3195 TypeSpec::Scoped(sc) if scoped_final_struct(sc).is_some() => {
3198 if let Some(def) = scoped_final_struct(sc) {
3199 let cpp_ty = scoped_to_cpp(sc);
3200 let var = format!("__se{}", next_nest_id());
3201 let binner = format!("{indent} ");
3202 writeln!(out, "{binner}{cpp_ty} {var}{{}};").map_err(fmt_err)?;
3203 for sm in &def.members {
3204 let sm_name = &sm.declarators[0].name().text;
3205 emit_value_read(
3206 out,
3207 &sm.type_spec,
3208 &format!("{var}.{sm_name}"),
3209 origin,
3210 &binner,
3211 false,
3212 )?;
3213 }
3214 writeln!(out, "{binner}__seq.push_back({var});").map_err(fmt_err)?;
3215 }
3216 }
3217 TypeSpec::Scoped(sc) if scoped_struct(sc).is_some() => {
3221 let cpp_ty = scoped_to_cpp(sc);
3222 let id = next_nest_id();
3223 let var = format!("__se{id}");
3224 let binner = format!("{indent} ");
3225 writeln!(
3226 out,
3227 "{binner}::dds::topic::xcdr2::skip_pad_from_origin(__pos, {origin}, 4);"
3228 )
3229 .map_err(fmt_err)?;
3230 writeln!(out, "{binner}const size_t __nss{id} = __pos;").map_err(fmt_err)?;
3231 writeln!(out, "{binner}size_t __npk{id} = __pos;").map_err(fmt_err)?;
3232 writeln!(out, "{binner}const uint32_t __nl{id} = ::dds::topic::xcdr2::dheader_read(__buf, __npk{id}, __len);").map_err(fmt_err)?;
3233 writeln!(out, "{binner}{cpp_ty} {var} = ::dds::topic::topic_type_support<{cpp_ty}>::decode(__buf + __nss{id}, 4u + __nl{id}, __repr);").map_err(fmt_err)?;
3234 writeln!(out, "{binner}__pos = __nss{id} + 4u + __nl{id};").map_err(fmt_err)?;
3235 writeln!(out, "{binner}__seq.push_back(std::move({var}));").map_err(fmt_err)?;
3236 }
3237 TypeSpec::Sequence(_) => {
3240 let inner_ty = typespec_to_cpp(&seq.elem)?;
3241 let var = format!("__se{}", next_nest_id());
3242 let binner = format!("{indent} ");
3243 writeln!(out, "{binner}{inner_ty} {var}{{}};").map_err(fmt_err)?;
3244 emit_value_read(out, &seq.elem, &format!("{var} ="), origin, &binner, false)?;
3245 writeln!(out, "{binner}__seq.push_back(std::move({var}));").map_err(fmt_err)?;
3246 }
3247 TypeSpec::Map(_) => {
3249 let inner_ty = typespec_to_cpp(&seq.elem)?;
3250 let var = format!("__se{}", next_nest_id());
3251 let binner = format!("{indent} ");
3252 writeln!(out, "{binner}{inner_ty} {var}{{}};").map_err(fmt_err)?;
3253 emit_value_read(out, &seq.elem, &format!("{var} ="), origin, &binner, false)?;
3254 writeln!(out, "{binner}__seq.push_back(std::move({var}));").map_err(fmt_err)?;
3255 }
3256 _ => {}
3257 }
3258 writeln!(out, "{indent} }}").map_err(fmt_err)?;
3259 writeln!(out, "{indent} {setter}(std::move(__seq));").map_err(fmt_err)?;
3260 writeln!(out, "{indent}}}").map_err(fmt_err)?;
3261 }
3262 TypeSpec::Map(m) => {
3268 let k_ty = typespec_to_cpp(&m.key)?;
3269 let v_ty = typespec_to_cpp(&m.value)?;
3270 let id = next_nest_id();
3271 let mapv = format!("__map{id}");
3272 let kv = format!("__mk{id}");
3273 let vv = format!("__mv{id}");
3274 let inner = format!("{indent} ");
3275 let li = format!("{inner} ");
3276 writeln!(out, "{indent}{{").map_err(fmt_err)?;
3277 writeln!(out, "{inner}const auto __map_dh = ::dds::topic::xcdr2::dheader_read(__buf, __pos, __len); (void)__map_dh;").map_err(fmt_err)?;
3278 writeln!(out, "{inner}auto __mcnt = ::dds::topic::xcdr2::read_le_origin<uint32_t>(__buf, __pos, __len, {origin}, __max_align);").map_err(fmt_err)?;
3279 writeln!(out, "{inner}std::map<{k_ty}, {v_ty}> {mapv};").map_err(fmt_err)?;
3280 writeln!(out, "{inner}for (uint32_t __i = 0; __i < __mcnt; ++__i) {{")
3281 .map_err(fmt_err)?;
3282 writeln!(out, "{li}{k_ty} {kv}{{}};").map_err(fmt_err)?;
3283 writeln!(out, "{li}{v_ty} {vv}{{}};").map_err(fmt_err)?;
3284 emit_value_read(out, &m.key, &format!("{kv} ="), origin, &li, false)?;
3285 emit_value_read(out, &m.value, &format!("{vv} ="), origin, &li, false)?;
3286 writeln!(out, "{li}{mapv}.emplace(std::move({kv}), std::move({vv}));")
3287 .map_err(fmt_err)?;
3288 writeln!(out, "{inner}}}").map_err(fmt_err)?;
3289 writeln!(out, "{inner}{setter}(std::move({mapv}));").map_err(fmt_err)?;
3290 writeln!(out, "{indent}}}").map_err(fmt_err)?;
3291 }
3292 TypeSpec::Scoped(s) if scoped_is_enum(s) => {
3294 let cpp_ty = scoped_to_cpp(s);
3295 writeln!(
3296 out,
3297 "{indent}{setter}(static_cast<{cpp_ty}>(::dds::topic::xcdr2::read_le_origin<int32_t>(__buf, __pos, __len, {origin}, __max_align)));"
3298 )
3299 .map_err(fmt_err)?;
3300 }
3301 TypeSpec::Scoped(sc) if scoped_struct(sc).is_some() => {
3306 let Some((def, ext)) = scoped_struct(sc) else {
3307 return Ok(());
3308 };
3309 let cpp_ty = scoped_to_cpp(sc);
3310 let id = next_nest_id();
3311 let var = format!("__ns{id}");
3312 let inner = format!("{indent} ");
3313 writeln!(out, "{indent}{{").map_err(fmt_err)?;
3314 writeln!(out, "{inner}{cpp_ty} {var}{{}};").map_err(fmt_err)?;
3315 match ext {
3316 Extensibility::Final => {
3317 for sm in &def.members {
3318 let sm_name = &sm.declarators[0].name().text;
3319 emit_value_read(
3320 out,
3321 &sm.type_spec,
3322 &format!("{var}.{sm_name}"),
3323 origin,
3324 &inner,
3325 false,
3326 )?;
3327 }
3328 }
3329 Extensibility::Appendable | Extensibility::Mutable => {
3330 writeln!(
3331 out,
3332 "{inner}::dds::topic::xcdr2::skip_pad_from_origin(__pos, {origin}, 4);"
3333 )
3334 .map_err(fmt_err)?;
3335 writeln!(out, "{inner}const size_t __nss{id} = __pos;").map_err(fmt_err)?;
3336 writeln!(out, "{inner}size_t __npk{id} = __pos;").map_err(fmt_err)?;
3337 writeln!(
3338 out,
3339 "{inner}const uint32_t __nl{id} = ::dds::topic::xcdr2::dheader_read(__buf, __npk{id}, __len);"
3340 )
3341 .map_err(fmt_err)?;
3342 writeln!(
3343 out,
3344 "{inner}{var} = ::dds::topic::topic_type_support<{cpp_ty}>::decode(__buf + __nss{id}, 4u + __nl{id}, __repr);"
3345 )
3346 .map_err(fmt_err)?;
3347 writeln!(out, "{inner}__pos = __nss{id} + 4u + __nl{id};").map_err(fmt_err)?;
3348 }
3349 }
3350 writeln!(out, "{inner}{setter}({var});").map_err(fmt_err)?;
3351 writeln!(out, "{indent}}}").map_err(fmt_err)?;
3352 }
3353 _ => {}
3354 }
3355 Ok(())
3356}
3357
3358fn emit_mutable_member_decode_case(out: &mut String, m: &Member) -> Result<(), CppGenError> {
3359 if !member_codegen_supported(m) {
3360 return Ok(());
3361 }
3362 let id_override = find_uint_annotation(&m.annotations, "id");
3363 let is_optional = has_optional_annotation(&m.annotations);
3364 let _ = is_optional; for decl in &m.declarators {
3366 let name = &decl.name().text;
3367 if !matches!(decl, Declarator::Simple(_)) {
3368 continue;
3369 }
3370 if !typespec_supported(&m.type_spec) {
3371 continue;
3372 }
3373 let id_expr = match id_override {
3374 Some(id) => id.to_string(),
3375 None => format!("0x{:x}u", auto_id_for(name)),
3376 };
3377 writeln!(out, " case {id_expr}: {{").map_err(fmt_err)?;
3378 match &m.type_spec {
3379 TypeSpec::Primitive(PrimitiveType::Boolean) => {
3380 writeln!(out, " uint8_t __b = ::dds::topic::xcdr2::read_u8(__buf, __pos, __len);").map_err(fmt_err)?;
3381 if has_optional_annotation(&m.annotations) {
3382 writeln!(
3383 out,
3384 " __v.{name}(static_cast<bool>(__b));"
3385 )
3386 .map_err(fmt_err)?;
3387 } else {
3388 writeln!(
3389 out,
3390 " __v.{name}(static_cast<bool>(__b));"
3391 )
3392 .map_err(fmt_err)?;
3393 }
3394 }
3395 TypeSpec::Primitive(PrimitiveType::Octet) => {
3396 writeln!(out, " __v.{name}(::dds::topic::xcdr2::read_u8(__buf, __pos, __len));").map_err(fmt_err)?;
3397 }
3398 TypeSpec::Primitive(p) => {
3399 let cpp_ty = primitive_to_cpp(*p);
3400 writeln!(out, " __v.{name}(::dds::topic::xcdr2::read_le_raw<{cpp_ty}>(__buf, __pos, __len));").map_err(fmt_err)?;
3401 }
3402 TypeSpec::String(s) if !s.wide => {
3403 writeln!(out, " auto __n = ::dds::topic::xcdr2::emheader_nextint_read(__buf, __pos, __len);").map_err(fmt_err)?;
3404 writeln!(out, " (void)__n;").map_err(fmt_err)?;
3405 writeln!(out, " auto __body_origin = __pos;")
3406 .map_err(fmt_err)?;
3407 writeln!(out, " __v.{name}(::dds::topic::xcdr2::read_string_origin(__buf, __pos, __len, __body_origin, __max_align));").map_err(fmt_err)?;
3408 }
3409 TypeSpec::String(s) if s.wide => {
3410 writeln!(out, " auto __n = ::dds::topic::xcdr2::emheader_nextint_read(__buf, __pos, __len);").map_err(fmt_err)?;
3411 writeln!(out, " (void)__n;").map_err(fmt_err)?;
3412 writeln!(out, " auto __body_origin = __pos;")
3413 .map_err(fmt_err)?;
3414 writeln!(out, " __v.{name}(::dds::topic::xcdr2::read_wstring_origin(__buf, __pos, __len, __body_origin, __max_align));").map_err(fmt_err)?;
3415 }
3416 TypeSpec::Sequence(seq) => {
3417 writeln!(out, " auto __n = ::dds::topic::xcdr2::emheader_nextint_read(__buf, __pos, __len);").map_err(fmt_err)?;
3418 writeln!(out, " (void)__n;").map_err(fmt_err)?;
3419 writeln!(out, " auto __body_origin = __pos;")
3420 .map_err(fmt_err)?;
3421 if !matches!(&*seq.elem, TypeSpec::Primitive(_)) {
3424 writeln!(out, " {{ const auto __seq_dh = ::dds::topic::xcdr2::dheader_read(__buf, __pos, __len); (void)__seq_dh; }}").map_err(fmt_err)?;
3425 }
3426 writeln!(out, " auto __cnt = ::dds::topic::xcdr2::read_le_origin<uint32_t>(__buf, __pos, __len, __body_origin, __max_align);").map_err(fmt_err)?;
3427 if matches!(&*seq.elem, TypeSpec::Primitive(PrimitiveType::Octet)) {
3428 writeln!(
3430 out,
3431 " ::dds::topic::xcdr2::check_avail(__pos, __cnt, __len);"
3432 )
3433 .map_err(fmt_err)?;
3434 writeln!(out, " std::vector<uint8_t> __seq(__buf + __pos, __buf + __pos + __cnt);").map_err(fmt_err)?;
3435 writeln!(out, " __pos += __cnt;").map_err(fmt_err)?;
3436 writeln!(out, " __v.{name}(std::move(__seq));")
3437 .map_err(fmt_err)?;
3438 } else {
3439 let elem_cpp_ty: String = match &*seq.elem {
3440 TypeSpec::Primitive(PrimitiveType::Boolean) => "bool".to_string(),
3441 TypeSpec::Primitive(p) => primitive_to_cpp(*p).to_string(),
3442 TypeSpec::String(s) if !s.wide => "std::string".to_string(),
3443 TypeSpec::String(_) => "std::wstring".to_string(),
3444 TypeSpec::Scoped(s) if scoped_is_enum(s) => scoped_to_cpp(s),
3445 TypeSpec::Scoped(s) if scoped_struct(s).is_some() => scoped_to_cpp(s),
3446 TypeSpec::Sequence(_) | TypeSpec::Map(_) => typespec_to_cpp(&seq.elem)?,
3447 _ => "uint8_t".to_string(),
3448 };
3449 writeln!(out, " std::vector<{elem_cpp_ty}> __seq;")
3450 .map_err(fmt_err)?;
3451 writeln!(out, " __seq.reserve(__cnt);").map_err(fmt_err)?;
3452 writeln!(
3453 out,
3454 " for (uint32_t __i = 0; __i < __cnt; ++__i) {{"
3455 )
3456 .map_err(fmt_err)?;
3457 match &*seq.elem {
3458 TypeSpec::Primitive(PrimitiveType::Boolean) => {
3459 writeln!(out, " __seq.push_back(::dds::topic::xcdr2::read_bool(__buf, __pos, __len));").map_err(fmt_err)?;
3460 }
3461 TypeSpec::Primitive(p) => {
3462 let cpp_ty = primitive_to_cpp(*p);
3463 writeln!(out, " __seq.push_back(::dds::topic::xcdr2::read_le_origin<{cpp_ty}>(__buf, __pos, __len, __body_origin, __max_align));").map_err(fmt_err)?;
3464 }
3465 TypeSpec::String(s) if !s.wide => {
3466 writeln!(out, " __seq.push_back(::dds::topic::xcdr2::read_string_origin(__buf, __pos, __len, __body_origin, __max_align));").map_err(fmt_err)?;
3467 }
3468 TypeSpec::String(_) => {
3469 writeln!(out, " __seq.push_back(::dds::topic::xcdr2::read_wstring_origin(__buf, __pos, __len, __body_origin, __max_align));").map_err(fmt_err)?;
3470 }
3471 TypeSpec::Scoped(s) if scoped_is_enum(s) => {
3472 let cpp_ty = scoped_to_cpp(s);
3473 writeln!(out, " __seq.push_back(static_cast<{cpp_ty}>(::dds::topic::xcdr2::read_le_origin<int32_t>(__buf, __pos, __len, __body_origin, __max_align)));").map_err(fmt_err)?;
3474 }
3475 TypeSpec::Scoped(sc) if scoped_final_struct(sc).is_some() => {
3476 if let Some(def) = scoped_final_struct(sc) {
3477 let cpp_ty = scoped_to_cpp(sc);
3478 let var = format!("__se{}", next_nest_id());
3479 writeln!(out, " {cpp_ty} {var}{{}};")
3480 .map_err(fmt_err)?;
3481 for sm in &def.members {
3482 let sm_name = &sm.declarators[0].name().text;
3483 emit_value_read(
3484 out,
3485 &sm.type_spec,
3486 &format!("{var}.{sm_name}"),
3487 "__body_origin",
3488 " ",
3489 false,
3490 )?;
3491 }
3492 writeln!(out, " __seq.push_back({var});")
3493 .map_err(fmt_err)?;
3494 }
3495 }
3496 TypeSpec::Scoped(sc) if scoped_struct(sc).is_some() => {
3500 let cpp_ty = scoped_to_cpp(sc);
3501 let id = next_nest_id();
3502 let var = format!("__se{id}");
3503 writeln!(out, " ::dds::topic::xcdr2::skip_pad_from_origin(__pos, __body_origin, 4);").map_err(fmt_err)?;
3504 writeln!(
3505 out,
3506 " const size_t __nss{id} = __pos;"
3507 )
3508 .map_err(fmt_err)?;
3509 writeln!(out, " size_t __npk{id} = __pos;")
3510 .map_err(fmt_err)?;
3511 writeln!(out, " const uint32_t __nl{id} = ::dds::topic::xcdr2::dheader_read(__buf, __npk{id}, __len);").map_err(fmt_err)?;
3512 writeln!(out, " {cpp_ty} {var} = ::dds::topic::topic_type_support<{cpp_ty}>::decode(__buf + __nss{id}, 4u + __nl{id}, __repr);").map_err(fmt_err)?;
3513 writeln!(
3514 out,
3515 " __pos = __nss{id} + 4u + __nl{id};"
3516 )
3517 .map_err(fmt_err)?;
3518 writeln!(
3519 out,
3520 " __seq.push_back(std::move({var}));"
3521 )
3522 .map_err(fmt_err)?;
3523 }
3524 TypeSpec::Sequence(_) | TypeSpec::Map(_) => {
3526 let inner_ty = typespec_to_cpp(&seq.elem)?;
3527 let var = format!("__se{}", next_nest_id());
3528 writeln!(out, " {inner_ty} {var}{{}};")
3529 .map_err(fmt_err)?;
3530 emit_value_read(
3531 out,
3532 &seq.elem,
3533 &format!("{var} ="),
3534 "__body_origin",
3535 " ",
3536 false,
3537 )?;
3538 writeln!(
3539 out,
3540 " __seq.push_back(std::move({var}));"
3541 )
3542 .map_err(fmt_err)?;
3543 }
3544 _ => {}
3545 }
3546 writeln!(out, " }}").map_err(fmt_err)?;
3547 writeln!(out, " __v.{name}(std::move(__seq));")
3548 .map_err(fmt_err)?;
3549 }
3550 }
3551 TypeSpec::Scoped(s) if scoped_is_enum(s) => {
3553 let cpp_ty = scoped_to_cpp(s);
3554 writeln!(out, " __v.{name}(static_cast<{cpp_ty}>(::dds::topic::xcdr2::read_le_raw<int32_t>(__buf, __pos, __len)));").map_err(fmt_err)?;
3555 }
3556 TypeSpec::Scoped(sc) if scoped_struct(sc).is_some() => {
3560 if let Some((def, ext)) = scoped_struct(sc) {
3561 let cpp_ty = scoped_to_cpp(sc);
3562 let id = next_nest_id();
3563 let var = format!("__ns{id}");
3564 writeln!(out, " auto __n = ::dds::topic::xcdr2::emheader_nextint_read(__buf, __pos, __len); (void)__n;").map_err(fmt_err)?;
3565 writeln!(
3566 out,
3567 " auto __body_origin = __pos; (void)__body_origin;"
3568 )
3569 .map_err(fmt_err)?;
3570 writeln!(out, " {cpp_ty} {var}{{}};").map_err(fmt_err)?;
3571 match ext {
3572 Extensibility::Final => {
3573 for sm in &def.members {
3574 let sm_name = &sm.declarators[0].name().text;
3575 emit_value_read(
3576 out,
3577 &sm.type_spec,
3578 &format!("{var}.{sm_name}"),
3579 "__body_origin",
3580 " ",
3581 false,
3582 )?;
3583 }
3584 }
3585 Extensibility::Appendable | Extensibility::Mutable => {
3586 writeln!(out, " const size_t __nss{id} = __pos;")
3587 .map_err(fmt_err)?;
3588 writeln!(out, " size_t __npk{id} = __pos;")
3589 .map_err(fmt_err)?;
3590 writeln!(out, " const uint32_t __nl{id} = ::dds::topic::xcdr2::dheader_read(__buf, __npk{id}, __len);").map_err(fmt_err)?;
3591 writeln!(out, " {var} = ::dds::topic::topic_type_support<{cpp_ty}>::decode(__buf + __nss{id}, 4u + __nl{id}, __repr);").map_err(fmt_err)?;
3592 writeln!(
3593 out,
3594 " __pos = __nss{id} + 4u + __nl{id};"
3595 )
3596 .map_err(fmt_err)?;
3597 }
3598 }
3599 writeln!(out, " __v.{name}({var});").map_err(fmt_err)?;
3600 }
3601 }
3602 TypeSpec::Map(m) => {
3605 let k_ty = typespec_to_cpp(&m.key)?;
3606 let v_ty = typespec_to_cpp(&m.value)?;
3607 let id = next_nest_id();
3608 let mapv = format!("__map{id}");
3609 let kv = format!("__mk{id}");
3610 let vv = format!("__mv{id}");
3611 writeln!(out, " auto __mn = ::dds::topic::xcdr2::emheader_nextint_read(__buf, __pos, __len); (void)__mn;").map_err(fmt_err)?;
3612 writeln!(out, " auto __body_origin = __pos;")
3613 .map_err(fmt_err)?;
3614 writeln!(out, " {{ const auto __map_dh = ::dds::topic::xcdr2::dheader_read(__buf, __pos, __len); (void)__map_dh; }}").map_err(fmt_err)?;
3616 writeln!(out, " auto __mcnt = ::dds::topic::xcdr2::read_le_origin<uint32_t>(__buf, __pos, __len, __body_origin, __max_align);").map_err(fmt_err)?;
3617 writeln!(out, " std::map<{k_ty}, {v_ty}> {mapv};")
3618 .map_err(fmt_err)?;
3619 writeln!(
3620 out,
3621 " for (uint32_t __i = 0; __i < __mcnt; ++__i) {{"
3622 )
3623 .map_err(fmt_err)?;
3624 writeln!(out, " {k_ty} {kv}{{}};").map_err(fmt_err)?;
3625 writeln!(out, " {v_ty} {vv}{{}};").map_err(fmt_err)?;
3626 emit_value_read(
3627 out,
3628 &m.key,
3629 &format!("{kv} ="),
3630 "__body_origin",
3631 " ",
3632 false,
3633 )?;
3634 emit_value_read(
3635 out,
3636 &m.value,
3637 &format!("{vv} ="),
3638 "__body_origin",
3639 " ",
3640 false,
3641 )?;
3642 writeln!(
3643 out,
3644 " {mapv}.emplace(std::move({kv}), std::move({vv}));"
3645 )
3646 .map_err(fmt_err)?;
3647 writeln!(out, " }}").map_err(fmt_err)?;
3648 writeln!(out, " __v.{name}(std::move({mapv}));")
3649 .map_err(fmt_err)?;
3650 }
3651 _ => {}
3652 }
3653 writeln!(out, " break;").map_err(fmt_err)?;
3654 writeln!(out, " }}").map_err(fmt_err)?;
3655 }
3656 Ok(())
3657}
3658
3659fn emit_key_hash_fn(
3660 out: &mut String,
3661 cpp_fqn: &str,
3662 s: &StructDef,
3663 is_keyed: bool,
3664) -> Result<(), CppGenError> {
3665 writeln!(
3666 out,
3667 " static std::array<uint8_t, 16> key_hash(const {cpp_fqn}& __v) {{"
3668 )
3669 .map_err(fmt_err)?;
3670 writeln!(out, " (void)__v;").map_err(fmt_err)?;
3671 if !is_keyed {
3672 writeln!(
3673 out,
3674 " return std::array<uint8_t, 16>{{{{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}}};"
3675 )
3676 .map_err(fmt_err)?;
3677 writeln!(out, " }}").map_err(fmt_err)?;
3678 return Ok(());
3679 }
3680 writeln!(out, " std::vector<uint8_t> __out;").map_err(fmt_err)?;
3681 writeln!(out, " const size_t __origin = 0;").map_err(fmt_err)?;
3682 writeln!(out, " (void)__origin;").map_err(fmt_err)?;
3683 for m in &s.members {
3684 if !has_key_annotation(&m.annotations) {
3685 continue;
3686 }
3687 emit_plain_member_encode(out, m, "be", "__origin")?;
3688 }
3689 writeln!(out, " std::array<uint8_t, 16> __h{{}};").map_err(fmt_err)?;
3691 writeln!(out, " if (__out.size() <= 16) {{").map_err(fmt_err)?;
3692 writeln!(
3693 out,
3694 " std::memcpy(__h.data(), __out.data(), __out.size());"
3695 )
3696 .map_err(fmt_err)?;
3697 writeln!(out, " return __h;").map_err(fmt_err)?;
3698 writeln!(out, " }}").map_err(fmt_err)?;
3699 writeln!(out, " return ::dds::topic::xcdr2_md5::md5(__out);").map_err(fmt_err)?;
3700 writeln!(out, " }}").map_err(fmt_err)?;
3701 Ok(())
3702}
3703
3704fn fmt_err(_: core::fmt::Error) -> CppGenError {
3709 CppGenError::Internal("string formatting failed".into())
3710}
3711
3712#[allow(dead_code)]
3713fn _ensure_used() {
3714 let _ = is_reserved("int");
3716}