1use std::collections::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
47pub(crate) fn emit_header(
49 spec: &Specification,
50 opts: &CppGenOptions,
51) -> Result<String, CppGenError> {
52 detect_inheritance_cycles(spec)?;
54
55 let mut includes = Includes::default();
57 includes.add("<cstdint>"); collect_includes(spec, &mut includes);
59
60 let mut out = String::new();
62 write_header_preamble(&mut out, opts, &includes)?;
63
64 let mut ctx = EmitCtx::new(opts);
66 let outer_prefix: Option<&str> = opts.namespace_prefix.as_deref().filter(|p| !p.is_empty());
67 if let Some(prefix) = outer_prefix {
68 ctx.open_namespace(&mut out, prefix)?;
69 }
70
71 for d in &spec.definitions {
75 if let Some(anns) = top_level_annotations(d) {
76 emit_verbatim_at(&mut out, "", anns, PlacementKind::BeginFile)?;
77 }
78 }
79
80 let mut probe_structs: Vec<(String, &StructDef)> = Vec::new();
88 collect_topic_structs(&spec.definitions, "", &mut probe_structs);
89 if !probe_structs.is_empty() {
90 writeln!(&mut out, "#include \"dds/topic/TopicTraits.hpp\"").map_err(fmt_err)?;
96 writeln!(&mut out, "#include \"dds/topic/xcdr2.hpp\"").map_err(fmt_err)?;
97 writeln!(&mut out, "#include \"dds/topic/xcdr2_md5.hpp\"").map_err(fmt_err)?;
98 writeln!(&mut out).map_err(fmt_err)?;
99 }
100
101 for d in &spec.definitions {
103 emit_definition(&mut out, &mut ctx, d)?;
104 }
105
106 for d in &spec.definitions {
109 if let Some(anns) = top_level_annotations(d) {
110 emit_verbatim_at(&mut out, "", anns, PlacementKind::EndFile)?;
111 }
112 }
113
114 if let Some(prefix) = outer_prefix {
116 ctx.close_namespace(&mut out, prefix)?;
117 }
118
119 if !probe_structs.is_empty() {
123 emit_topic_type_support_specs(&mut out, opts, &probe_structs)?;
124 }
125
126 Ok(out)
127}
128
129fn top_level_annotations(d: &Definition) -> Option<&[Annotation]> {
132 match d {
133 Definition::Module(m) => Some(&m.annotations),
134 Definition::Type(TypeDecl::Constr(c)) => match c {
135 ConstrTypeDecl::Struct(StructDcl::Def(s)) => Some(&s.annotations),
136 ConstrTypeDecl::Union(UnionDcl::Def(u)) => Some(&u.annotations),
137 ConstrTypeDecl::Enum(e) => Some(&e.annotations),
138 _ => None,
139 },
140 Definition::Type(TypeDecl::Typedef(t)) => Some(&t.annotations),
141 Definition::Const(c) => Some(&c.annotations),
142 Definition::Except(e) => Some(&e.annotations),
143 _ => None,
144 }
145}
146
147fn write_header_preamble(
149 out: &mut String,
150 opts: &CppGenOptions,
151 includes: &Includes,
152) -> Result<(), CppGenError> {
153 writeln!(out, "// Generated by zerodds idl-cpp. Do not edit.").map_err(fmt_err)?;
154 writeln!(out, "#pragma once").map_err(fmt_err)?;
155 if let Some(guard) = opts.include_guard_prefix.as_deref() {
156 if !guard.is_empty() {
157 writeln!(out, "// guard-prefix: {guard}").map_err(fmt_err)?;
160 }
161 }
162 writeln!(out).map_err(fmt_err)?;
163 for h in &includes.headers {
164 writeln!(out, "#include {h}").map_err(fmt_err)?;
165 }
166 writeln!(out).map_err(fmt_err)?;
167 Ok(())
168}
169
170fn collect_includes(spec: &Specification, inc: &mut Includes) {
172 for d in &spec.definitions {
173 collect_in_def(d, inc);
174 }
175}
176
177fn collect_in_def(d: &Definition, inc: &mut Includes) {
179 match d {
180 Definition::Module(m) => {
181 for sub in &m.definitions {
182 collect_in_def(sub, inc);
183 }
184 }
185 Definition::Type(td) => collect_in_typedecl(td, inc),
186 Definition::Const(_) => {}
187 Definition::Except(e) => {
188 inc.add("<exception>");
189 for m in &e.members {
190 collect_in_typespec(&m.type_spec, inc);
191 for decl in &m.declarators {
192 if matches!(decl, Declarator::Array(_)) {
193 inc.add("<array>");
194 }
195 }
196 }
197 }
198 Definition::Interface(_)
199 | Definition::ValueBox(_)
200 | Definition::ValueForward(_)
201 | Definition::ValueDef(_)
202 | Definition::TypeId(_)
203 | Definition::TypePrefix(_)
204 | Definition::Import(_)
205 | Definition::Component(_)
206 | Definition::Home(_)
207 | Definition::Event(_)
208 | Definition::Porttype(_)
209 | Definition::Connector(_)
210 | Definition::TemplateModule(_)
211 | Definition::TemplateModuleInst(_)
212 | Definition::Annotation(_)
213 | Definition::VendorExtension(_) => {
214 }
217 }
218}
219
220fn collect_in_typedecl(td: &TypeDecl, inc: &mut Includes) {
221 match td {
222 TypeDecl::Constr(c) => match c {
223 ConstrTypeDecl::Struct(StructDcl::Def(s)) => {
224 for m in &s.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 if has_optional_annotation(&m.annotations) {
232 inc.add("<optional>");
233 }
234 if has_shared_annotation(&m.annotations) {
235 inc.add("<memory>");
236 }
237 }
238 }
239 ConstrTypeDecl::Struct(StructDcl::Forward(_)) => {}
240 ConstrTypeDecl::Union(UnionDcl::Def(u)) => {
241 inc.add("<variant>");
242 for c in &u.cases {
243 collect_in_typespec(&c.element.type_spec, inc);
244 if matches!(c.element.declarator, Declarator::Array(_)) {
245 inc.add("<array>");
246 }
247 }
248 }
249 ConstrTypeDecl::Union(UnionDcl::Forward(_)) => {}
250 ConstrTypeDecl::Enum(_) | ConstrTypeDecl::Bitset(_) | ConstrTypeDecl::Bitmask(_) => {}
251 },
252 TypeDecl::Typedef(t) => {
253 collect_in_typespec(&t.type_spec, inc);
254 for decl in &t.declarators {
255 if matches!(decl, Declarator::Array(_)) {
256 inc.add("<array>");
257 }
258 }
259 }
260 }
261}
262
263fn collect_in_typespec(ts: &TypeSpec, inc: &mut Includes) {
265 match ts {
266 TypeSpec::Primitive(_) => {}
267 TypeSpec::Scoped(_) => {}
268 TypeSpec::Sequence(s) => {
269 inc.add("<vector>");
270 collect_in_typespec(&s.elem, inc);
271 }
272 TypeSpec::String(_) => {
273 inc.add("<string>");
275 }
276 TypeSpec::Map(m) => {
277 inc.add("<map>");
278 collect_in_typespec(&m.key, inc);
279 collect_in_typespec(&m.value, inc);
280 }
281 TypeSpec::Fixed(_) => {
282 inc.add("<cstdint>");
286 }
287 TypeSpec::Any => {
288 inc.add("<cstdint>");
290 }
291 }
292}
293
294struct EmitCtx<'o> {
296 opts: &'o CppGenOptions,
297 indent_level: usize,
298}
299
300impl<'o> EmitCtx<'o> {
301 fn new(opts: &'o CppGenOptions) -> Self {
302 Self {
303 opts,
304 indent_level: 0,
305 }
306 }
307
308 fn indent(&self) -> String {
309 " ".repeat(self.indent_level * self.opts.indent_width)
310 }
311
312 fn open_namespace(&mut self, out: &mut String, name: &str) -> Result<(), CppGenError> {
313 writeln!(out, "{}namespace {name} {{", self.indent()).map_err(fmt_err)?;
314 self.indent_level += 1;
315 Ok(())
316 }
317
318 fn close_namespace(&mut self, out: &mut String, name: &str) -> Result<(), CppGenError> {
319 self.indent_level = self.indent_level.saturating_sub(1);
320 writeln!(out, "{}}} // namespace {name}", self.indent()).map_err(fmt_err)?;
321 Ok(())
322 }
323}
324
325fn emit_definition(
327 out: &mut String,
328 ctx: &mut EmitCtx<'_>,
329 def: &Definition,
330) -> Result<(), CppGenError> {
331 match def {
332 Definition::Module(m) => emit_module(out, ctx, m),
333 Definition::Type(td) => emit_type_decl(out, ctx, td),
334 Definition::Const(c) => emit_const_decl(out, ctx, c),
335 Definition::Except(e) => emit_exception(out, ctx, e),
336 Definition::Interface(InterfaceDcl::Def(iface)) => {
337 emit_interface_stub(out, ctx, iface)
342 }
343 Definition::Interface(InterfaceDcl::Forward(f)) => {
344 check_identifier(&f.name.text)?;
345 writeln!(out, "{}class {};", ctx.indent(), f.name.text).map_err(fmt_err)?;
346 Ok(())
347 }
348 Definition::ValueDef(v) => emit_value_type(out, ctx, v),
349 Definition::ValueBox(_) | Definition::ValueForward(_) => {
350 Ok(())
354 }
355 Definition::TypeId(_)
356 | Definition::TypePrefix(_)
357 | Definition::Import(_)
358 | Definition::Component(_)
359 | Definition::Home(_)
360 | Definition::Event(_)
361 | Definition::Porttype(_)
362 | Definition::Connector(_)
363 | Definition::TemplateModule(_)
364 | Definition::TemplateModuleInst(_) => Err(CppGenError::UnsupportedConstruct {
365 construct: "corba/ccm/template construct".into(),
366 context: None,
367 }),
368 Definition::Annotation(_) => {
369 Ok(())
373 }
374 Definition::VendorExtension(v) => Err(CppGenError::UnsupportedConstruct {
375 construct: format!("vendor-extension:{}", v.production_name),
376 context: None,
377 }),
378 }
379}
380
381fn emit_module(out: &mut String, ctx: &mut EmitCtx<'_>, m: &ModuleDef) -> Result<(), CppGenError> {
383 check_identifier(&m.name.text)?;
384 ctx.open_namespace(out, &m.name.text)?;
385 for d in &m.definitions {
386 emit_definition(out, ctx, d)?;
387 }
388 ctx.close_namespace(out, &m.name.text)?;
389 Ok(())
390}
391
392fn emit_type_decl(
393 out: &mut String,
394 ctx: &mut EmitCtx<'_>,
395 td: &TypeDecl,
396) -> Result<(), CppGenError> {
397 match td {
398 TypeDecl::Constr(c) => match c {
399 ConstrTypeDecl::Struct(StructDcl::Def(s)) => emit_struct(out, ctx, s),
400 ConstrTypeDecl::Struct(StructDcl::Forward(f)) => {
401 check_identifier(&f.name.text)?;
402 writeln!(out, "{}class {};", ctx.indent(), f.name.text).map_err(fmt_err)?;
403 Ok(())
404 }
405 ConstrTypeDecl::Union(UnionDcl::Def(u)) => emit_union(out, ctx, u),
406 ConstrTypeDecl::Union(UnionDcl::Forward(f)) => {
407 check_identifier(&f.name.text)?;
408 writeln!(out, "{}class {};", ctx.indent(), f.name.text).map_err(fmt_err)?;
409 Ok(())
410 }
411 ConstrTypeDecl::Enum(e) => emit_enum(out, ctx, e),
412 ConstrTypeDecl::Bitset(b) => {
413 check_identifier(&b.name.text)?;
414 let ind = ctx.indent();
415 let inner = " ".repeat((ctx.indent_level + 1) * ctx.opts.indent_width);
416 emit_bitset(out, &ind, &inner, b)
417 }
418 ConstrTypeDecl::Bitmask(b) => {
419 check_identifier(&b.name.text)?;
420 let ind = ctx.indent();
421 let inner = " ".repeat((ctx.indent_level + 1) * ctx.opts.indent_width);
422 emit_bitmask(out, &ind, &inner, b)
423 }
424 },
425 TypeDecl::Typedef(t) => emit_typedef(out, ctx, t),
426 }
427}
428
429fn emit_struct(out: &mut String, ctx: &mut EmitCtx<'_>, s: &StructDef) -> Result<(), CppGenError> {
430 check_identifier(&s.name.text)?;
431 let ind = ctx.indent();
432
433 emit_verbatim_at(out, &ind, &s.annotations, PlacementKind::BeforeDeclaration)?;
436
437 if let Some(base) = &s.base {
439 let base_str = scoped_to_cpp(base);
440 writeln!(out, "{ind}class {} : public {} {{", s.name.text, base_str).map_err(fmt_err)?;
441 } else {
442 writeln!(out, "{ind}class {} {{", s.name.text).map_err(fmt_err)?;
443 }
444 writeln!(out, "{ind}public:").map_err(fmt_err)?;
445
446 let inner = " ".repeat((ctx.indent_level + 1) * ctx.opts.indent_width);
447
448 emit_verbatim_at(out, &inner, &s.annotations, PlacementKind::BeginDeclaration)?;
451
452 writeln!(out, "{inner}{}() = default;", s.name.text).map_err(fmt_err)?;
454 writeln!(out, "{inner}~{}() = default;", s.name.text).map_err(fmt_err)?;
455 writeln!(out).map_err(fmt_err)?;
456
457 writeln!(out, "{ind}private:").map_err(fmt_err)?;
459 for m in &s.members {
460 emit_struct_member_field(out, ctx, m)?;
461 }
462 writeln!(out).map_err(fmt_err)?;
463
464 writeln!(out, "{ind}public:").map_err(fmt_err)?;
466 for m in &s.members {
467 emit_struct_member_accessors(out, ctx, m)?;
468 }
469
470 emit_verbatim_at(out, &inner, &s.annotations, PlacementKind::EndDeclaration)?;
473
474 writeln!(out, "{ind}}};").map_err(fmt_err)?;
475
476 emit_verbatim_at(out, &ind, &s.annotations, PlacementKind::AfterDeclaration)?;
479
480 writeln!(out).map_err(fmt_err)?;
481 Ok(())
482}
483
484fn emit_struct_member_field(
485 out: &mut String,
486 ctx: &EmitCtx<'_>,
487 m: &Member,
488) -> Result<(), CppGenError> {
489 let inner = " ".repeat((ctx.indent_level + 1) * ctx.opts.indent_width);
490 let optional = has_optional_annotation(&m.annotations);
491 let shared = has_shared_annotation(&m.annotations);
492 for decl in &m.declarators {
493 let cpp_ty = type_for_declarator(&m.type_spec, decl)?;
494 let name = decl.name();
495 check_identifier(&name.text)?;
496 let key_marker = if has_key_annotation(&m.annotations) {
497 " // @key"
498 } else {
499 ""
500 };
501 let core_ty = if shared {
504 format!("std::shared_ptr<{cpp_ty}>")
505 } else {
506 cpp_ty
507 };
508 if optional {
509 writeln!(
510 out,
511 "{inner}std::optional<{core_ty}> {}_;{key_marker}",
512 name.text
513 )
514 .map_err(fmt_err)?;
515 } else {
516 writeln!(out, "{inner}{core_ty} {}_;{key_marker}", name.text).map_err(fmt_err)?;
517 }
518 }
519 Ok(())
520}
521
522fn emit_struct_member_accessors(
523 out: &mut String,
524 ctx: &EmitCtx<'_>,
525 m: &Member,
526) -> Result<(), CppGenError> {
527 let inner = " ".repeat((ctx.indent_level + 1) * ctx.opts.indent_width);
528 let optional = has_optional_annotation(&m.annotations);
529 let shared = has_shared_annotation(&m.annotations);
530 for decl in &m.declarators {
531 let cpp_ty = type_for_declarator(&m.type_spec, decl)?;
532 let name = &decl.name().text;
533 let core_ty = if shared {
534 format!("std::shared_ptr<{cpp_ty}>")
535 } else {
536 cpp_ty.clone()
537 };
538 let storage_ty = if optional {
539 format!("std::optional<{core_ty}>")
540 } else {
541 core_ty
542 };
543 writeln!(out, "{inner}{storage_ty}& {name}() {{ return {name}_; }}").map_err(fmt_err)?;
544 writeln!(
545 out,
546 "{inner}const {storage_ty}& {name}() const {{ return {name}_; }}"
547 )
548 .map_err(fmt_err)?;
549 writeln!(
550 out,
551 "{inner}void {name}(const {storage_ty}& value) {{ {name}_ = value; }}"
552 )
553 .map_err(fmt_err)?;
554 }
555 Ok(())
556}
557
558fn emit_union(out: &mut String, ctx: &mut EmitCtx<'_>, u: &UnionDef) -> Result<(), CppGenError> {
559 check_identifier(&u.name.text)?;
560 let ind = ctx.indent();
561 emit_verbatim_at(out, &ind, &u.annotations, PlacementKind::BeforeDeclaration)?;
562 writeln!(out, "{ind}class {} {{", u.name.text).map_err(fmt_err)?;
563 writeln!(out, "{ind}public:").map_err(fmt_err)?;
564 let inner = " ".repeat((ctx.indent_level + 1) * ctx.opts.indent_width);
565 emit_verbatim_at(out, &inner, &u.annotations, PlacementKind::BeginDeclaration)?;
566
567 let disc_ty = switch_type_to_cpp(&u.switch_type)?;
568
569 let mut variant_types: Vec<String> = Vec::new();
571 for c in &u.cases {
572 let cpp_ty = type_for_declarator(&c.element.type_spec, &c.element.declarator)?;
573 if !variant_types.iter().any(|t| t == &cpp_ty) {
574 variant_types.push(cpp_ty);
575 }
576 }
577 let variant_str = if variant_types.is_empty() {
578 "std::monostate".to_string()
579 } else {
580 variant_types.join(", ")
581 };
582
583 writeln!(
584 out,
585 "{inner}using value_type = std::variant<{variant_str}>;"
586 )
587 .map_err(fmt_err)?;
588 writeln!(out, "{inner}{}() = default;", u.name.text).map_err(fmt_err)?;
589 writeln!(out, "{inner}~{}() = default;", u.name.text).map_err(fmt_err)?;
590 writeln!(out).map_err(fmt_err)?;
591
592 writeln!(
594 out,
595 "{inner}{disc_ty} _d() const {{ return discriminator_; }}"
596 )
597 .map_err(fmt_err)?;
598 writeln!(out, "{inner}void _d({disc_ty} d) {{ discriminator_ = d; }}").map_err(fmt_err)?;
599 writeln!(out, "{inner}value_type& value() {{ return value_; }}").map_err(fmt_err)?;
600 writeln!(
601 out,
602 "{inner}const value_type& value() const {{ return value_; }}"
603 )
604 .map_err(fmt_err)?;
605 writeln!(out).map_err(fmt_err)?;
606
607 let mut has_default = false;
609 for c in &u.cases {
610 emit_union_case_comment(out, &inner, c, &mut has_default)?;
611 }
612 if !has_default {
613 writeln!(out, "{inner}// no explicit 'default:' branch").map_err(fmt_err)?;
614 }
615
616 writeln!(out).map_err(fmt_err)?;
617 writeln!(out, "{ind}private:").map_err(fmt_err)?;
618 writeln!(out, "{inner}{disc_ty} discriminator_{{}};").map_err(fmt_err)?;
619 writeln!(out, "{inner}value_type value_{{}};").map_err(fmt_err)?;
620 emit_verbatim_at(out, &inner, &u.annotations, PlacementKind::EndDeclaration)?;
621 writeln!(out, "{ind}}};").map_err(fmt_err)?;
622 emit_verbatim_at(out, &ind, &u.annotations, PlacementKind::AfterDeclaration)?;
623 writeln!(out).map_err(fmt_err)?;
624 Ok(())
625}
626
627fn emit_union_case_comment(
628 out: &mut String,
629 inner: &str,
630 c: &Case,
631 has_default: &mut bool,
632) -> Result<(), CppGenError> {
633 for label in &c.labels {
634 match label {
635 CaseLabel::Default => {
636 *has_default = true;
637 writeln!(
638 out,
639 "{inner}// case default -> {}",
640 declarator_name(&c.element.declarator)
641 )
642 .map_err(fmt_err)?;
643 }
644 CaseLabel::Value(expr) => {
645 let val = const_expr_to_cpp(expr);
646 writeln!(
647 out,
648 "{inner}// case {val} -> {}",
649 declarator_name(&c.element.declarator)
650 )
651 .map_err(fmt_err)?;
652 }
653 }
654 }
655 Ok(())
656}
657
658pub(crate) fn declarator_name(d: &Declarator) -> &str {
659 &d.name().text
660}
661
662fn emit_enum(out: &mut String, ctx: &mut EmitCtx<'_>, e: &EnumDef) -> Result<(), CppGenError> {
663 check_identifier(&e.name.text)?;
664 let ind = ctx.indent();
665 emit_verbatim_at(out, &ind, &e.annotations, PlacementKind::BeforeDeclaration)?;
666 writeln!(out, "{ind}enum class {} : int32_t {{", e.name.text).map_err(fmt_err)?;
667 let inner = " ".repeat((ctx.indent_level + 1) * ctx.opts.indent_width);
668 emit_verbatim_at(out, &inner, &e.annotations, PlacementKind::BeginDeclaration)?;
669 for en in &e.enumerators {
670 check_identifier(&en.name.text)?;
671 writeln!(out, "{inner}{},", en.name.text).map_err(fmt_err)?;
672 }
673 emit_verbatim_at(out, &inner, &e.annotations, PlacementKind::EndDeclaration)?;
674 writeln!(out, "{ind}}};").map_err(fmt_err)?;
675 emit_verbatim_at(out, &ind, &e.annotations, PlacementKind::AfterDeclaration)?;
676 writeln!(out).map_err(fmt_err)?;
677 Ok(())
678}
679
680fn emit_interface_stub(
681 out: &mut String,
682 ctx: &mut EmitCtx<'_>,
683 iface: &InterfaceDef,
684) -> Result<(), CppGenError> {
685 let name = &iface.name.text;
686 check_identifier(name)?;
687 let ind = ctx.indent();
688 let inner = " ".repeat((ctx.indent_level + 1) * ctx.opts.indent_width);
689
690 emit_verbatim_at(
691 out,
692 &ind,
693 &iface.annotations,
694 PlacementKind::BeforeDeclaration,
695 )?;
696
697 if iface.bases.is_empty() {
699 writeln!(out, "{ind}class {name} {{").map_err(fmt_err)?;
700 } else {
701 let bases: Vec<String> = iface
702 .bases
703 .iter()
704 .map(|b| format!("public virtual {}", scoped_to_cpp(b)))
705 .collect();
706 writeln!(out, "{ind}class {name} : {} {{", bases.join(", ")).map_err(fmt_err)?;
707 }
708 writeln!(out, "{ind}public:").map_err(fmt_err)?;
709 writeln!(out, "{inner}virtual ~{name}() = default;").map_err(fmt_err)?;
710
711 for export in &iface.exports {
712 match export {
713 Export::Op(op) => emit_interface_op(out, &inner, op)?,
714 Export::Attr(attr) => emit_interface_attr(out, &inner, attr)?,
715 Export::Type(td) => emit_type_decl(out, ctx, td)?,
716 Export::Const(c) => emit_const_decl(out, ctx, c)?,
717 Export::Except(e) => emit_exception(out, ctx, e)?,
718 }
719 }
720
721 writeln!(out, "{ind}}};").map_err(fmt_err)?;
722 emit_verbatim_at(
723 out,
724 &ind,
725 &iface.annotations,
726 PlacementKind::AfterDeclaration,
727 )?;
728 writeln!(out).map_err(fmt_err)?;
729 Ok(())
730}
731
732fn emit_interface_op(out: &mut String, inner: &str, op: &OpDecl) -> Result<(), CppGenError> {
733 check_identifier(&op.name.text)?;
734 let ret = match &op.return_type {
735 None => "void".to_string(),
736 Some(t) => typespec_to_cpp(t)?,
737 };
738 let params: Vec<String> = op
739 .params
740 .iter()
741 .map(|p| -> Result<String, CppGenError> {
742 let ty = typespec_to_cpp(&p.type_spec)?;
743 let qual = match p.attribute {
746 ParamAttribute::In => format!("const {ty}&"),
747 ParamAttribute::Out | ParamAttribute::InOut => format!("{ty}&"),
748 };
749 Ok(format!("{qual} {}", p.name.text))
750 })
751 .collect::<Result<_, _>>()?;
752 let raises_comment = if op.raises.is_empty() {
753 String::new()
754 } else {
755 let raises: Vec<String> = op.raises.iter().map(scoped_to_cpp).collect();
756 format!(" /* throws {} */", raises.join(", "))
757 };
758 writeln!(
759 out,
760 "{inner}virtual {ret} {}({}) = 0;{raises_comment}",
761 op.name.text,
762 params.join(", ")
763 )
764 .map_err(fmt_err)?;
765 Ok(())
766}
767
768fn emit_interface_attr(
769 out: &mut String,
770 inner: &str,
771 attr: &zerodds_idl::ast::AttrDecl,
772) -> Result<(), CppGenError> {
773 check_identifier(&attr.name.text)?;
774 let ty = typespec_to_cpp(&attr.type_spec)?;
775 writeln!(out, "{inner}virtual {ty} {}() const = 0;", attr.name.text).map_err(fmt_err)?;
777 if !attr.readonly {
779 writeln!(
780 out,
781 "{inner}virtual void {}(const {ty}& value) = 0;",
782 attr.name.text
783 )
784 .map_err(fmt_err)?;
785 }
786 Ok(())
787}
788
789fn emit_value_type(
790 out: &mut String,
791 ctx: &mut EmitCtx<'_>,
792 v: &ValueDef,
793) -> Result<(), CppGenError> {
794 let name = &v.name.text;
795 check_identifier(name)?;
796 let ind = ctx.indent();
797 let inner = " ".repeat((ctx.indent_level + 1) * ctx.opts.indent_width);
798
799 emit_verbatim_at(out, &ind, &v.annotations, PlacementKind::BeforeDeclaration)?;
800
801 let mut bases: Vec<String> = Vec::new();
805 if let Some(inh) = &v.inheritance {
806 for b in &inh.bases {
807 bases.push(format!("public virtual {}", scoped_to_cpp(b)));
808 }
809 for s in &inh.supports {
810 bases.push(format!("public virtual {}", scoped_to_cpp(s)));
811 }
812 }
813 if bases.is_empty() {
814 writeln!(out, "{ind}class {name} {{").map_err(fmt_err)?;
815 } else {
816 writeln!(out, "{ind}class {name} : {} {{", bases.join(", ")).map_err(fmt_err)?;
817 }
818 writeln!(out, "{ind}public:").map_err(fmt_err)?;
819 writeln!(out, "{inner}virtual ~{name}() = default;").map_err(fmt_err)?;
820
821 let mut has_protected = false;
823 for el in &v.elements {
824 match el {
825 ValueElement::State(s) if matches!(s.visibility, StateVisibility::Public) => {
826 let ty = typespec_to_cpp(&s.type_spec)?;
827 for d in &s.declarators {
828 let n = &d.name().text;
829 writeln!(out, "{inner}virtual const {ty}& {n}() const = 0;")
830 .map_err(fmt_err)?;
831 writeln!(out, "{inner}virtual void {n}(const {ty}& value) = 0;")
832 .map_err(fmt_err)?;
833 }
834 }
835 ValueElement::State(s) if matches!(s.visibility, StateVisibility::Private) => {
836 has_protected = true;
837 }
838 ValueElement::Export(Export::Op(op)) => emit_interface_op(out, &inner, op)?,
839 ValueElement::Export(Export::Attr(a)) => emit_interface_attr(out, &inner, a)?,
840 _ => {}
841 }
842 }
843
844 if has_protected {
846 writeln!(out, "{ind}protected:").map_err(fmt_err)?;
847 for el in &v.elements {
848 if let ValueElement::State(s) = el {
849 if matches!(s.visibility, StateVisibility::Private) {
850 let ty = typespec_to_cpp(&s.type_spec)?;
851 for d in &s.declarators {
852 let n = &d.name().text;
853 writeln!(out, "{inner}virtual const {ty}& {n}() const = 0;")
854 .map_err(fmt_err)?;
855 writeln!(out, "{inner}virtual void {n}(const {ty}& value) = 0;")
856 .map_err(fmt_err)?;
857 }
858 }
859 }
860 }
861 }
862
863 writeln!(out, "{ind}}};").map_err(fmt_err)?;
864
865 let factories: Vec<&zerodds_idl::ast::InitDcl> = v
867 .elements
868 .iter()
869 .filter_map(|e| {
870 if let ValueElement::Init(i) = e {
871 Some(i)
872 } else {
873 None
874 }
875 })
876 .collect();
877 if !factories.is_empty() {
878 writeln!(out, "{ind}class {name}_factory {{").map_err(fmt_err)?;
879 writeln!(out, "{ind}public:").map_err(fmt_err)?;
880 writeln!(out, "{inner}virtual ~{name}_factory() = default;").map_err(fmt_err)?;
881 for f in &factories {
882 check_identifier(&f.name.text)?;
883 let params: Vec<String> = f
884 .params
885 .iter()
886 .map(|p| -> Result<String, CppGenError> {
887 let ty = typespec_to_cpp(&p.type_spec)?;
888 let qual = match p.attribute {
889 ParamAttribute::In => format!("const {ty}&"),
890 ParamAttribute::Out | ParamAttribute::InOut => format!("{ty}&"),
891 };
892 Ok(format!("{qual} {}", p.name.text))
893 })
894 .collect::<Result<_, _>>()?;
895 writeln!(
896 out,
897 "{inner}virtual std::shared_ptr<{name}> {}({}) = 0;",
898 f.name.text,
899 params.join(", ")
900 )
901 .map_err(fmt_err)?;
902 }
903 writeln!(out, "{ind}}};").map_err(fmt_err)?;
904 }
905
906 emit_verbatim_at(out, &ind, &v.annotations, PlacementKind::AfterDeclaration)?;
907 writeln!(out).map_err(fmt_err)?;
908 Ok(())
909}
910
911fn emit_typedef(
912 out: &mut String,
913 ctx: &mut EmitCtx<'_>,
914 t: &TypedefDecl,
915) -> Result<(), CppGenError> {
916 let ind = ctx.indent();
917 emit_verbatim_at(out, &ind, &t.annotations, PlacementKind::BeforeDeclaration)?;
918 for decl in &t.declarators {
919 let alias = &decl.name().text;
920 check_identifier(alias)?;
921 let target_ty = type_for_declarator(&t.type_spec, decl)?;
922 writeln!(out, "{ind}using {alias} = {target_ty};").map_err(fmt_err)?;
923 }
924 emit_verbatim_at(out, &ind, &t.annotations, PlacementKind::AfterDeclaration)?;
925 writeln!(out).map_err(fmt_err)?;
926 Ok(())
927}
928
929fn emit_const_decl(
930 out: &mut String,
931 ctx: &mut EmitCtx<'_>,
932 c: &zerodds_idl::ast::ConstDecl,
933) -> Result<(), CppGenError> {
934 check_identifier(&c.name.text)?;
935 let ind = ctx.indent();
936 let cpp_ty = match &c.type_ {
937 zerodds_idl::ast::ConstType::Integer(i) => crate::type_map::integer_to_cpp(*i).to_string(),
938 zerodds_idl::ast::ConstType::Floating(f) => {
939 crate::type_map::floating_to_cpp(*f).to_string()
940 }
941 zerodds_idl::ast::ConstType::Boolean => "bool".into(),
942 zerodds_idl::ast::ConstType::Char => "char".into(),
943 zerodds_idl::ast::ConstType::WideChar => "wchar_t".into(),
944 zerodds_idl::ast::ConstType::Octet => "uint8_t".into(),
945 zerodds_idl::ast::ConstType::String { wide: false } => "std::string".into(),
946 zerodds_idl::ast::ConstType::String { wide: true } => "std::wstring".into(),
947 zerodds_idl::ast::ConstType::Scoped(s) => scoped_to_cpp(s),
948 zerodds_idl::ast::ConstType::Fixed => {
949 "::dds::core::Fixed<31, 0>".into()
953 }
954 };
955 let val = const_expr_to_cpp(&c.value);
956 writeln!(out, "{ind}constexpr {cpp_ty} {} = {val};", c.name.text).map_err(fmt_err)?;
957 Ok(())
958}
959
960fn emit_exception(
961 out: &mut String,
962 ctx: &mut EmitCtx<'_>,
963 e: &ExceptDecl,
964) -> Result<(), CppGenError> {
965 check_identifier(&e.name.text)?;
966 let ind = ctx.indent();
967 writeln!(out, "{ind}class {} : public std::exception {{", e.name.text).map_err(fmt_err)?;
968 writeln!(out, "{ind}public:").map_err(fmt_err)?;
969 let inner = " ".repeat((ctx.indent_level + 1) * ctx.opts.indent_width);
970 writeln!(out, "{inner}{}() = default;", e.name.text).map_err(fmt_err)?;
971 writeln!(out, "{inner}~{}() override = default;", e.name.text).map_err(fmt_err)?;
972 writeln!(
973 out,
974 "{inner}const char* what() const noexcept override {{ return \"{}\"; }}",
975 e.name.text
976 )
977 .map_err(fmt_err)?;
978 writeln!(out).map_err(fmt_err)?;
979 writeln!(out, "{ind}private:").map_err(fmt_err)?;
980 for m in &e.members {
981 for decl in &m.declarators {
982 let cpp_ty = type_for_declarator(&m.type_spec, decl)?;
983 let name = &decl.name().text;
984 check_identifier(name)?;
985 writeln!(out, "{inner}{cpp_ty} {name}_;").map_err(fmt_err)?;
986 }
987 }
988 writeln!(out, "{ind}}};").map_err(fmt_err)?;
989 writeln!(out).map_err(fmt_err)?;
990 Ok(())
991}
992
993pub(crate) fn type_for_declarator(ts: &TypeSpec, decl: &Declarator) -> Result<String, CppGenError> {
1000 let base = typespec_to_cpp(ts)?;
1001 match decl {
1002 Declarator::Simple(_) => Ok(base),
1003 Declarator::Array(arr) => {
1004 let mut out = base;
1007 for size in arr.sizes.iter().rev() {
1008 let n = const_expr_to_usize(size).unwrap_or_default();
1009 out = format!("std::array<{out}, {n}>");
1010 }
1011 Ok(out)
1012 }
1013 }
1014}
1015
1016pub(crate) fn typespec_to_cpp(ts: &TypeSpec) -> Result<String, CppGenError> {
1018 match ts {
1019 TypeSpec::Primitive(p) => Ok(primitive_to_cpp(*p).to_string()),
1020 TypeSpec::Scoped(s) => Ok(scoped_to_cpp(s)),
1021 TypeSpec::Sequence(s) => {
1022 let inner = typespec_to_cpp(&s.elem)?;
1023 Ok(format!("std::vector<{inner}>"))
1024 }
1025 TypeSpec::String(s) => {
1026 if s.wide {
1027 Ok("std::wstring".into())
1028 } else {
1029 Ok("std::string".into())
1030 }
1031 }
1032 TypeSpec::Map(m) => {
1033 let k = typespec_to_cpp(&m.key)?;
1034 let v = typespec_to_cpp(&m.value)?;
1035 Ok(format!("std::map<{k}, {v}>"))
1036 }
1037 TypeSpec::Fixed(f) => {
1038 let digits = const_expr_to_u32(&f.digits).unwrap_or(0);
1042 let scale = const_expr_to_u32(&f.scale).unwrap_or(0);
1043 Ok(format!("::dds::core::Fixed<{digits}, {scale}>"))
1044 }
1045 TypeSpec::Any => {
1046 Ok("::dds::core::Any".into())
1051 }
1052 }
1053}
1054
1055pub(crate) fn switch_type_to_cpp(s: &SwitchTypeSpec) -> Result<String, CppGenError> {
1056 Ok(match s {
1057 SwitchTypeSpec::Integer(i) => crate::type_map::integer_to_cpp(*i).to_string(),
1058 SwitchTypeSpec::Char => "char".into(),
1059 SwitchTypeSpec::Boolean => "bool".into(),
1060 SwitchTypeSpec::Octet => "uint8_t".into(),
1061 SwitchTypeSpec::Scoped(s) => scoped_to_cpp(s),
1062 })
1063}
1064
1065pub(crate) fn scoped_to_cpp(s: &ScopedName) -> String {
1066 if s.parts.len() == 1 {
1068 if let Some(mapped) = TIME_DURATION_TYPES
1069 .iter()
1070 .find(|(idl, _)| *idl == s.parts[0].text)
1071 .map(|(_, cpp)| *cpp)
1072 {
1073 return mapped.to_string();
1074 }
1075 }
1076 let parts: Vec<String> = s.parts.iter().map(|p| p.text.clone()).collect();
1077 let joined = parts.join("::");
1078 if s.absolute {
1079 format!("::{joined}")
1080 } else {
1081 joined
1082 }
1083}
1084
1085fn const_expr_to_u32(e: &ConstExpr) -> Option<u32> {
1090 if let ConstExpr::Literal(l) = e {
1091 if matches!(l.kind, LiteralKind::Integer) {
1092 return l.raw.parse::<u32>().ok();
1093 }
1094 }
1095 None
1096}
1097
1098pub(crate) fn const_expr_to_cpp(e: &ConstExpr) -> String {
1099 match e {
1100 ConstExpr::Literal(l) => literal_to_cpp(l),
1101 ConstExpr::Scoped(s) => scoped_to_cpp(s),
1102 ConstExpr::Unary { op, operand, .. } => {
1103 let prefix = match op {
1104 zerodds_idl::ast::UnaryOp::Plus => "+",
1105 zerodds_idl::ast::UnaryOp::Minus => "-",
1106 zerodds_idl::ast::UnaryOp::BitNot => "~",
1107 };
1108 format!("{prefix}{}", const_expr_to_cpp(operand))
1109 }
1110 ConstExpr::Binary { op, lhs, rhs, .. } => {
1111 let opstr = match op {
1112 zerodds_idl::ast::BinaryOp::Or => "|",
1113 zerodds_idl::ast::BinaryOp::Xor => "^",
1114 zerodds_idl::ast::BinaryOp::And => "&",
1115 zerodds_idl::ast::BinaryOp::Shl => "<<",
1116 zerodds_idl::ast::BinaryOp::Shr => ">>",
1117 zerodds_idl::ast::BinaryOp::Add => "+",
1118 zerodds_idl::ast::BinaryOp::Sub => "-",
1119 zerodds_idl::ast::BinaryOp::Mul => "*",
1120 zerodds_idl::ast::BinaryOp::Div => "/",
1121 zerodds_idl::ast::BinaryOp::Mod => "%",
1122 };
1123 format!(
1124 "({} {opstr} {})",
1125 const_expr_to_cpp(lhs),
1126 const_expr_to_cpp(rhs)
1127 )
1128 }
1129 }
1130}
1131
1132fn literal_to_cpp(l: &Literal) -> String {
1133 match l.kind {
1134 LiteralKind::Boolean => l.raw.clone(),
1135 LiteralKind::Integer | LiteralKind::Floating => l.raw.clone(),
1136 LiteralKind::Char => l.raw.clone(),
1137 LiteralKind::WideChar => l.raw.clone(),
1138 LiteralKind::String => l.raw.clone(),
1139 LiteralKind::WideString => l.raw.clone(),
1140 LiteralKind::Fixed => l.raw.clone(),
1141 }
1142}
1143
1144fn const_expr_to_usize(e: &ConstExpr) -> Option<usize> {
1145 match e {
1146 ConstExpr::Literal(l) if l.kind == LiteralKind::Integer => l.raw.parse::<usize>().ok(),
1147 _ => None,
1148 }
1149}
1150
1151fn has_key_annotation(anns: &[Annotation]) -> bool {
1156 has_named_annotation(anns, "key")
1157}
1158
1159fn has_optional_annotation(anns: &[Annotation]) -> bool {
1160 has_named_annotation(anns, "optional")
1161}
1162
1163fn has_shared_annotation(anns: &[Annotation]) -> bool {
1164 has_named_annotation(anns, "shared")
1165}
1166
1167fn has_named_annotation(anns: &[Annotation], name: &str) -> bool {
1168 anns.iter().any(|a| {
1169 a.name.parts.last().is_some_and(|p| p.text == name)
1170 && matches!(a.params, AnnotationParams::None | AnnotationParams::Empty)
1171 })
1172}
1173
1174fn find_uint_annotation(anns: &[Annotation], name: &str) -> Option<u32> {
1176 for a in anns {
1177 if a.name.parts.last().is_some_and(|p| p.text == name) {
1178 if let AnnotationParams::Single(expr) = &a.params {
1179 if let Some(v) = const_expr_as_u32(expr) {
1180 return Some(v);
1181 }
1182 }
1183 }
1184 }
1185 None
1186}
1187fn const_expr_as_u32(e: &ConstExpr) -> Option<u32> {
1191 match e {
1192 ConstExpr::Literal(Literal {
1193 kind: LiteralKind::Integer,
1194 raw,
1195 ..
1196 }) => parse_int_literal(raw).and_then(|v| u32::try_from(v).ok()),
1197 ConstExpr::Unary {
1198 op: zerodds_idl::ast::UnaryOp::Plus,
1199 operand,
1200 ..
1201 } => const_expr_as_u32(operand),
1202 _ => None,
1203 }
1204}
1205
1206fn parse_int_literal(raw: &str) -> Option<u64> {
1208 let s = raw.trim_end_matches(|c: char| matches!(c, 'l' | 'L' | 'u' | 'U'));
1209 if let Some(hex) = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")) {
1210 u64::from_str_radix(hex, 16).ok()
1211 } else if s.len() > 1 && s.starts_with('0') {
1212 u64::from_str_radix(&s[1..], 8).ok()
1213 } else {
1214 s.parse::<u64>().ok()
1215 }
1216}
1217
1218#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1220enum Extensibility {
1221 Final,
1222 Appendable,
1223 Mutable,
1224}
1225
1226fn struct_extensibility(anns: &[Annotation]) -> Extensibility {
1227 if has_named_annotation(anns, "final") {
1228 Extensibility::Final
1229 } else if has_named_annotation(anns, "mutable") {
1230 Extensibility::Mutable
1231 } else if has_named_annotation(anns, "appendable") {
1232 Extensibility::Appendable
1233 } else {
1234 Extensibility::Appendable
1236 }
1237}
1238
1239fn collect_inheritance_edges(
1247 defs: &[Definition],
1248 parents: &mut std::collections::HashMap<String, String>,
1249 prefix: &str,
1250) {
1251 for d in defs {
1252 match d {
1253 Definition::Module(m) => {
1254 let new_prefix = if prefix.is_empty() {
1255 m.name.text.clone()
1256 } else {
1257 format!("{prefix}::{}", m.name.text)
1258 };
1259 collect_inheritance_edges(&m.definitions, parents, &new_prefix);
1260 }
1261 Definition::Type(TypeDecl::Constr(ConstrTypeDecl::Struct(StructDcl::Def(s)))) => {
1262 let key = if prefix.is_empty() {
1263 s.name.text.clone()
1264 } else {
1265 format!("{prefix}::{}", s.name.text)
1266 };
1267 if let Some(b) = &s.base {
1268 let base_str = b
1269 .parts
1270 .iter()
1271 .map(|p| p.text.clone())
1272 .collect::<Vec<_>>()
1273 .join("::");
1274 parents.insert(key, base_str);
1275 }
1276 }
1277 _ => {}
1278 }
1279 }
1280}
1281
1282fn detect_inheritance_cycles(spec: &Specification) -> Result<(), CppGenError> {
1283 use std::collections::HashMap;
1284
1285 let mut parents: HashMap<String, String> = HashMap::new();
1286 collect_inheritance_edges(&spec.definitions, &mut parents, "");
1287
1288 for start in parents.keys() {
1290 let mut current = start.clone();
1291 let mut visited: BTreeSet<String> = BTreeSet::new();
1292 visited.insert(current.clone());
1293 while let Some(p) = parents.get(¤t) {
1294 let resolved = parents
1296 .keys()
1297 .find(|k| *k == p || k.ends_with(&format!("::{p}")))
1298 .cloned()
1299 .unwrap_or_else(|| p.clone());
1300 if visited.contains(&resolved) {
1301 return Err(CppGenError::InheritanceCycle {
1302 type_name: short_name(&resolved),
1303 });
1304 }
1305 visited.insert(resolved.clone());
1306 if resolved == current {
1308 return Err(CppGenError::InheritanceCycle {
1309 type_name: short_name(&resolved),
1310 });
1311 }
1312 current = resolved;
1313 if !parents.contains_key(¤t) {
1314 break;
1315 }
1316 }
1317 }
1318 Ok(())
1319}
1320
1321fn short_name(s: &str) -> String {
1322 s.rsplit("::").next().unwrap_or(s).to_string()
1323}
1324
1325fn collect_topic_structs<'a>(
1348 defs: &'a [Definition],
1349 prefix: &str,
1350 out: &mut Vec<(String, &'a StructDef)>,
1351) {
1352 for d in defs {
1353 match d {
1354 Definition::Module(m) => {
1355 let np = if prefix.is_empty() {
1356 m.name.text.clone()
1357 } else {
1358 format!("{prefix}::{}", m.name.text)
1359 };
1360 collect_topic_structs(&m.definitions, &np, out);
1361 }
1362 Definition::Type(TypeDecl::Constr(ConstrTypeDecl::Struct(StructDcl::Def(s)))) => {
1363 let fqn = if prefix.is_empty() {
1364 s.name.text.clone()
1365 } else {
1366 format!("{prefix}::{}", s.name.text)
1367 };
1368 out.push((fqn, s));
1369 }
1370 _ => {}
1371 }
1372 }
1373}
1374
1375fn emit_topic_type_support_specs(
1376 out: &mut String,
1377 opts: &CppGenOptions,
1378 structs: &[(String, &StructDef)],
1379) -> Result<(), CppGenError> {
1380 writeln!(out).map_err(fmt_err)?;
1381 writeln!(
1382 out,
1383 "// DDS-PSM-Cxx topic_type_support<T> -- auto-generiert (XCDR2 Wire, XTypes 1.3 7.4)."
1384 )
1385 .map_err(fmt_err)?;
1386 writeln!(out, "namespace dds {{").map_err(fmt_err)?;
1387 writeln!(out, "namespace topic {{").map_err(fmt_err)?;
1388 writeln!(out).map_err(fmt_err)?;
1389
1390 let user_prefix = opts
1391 .namespace_prefix
1392 .as_deref()
1393 .filter(|p| !p.is_empty())
1394 .unwrap_or("");
1395 for (fqn, s) in structs {
1396 let cpp_fqn = if user_prefix.is_empty() {
1397 format!("::{fqn}")
1398 } else {
1399 format!("::{user_prefix}::{fqn}")
1400 };
1401 emit_topic_type_support_for(out, &cpp_fqn, fqn, s)?;
1402 }
1403
1404 writeln!(out, "}} // namespace topic").map_err(fmt_err)?;
1405 writeln!(out, "}} // namespace dds").map_err(fmt_err)?;
1406 Ok(())
1407}
1408
1409fn member_codegen_supported(m: &Member) -> bool {
1413 !has_shared_annotation(&m.annotations)
1414}
1415
1416fn typespec_supported(ts: &TypeSpec) -> bool {
1418 match ts {
1419 TypeSpec::Primitive(_) => true,
1420 TypeSpec::String(s) => !s.wide,
1421 TypeSpec::Sequence(seq) => match &*seq.elem {
1422 TypeSpec::Primitive(_) => true,
1423 TypeSpec::String(s) => !s.wide,
1424 _ => false,
1425 },
1426 _ => false,
1427 }
1428}
1429
1430fn emit_topic_type_support_for(
1431 out: &mut String,
1432 cpp_fqn: &str,
1433 type_name: &str,
1434 s: &StructDef,
1435) -> Result<(), CppGenError> {
1436 let ext = struct_extensibility(&s.annotations);
1437
1438 writeln!(out, "template <>").map_err(fmt_err)?;
1439 writeln!(out, "struct topic_type_support<{cpp_fqn}> {{").map_err(fmt_err)?;
1440 writeln!(
1441 out,
1442 " static const char* type_name() {{ return \"{type_name}\"; }}"
1443 )
1444 .map_err(fmt_err)?;
1445
1446 let is_keyed = s.members.iter().any(|m| has_key_annotation(&m.annotations));
1448 writeln!(
1449 out,
1450 " static constexpr bool is_keyed() {{ return {}; }}",
1451 if is_keyed { "true" } else { "false" }
1452 )
1453 .map_err(fmt_err)?;
1454
1455 let ext_lit = match ext {
1457 Extensibility::Final => "FINAL",
1458 Extensibility::Appendable => "APPENDABLE",
1459 Extensibility::Mutable => "MUTABLE",
1460 };
1461 writeln!(
1462 out,
1463 " static constexpr ::dds::topic::core::policy::DataRepresentationKind extensibility() {{ return ::dds::topic::core::policy::DataRepresentationKind::{ext_lit}; }}"
1464 )
1465 .map_err(fmt_err)?;
1466
1467 emit_encode_fn(out, cpp_fqn, s, ext, false)?;
1469 emit_encode_fn(out, cpp_fqn, s, ext, true)?;
1471 emit_decode_fn(out, cpp_fqn, s, ext)?;
1473 emit_key_hash_fn(out, cpp_fqn, s, is_keyed)?;
1475
1476 writeln!(out, "}};").map_err(fmt_err)?;
1477 writeln!(out).map_err(fmt_err)?;
1478 Ok(())
1479}
1480
1481fn emit_encode_fn(
1482 out: &mut String,
1483 cpp_fqn: &str,
1484 s: &StructDef,
1485 ext: Extensibility,
1486 be: bool,
1487) -> Result<(), CppGenError> {
1488 let fn_name = if be { "encode_be" } else { "encode" };
1489 writeln!(
1490 out,
1491 " static std::vector<uint8_t> {fn_name}(const {cpp_fqn}& __v) {{"
1492 )
1493 .map_err(fmt_err)?;
1494 writeln!(out, " std::vector<uint8_t> __out;").map_err(fmt_err)?;
1495 writeln!(out, " (void)__v;").map_err(fmt_err)?;
1496
1497 let endian_suffix = if be { "be" } else { "le" };
1499
1500 match ext {
1501 Extensibility::Final => {
1502 writeln!(out, " const size_t __origin = 0;").map_err(fmt_err)?;
1505 writeln!(out, " (void)__origin;").map_err(fmt_err)?;
1506 for m in &s.members {
1507 emit_plain_member_encode(out, m, endian_suffix, "__origin")?;
1508 }
1509 }
1510 Extensibility::Appendable => {
1511 writeln!(
1512 out,
1513 " const auto __dh = ::dds::topic::xcdr2::dheader_begin(__out);"
1514 )
1515 .map_err(fmt_err)?;
1516 writeln!(out, " const size_t __origin = __out.size();").map_err(fmt_err)?;
1517 writeln!(out, " (void)__origin;").map_err(fmt_err)?;
1518 for m in &s.members {
1519 emit_plain_member_encode(out, m, endian_suffix, "__origin")?;
1520 }
1521 writeln!(
1522 out,
1523 " ::dds::topic::xcdr2::dheader_end(__out, __dh);"
1524 )
1525 .map_err(fmt_err)?;
1526 }
1527 Extensibility::Mutable => {
1528 writeln!(
1529 out,
1530 " const auto __scope = ::dds::topic::xcdr2::mutable_begin(__out);"
1531 )
1532 .map_err(fmt_err)?;
1533 writeln!(out, " const size_t __origin = __scope.origin;").map_err(fmt_err)?;
1534 for m in &s.members {
1535 emit_mutable_member_encode(out, m, endian_suffix)?;
1536 }
1537 writeln!(
1538 out,
1539 " ::dds::topic::xcdr2::mutable_end(__out, __scope);"
1540 )
1541 .map_err(fmt_err)?;
1542 }
1543 }
1544
1545 writeln!(out, " return __out;").map_err(fmt_err)?;
1546 writeln!(out, " }}").map_err(fmt_err)?;
1547 Ok(())
1548}
1549
1550fn emit_plain_member_encode(
1553 out: &mut String,
1554 m: &Member,
1555 endian: &str,
1556 origin: &str,
1557) -> Result<(), CppGenError> {
1558 if !member_codegen_supported(m) {
1559 for decl in &m.declarators {
1560 let name = &decl.name().text;
1561 writeln!(
1562 out,
1563 " // xcdr2: @shared member '{name}' nicht unterstuetzt (skip)"
1564 )
1565 .map_err(fmt_err)?;
1566 }
1567 return Ok(());
1568 }
1569 let is_optional = has_optional_annotation(&m.annotations);
1570 for decl in &m.declarators {
1571 let name = &decl.name().text;
1572 if !matches!(decl, Declarator::Simple(_)) {
1573 writeln!(
1574 out,
1575 " // xcdr2: array member '{name}' nicht unterstuetzt (skip)"
1576 )
1577 .map_err(fmt_err)?;
1578 continue;
1579 }
1580 if !typespec_supported(&m.type_spec) {
1581 writeln!(
1582 out,
1583 " // xcdr2: member '{name}' nicht unterstuetzt (nested/enum/wstring/map/fixed; skip)"
1584 )
1585 .map_err(fmt_err)?;
1586 continue;
1587 }
1588 if is_optional {
1589 writeln!(out, " if (__v.{name}().has_value()) {{").map_err(fmt_err)?;
1591 writeln!(out, " __out.push_back(uint8_t{{1}});").map_err(fmt_err)?;
1592 emit_value_write(
1593 out,
1594 &m.type_spec,
1595 &format!("(*__v.{name}())"),
1596 endian,
1597 origin,
1598 " ",
1599 )?;
1600 writeln!(out, " }} else {{").map_err(fmt_err)?;
1601 writeln!(out, " __out.push_back(uint8_t{{0}});").map_err(fmt_err)?;
1602 writeln!(out, " }}").map_err(fmt_err)?;
1603 } else {
1604 emit_value_write(
1605 out,
1606 &m.type_spec,
1607 &format!("__v.{name}()"),
1608 endian,
1609 origin,
1610 " ",
1611 )?;
1612 }
1613 }
1614 Ok(())
1615}
1616
1617fn emit_value_write(
1619 out: &mut String,
1620 ts: &TypeSpec,
1621 access: &str,
1622 endian: &str,
1623 origin: &str,
1624 indent: &str,
1625) -> Result<(), CppGenError> {
1626 let pre = format!("{indent} ");
1627 match ts {
1628 TypeSpec::Primitive(PrimitiveType::Boolean) => {
1629 writeln!(
1630 out,
1631 "{pre}::dds::topic::xcdr2::write_bool(__out, {access});"
1632 )
1633 .map_err(fmt_err)?;
1634 }
1635 TypeSpec::Primitive(PrimitiveType::Octet) => {
1636 writeln!(out, "{pre}::dds::topic::xcdr2::write_u8(__out, {access});")
1637 .map_err(fmt_err)?;
1638 }
1639 TypeSpec::Primitive(p) => {
1640 let cpp_ty = primitive_to_cpp(*p);
1641 writeln!(
1642 out,
1643 "{pre}::dds::topic::xcdr2::write_{endian}_origin<{cpp_ty}>(__out, {origin}, {access});"
1644 )
1645 .map_err(fmt_err)?;
1646 }
1647 TypeSpec::String(s) if !s.wide => {
1648 if endian == "be" {
1649 writeln!(
1650 out,
1651 "{pre}::dds::topic::xcdr2::write_string_be(__out, {access});"
1652 )
1653 .map_err(fmt_err)?;
1654 } else {
1655 writeln!(
1656 out,
1657 "{pre}::dds::topic::xcdr2::write_string_origin(__out, {origin}, {access});"
1658 )
1659 .map_err(fmt_err)?;
1660 }
1661 }
1662 TypeSpec::Sequence(seq) => {
1663 let count_call = if endian == "be" {
1664 format!(
1665 "{pre}::dds::topic::xcdr2::write_be<uint32_t>(__out, static_cast<uint32_t>({access}.size()));"
1666 )
1667 } else {
1668 format!(
1669 "{pre}::dds::topic::xcdr2::write_le_origin<uint32_t>(__out, {origin}, static_cast<uint32_t>({access}.size()));"
1670 )
1671 };
1672 writeln!(out, "{count_call}").map_err(fmt_err)?;
1673 writeln!(out, "{pre}for (const auto& __e : {access}) {{").map_err(fmt_err)?;
1674 let elem_indent = format!("{pre} ");
1675 match &*seq.elem {
1676 TypeSpec::Primitive(PrimitiveType::Boolean) => {
1677 writeln!(
1678 out,
1679 "{elem_indent}::dds::topic::xcdr2::write_bool(__out, __e);"
1680 )
1681 .map_err(fmt_err)?;
1682 }
1683 TypeSpec::Primitive(PrimitiveType::Octet) => {
1684 writeln!(
1685 out,
1686 "{elem_indent}::dds::topic::xcdr2::write_u8(__out, __e);"
1687 )
1688 .map_err(fmt_err)?;
1689 }
1690 TypeSpec::Primitive(p) => {
1691 let cpp_ty = primitive_to_cpp(*p);
1692 if endian == "be" {
1693 writeln!(
1694 out,
1695 "{elem_indent}::dds::topic::xcdr2::write_be<{cpp_ty}>(__out, __e);"
1696 )
1697 .map_err(fmt_err)?;
1698 } else {
1699 writeln!(
1700 out,
1701 "{elem_indent}::dds::topic::xcdr2::write_le_origin<{cpp_ty}>(__out, {origin}, __e);"
1702 )
1703 .map_err(fmt_err)?;
1704 }
1705 }
1706 TypeSpec::String(s) if !s.wide => {
1707 if endian == "be" {
1708 writeln!(
1709 out,
1710 "{elem_indent}::dds::topic::xcdr2::write_string_be(__out, __e);"
1711 )
1712 .map_err(fmt_err)?;
1713 } else {
1714 writeln!(
1715 out,
1716 "{elem_indent}::dds::topic::xcdr2::write_string_origin(__out, {origin}, __e);"
1717 )
1718 .map_err(fmt_err)?;
1719 }
1720 }
1721 _ => {
1722 writeln!(
1723 out,
1724 "{elem_indent}// xcdr2: nested/wstring sequence-element nicht unterstuetzt"
1725 )
1726 .map_err(fmt_err)?;
1727 }
1728 }
1729 writeln!(out, "{pre}}}").map_err(fmt_err)?;
1730 }
1731 _ => {
1732 writeln!(out, "{pre}// xcdr2: member type nicht unterstuetzt (skip)")
1733 .map_err(fmt_err)?;
1734 }
1735 }
1736 Ok(())
1737}
1738
1739fn emit_mutable_member_encode(
1741 out: &mut String,
1742 m: &Member,
1743 endian: &str,
1744) -> Result<(), CppGenError> {
1745 if !member_codegen_supported(m) {
1746 for decl in &m.declarators {
1747 let name = &decl.name().text;
1748 writeln!(
1749 out,
1750 " // xcdr2: @shared member '{name}' nicht unterstuetzt (skip)"
1751 )
1752 .map_err(fmt_err)?;
1753 }
1754 return Ok(());
1755 }
1756 let is_optional = has_optional_annotation(&m.annotations);
1757 let must_understand = has_named_annotation(&m.annotations, "must_understand");
1758 let id_override = find_uint_annotation(&m.annotations, "id");
1759 let mu_lit = if must_understand { "true" } else { "false" };
1760
1761 for (idx, decl) in m.declarators.iter().enumerate() {
1762 let name = &decl.name().text;
1763 if !matches!(decl, Declarator::Simple(_)) {
1764 writeln!(
1765 out,
1766 " // xcdr2: array member '{name}' nicht unterstuetzt (skip)"
1767 )
1768 .map_err(fmt_err)?;
1769 continue;
1770 }
1771 if !typespec_supported(&m.type_spec) {
1772 writeln!(
1773 out,
1774 " // xcdr2: member '{name}' nicht unterstuetzt (skip)"
1775 )
1776 .map_err(fmt_err)?;
1777 continue;
1778 }
1779 let _ = idx;
1783 let id_expr = match id_override {
1784 Some(id) => id.to_string(),
1785 None => format!("0x{:x}u", auto_id_for(name)),
1786 };
1787 if is_optional {
1788 writeln!(out, " if (__v.{name}().has_value()) {{").map_err(fmt_err)?;
1790 emit_mutable_value_emit(
1791 out,
1792 &m.type_spec,
1793 &format!("(*__v.{name}())"),
1794 &id_expr,
1795 mu_lit,
1796 endian,
1797 " ",
1798 )?;
1799 writeln!(out, " }}").map_err(fmt_err)?;
1800 } else {
1801 emit_mutable_value_emit(
1802 out,
1803 &m.type_spec,
1804 &format!("__v.{name}()"),
1805 &id_expr,
1806 mu_lit,
1807 endian,
1808 " ",
1809 )?;
1810 }
1811 }
1812 Ok(())
1813}
1814
1815fn auto_id_for(name: &str) -> u32 {
1819 let mut h: u32 = 0x811C9DC5;
1821 for b in name.as_bytes() {
1822 h ^= u32::from(*b);
1823 h = h.wrapping_mul(0x01000193);
1824 }
1825 h & 0x0FFF_FFFF
1826}
1827
1828fn emit_mutable_value_emit(
1829 out: &mut String,
1830 ts: &TypeSpec,
1831 access: &str,
1832 id_expr: &str,
1833 mu_lit: &str,
1834 endian: &str,
1835 indent: &str,
1836) -> Result<(), CppGenError> {
1837 match ts {
1838 TypeSpec::Primitive(PrimitiveType::Boolean) => {
1839 writeln!(
1840 out,
1841 "{indent}::dds::topic::xcdr2::emheader_u8(__out, __origin, {id_expr}, {mu_lit}, static_cast<uint8_t>({access} ? 1 : 0));"
1842 )
1843 .map_err(fmt_err)?;
1844 }
1845 TypeSpec::Primitive(PrimitiveType::Octet) => {
1846 writeln!(
1847 out,
1848 "{indent}::dds::topic::xcdr2::emheader_u8(__out, __origin, {id_expr}, {mu_lit}, {access});"
1849 )
1850 .map_err(fmt_err)?;
1851 }
1852 TypeSpec::Primitive(p) => {
1853 let cpp_ty = primitive_to_cpp(*p);
1854 let size = primitive_size(*p);
1856 match size {
1857 2 => {
1858 writeln!(
1859 out,
1860 "{indent}::dds::topic::xcdr2::emheader_2<{cpp_ty}>(__out, __origin, {id_expr}, {mu_lit}, {access});"
1861 )
1862 .map_err(fmt_err)?;
1863 }
1864 4 => {
1865 writeln!(
1866 out,
1867 "{indent}::dds::topic::xcdr2::emheader_4<{cpp_ty}>(__out, __origin, {id_expr}, {mu_lit}, {access});"
1868 )
1869 .map_err(fmt_err)?;
1870 }
1871 8 => {
1872 writeln!(
1873 out,
1874 "{indent}::dds::topic::xcdr2::emheader_8<{cpp_ty}>(__out, __origin, {id_expr}, {mu_lit}, {access});"
1875 )
1876 .map_err(fmt_err)?;
1877 }
1878 _ => {
1879 writeln!(
1880 out,
1881 "{indent}// xcdr2: unexpected primitive size {size} (skip)"
1882 )
1883 .map_err(fmt_err)?;
1884 }
1885 }
1886 }
1887 TypeSpec::String(s) if !s.wide => {
1888 writeln!(
1890 out,
1891 "{indent}{{ const auto __sub = ::dds::topic::xcdr2::emheader_nextint_begin(__out, __origin, {id_expr}, {mu_lit});"
1892 )
1893 .map_err(fmt_err)?;
1894 let body_endian = if endian == "be" { "be" } else { "le" };
1897 let _ = body_endian;
1898 writeln!(
1899 out,
1900 "{indent} {{ const auto __body_origin = __sub.body_start; (void)__body_origin;"
1901 )
1902 .map_err(fmt_err)?;
1903 if endian == "be" {
1904 writeln!(
1905 out,
1906 "{indent} ::dds::topic::xcdr2::write_string_be(__out, {access});"
1907 )
1908 .map_err(fmt_err)?;
1909 } else {
1910 writeln!(
1911 out,
1912 "{indent} ::dds::topic::xcdr2::write_string_origin(__out, __body_origin, {access});"
1913 )
1914 .map_err(fmt_err)?;
1915 }
1916 writeln!(out, "{indent} }}").map_err(fmt_err)?;
1917 writeln!(
1918 out,
1919 "{indent} ::dds::topic::xcdr2::emheader_nextint_end(__out, __sub); }}"
1920 )
1921 .map_err(fmt_err)?;
1922 }
1923 TypeSpec::Sequence(seq) => {
1924 writeln!(
1925 out,
1926 "{indent}{{ const auto __sub = ::dds::topic::xcdr2::emheader_nextint_begin(__out, __origin, {id_expr}, {mu_lit});"
1927 )
1928 .map_err(fmt_err)?;
1929 writeln!(
1930 out,
1931 "{indent} {{ const auto __body_origin = __sub.body_start; (void)__body_origin;"
1932 )
1933 .map_err(fmt_err)?;
1934 if endian == "be" {
1935 writeln!(
1936 out,
1937 "{indent} ::dds::topic::xcdr2::write_be<uint32_t>(__out, static_cast<uint32_t>({access}.size()));"
1938 )
1939 .map_err(fmt_err)?;
1940 } else {
1941 writeln!(
1942 out,
1943 "{indent} ::dds::topic::xcdr2::write_le_origin<uint32_t>(__out, __body_origin, static_cast<uint32_t>({access}.size()));"
1944 )
1945 .map_err(fmt_err)?;
1946 }
1947 writeln!(out, "{indent} for (const auto& __e : {access}) {{").map_err(fmt_err)?;
1948 match &*seq.elem {
1949 TypeSpec::Primitive(PrimitiveType::Boolean) => {
1950 writeln!(
1951 out,
1952 "{indent} ::dds::topic::xcdr2::write_bool(__out, __e);"
1953 )
1954 .map_err(fmt_err)?;
1955 }
1956 TypeSpec::Primitive(PrimitiveType::Octet) => {
1957 writeln!(
1958 out,
1959 "{indent} ::dds::topic::xcdr2::write_u8(__out, __e);"
1960 )
1961 .map_err(fmt_err)?;
1962 }
1963 TypeSpec::Primitive(p) => {
1964 let cpp_ty = primitive_to_cpp(*p);
1965 if endian == "be" {
1966 writeln!(
1967 out,
1968 "{indent} ::dds::topic::xcdr2::write_be<{cpp_ty}>(__out, __e);"
1969 )
1970 .map_err(fmt_err)?;
1971 } else {
1972 writeln!(out, "{indent} ::dds::topic::xcdr2::write_le_origin<{cpp_ty}>(__out, __body_origin, __e);").map_err(fmt_err)?;
1973 }
1974 }
1975 TypeSpec::String(s) if !s.wide => {
1976 if endian == "be" {
1977 writeln!(
1978 out,
1979 "{indent} ::dds::topic::xcdr2::write_string_be(__out, __e);"
1980 )
1981 .map_err(fmt_err)?;
1982 } else {
1983 writeln!(out, "{indent} ::dds::topic::xcdr2::write_string_origin(__out, __body_origin, __e);").map_err(fmt_err)?;
1984 }
1985 }
1986 _ => {
1987 writeln!(
1988 out,
1989 "{indent} // xcdr2: nested seq-elem nicht unterstuetzt"
1990 )
1991 .map_err(fmt_err)?;
1992 }
1993 }
1994 writeln!(out, "{indent} }}").map_err(fmt_err)?;
1995 writeln!(out, "{indent} }}").map_err(fmt_err)?;
1996 writeln!(
1997 out,
1998 "{indent} ::dds::topic::xcdr2::emheader_nextint_end(__out, __sub); }}"
1999 )
2000 .map_err(fmt_err)?;
2001 }
2002 _ => {
2003 writeln!(out, "{indent}// xcdr2: nicht unterstuetzter member-type").map_err(fmt_err)?;
2004 }
2005 }
2006 Ok(())
2007}
2008
2009fn primitive_size(p: PrimitiveType) -> usize {
2010 use zerodds_idl::ast::{FloatingType, IntegerType};
2011 match p {
2012 PrimitiveType::Boolean => 1,
2013 PrimitiveType::Octet => 1,
2014 PrimitiveType::Char => 1,
2015 PrimitiveType::WideChar => 2,
2016 PrimitiveType::Integer(i) => match i {
2017 IntegerType::Int8 | IntegerType::UInt8 => 1,
2018 IntegerType::Short | IntegerType::UShort | IntegerType::Int16 | IntegerType::UInt16 => {
2019 2
2020 }
2021 IntegerType::Long | IntegerType::ULong | IntegerType::Int32 | IntegerType::UInt32 => 4,
2022 IntegerType::LongLong
2023 | IntegerType::ULongLong
2024 | IntegerType::Int64
2025 | IntegerType::UInt64 => 8,
2026 },
2027 PrimitiveType::Floating(f) => match f {
2028 FloatingType::Float => 4,
2029 FloatingType::Double => 8,
2030 FloatingType::LongDouble => 16,
2031 },
2032 }
2033}
2034
2035fn emit_decode_fn(
2036 out: &mut String,
2037 cpp_fqn: &str,
2038 s: &StructDef,
2039 ext: Extensibility,
2040) -> Result<(), CppGenError> {
2041 writeln!(
2042 out,
2043 " static {cpp_fqn} decode(const uint8_t* __buf, size_t __len) {{"
2044 )
2045 .map_err(fmt_err)?;
2046 writeln!(out, " size_t __pos = 0;").map_err(fmt_err)?;
2047 writeln!(out, " {cpp_fqn} __v;").map_err(fmt_err)?;
2048 writeln!(out, " (void)__buf; (void)__len; (void)__pos;").map_err(fmt_err)?;
2049
2050 match ext {
2051 Extensibility::Final => {
2052 writeln!(out, " const size_t __origin = 0;").map_err(fmt_err)?;
2053 writeln!(out, " (void)__origin;").map_err(fmt_err)?;
2054 for m in &s.members {
2055 emit_plain_member_decode(out, m, "__origin")?;
2056 }
2057 }
2058 Extensibility::Appendable => {
2059 writeln!(
2060 out,
2061 " const auto __dh = ::dds::topic::xcdr2::dheader_read(__buf, __pos, __len);"
2062 )
2063 .map_err(fmt_err)?;
2064 writeln!(out, " const size_t __origin = __pos;").map_err(fmt_err)?;
2065 writeln!(out, " const size_t __end = __origin + __dh;").map_err(fmt_err)?;
2066 writeln!(out, " (void)__end;").map_err(fmt_err)?;
2067 for m in &s.members {
2068 emit_plain_member_decode(out, m, "__origin")?;
2069 }
2070 writeln!(out, " if (__pos < __end) __pos = __end;").map_err(fmt_err)?;
2072 }
2073 Extensibility::Mutable => {
2074 writeln!(
2075 out,
2076 " const auto __dh = ::dds::topic::xcdr2::dheader_read(__buf, __pos, __len);"
2077 )
2078 .map_err(fmt_err)?;
2079 writeln!(out, " const size_t __origin = __pos;").map_err(fmt_err)?;
2080 writeln!(out, " const size_t __end = __origin + __dh;").map_err(fmt_err)?;
2081 writeln!(out, " while (__pos + 4 <= __end) {{").map_err(fmt_err)?;
2082 writeln!(
2083 out,
2084 " const auto __h = ::dds::topic::xcdr2::emheader_read(__buf, __pos, __len, __origin);"
2085 )
2086 .map_err(fmt_err)?;
2087 writeln!(out, " switch (__h.member_id) {{").map_err(fmt_err)?;
2088 for m in &s.members {
2089 emit_mutable_member_decode_case(out, m)?;
2090 }
2091 writeln!(out, " default: {{").map_err(fmt_err)?;
2092 writeln!(
2093 out,
2094 " // unbekannter Member: NEXTINT-Skip falls LC>=3 ohne primitive-mapping."
2095 )
2096 .map_err(fmt_err)?;
2097 writeln!(out, " if (__h.lc == 0) {{ ++__pos; }}")
2098 .map_err(fmt_err)?;
2099 writeln!(
2100 out,
2101 " else if (__h.lc == 1) {{ __pos += 2; }}"
2102 )
2103 .map_err(fmt_err)?;
2104 writeln!(
2105 out,
2106 " else if (__h.lc == 2) {{ __pos += 4; }}"
2107 )
2108 .map_err(fmt_err)?;
2109 writeln!(
2110 out,
2111 " else {{ auto __n = ::dds::topic::xcdr2::emheader_nextint_read(__buf, __pos, __len); __pos += __n; }}"
2112 )
2113 .map_err(fmt_err)?;
2114 writeln!(out, " break;").map_err(fmt_err)?;
2115 writeln!(out, " }}").map_err(fmt_err)?;
2116 writeln!(out, " }}").map_err(fmt_err)?;
2117 writeln!(out, " }}").map_err(fmt_err)?;
2118 writeln!(out, " if (__pos < __end) __pos = __end;").map_err(fmt_err)?;
2119 }
2120 }
2121
2122 writeln!(out, " return __v;").map_err(fmt_err)?;
2123 writeln!(out, " }}").map_err(fmt_err)?;
2124 Ok(())
2125}
2126
2127fn emit_plain_member_decode(out: &mut String, m: &Member, origin: &str) -> Result<(), CppGenError> {
2128 if !member_codegen_supported(m) {
2129 for decl in &m.declarators {
2130 let name = &decl.name().text;
2131 writeln!(
2132 out,
2133 " // xcdr2: @shared member '{name}' nicht unterstuetzt (skip)"
2134 )
2135 .map_err(fmt_err)?;
2136 }
2137 return Ok(());
2138 }
2139 let is_optional = has_optional_annotation(&m.annotations);
2140 for decl in &m.declarators {
2141 let name = &decl.name().text;
2142 if !matches!(decl, Declarator::Simple(_)) {
2143 writeln!(
2144 out,
2145 " // xcdr2: array member '{name}' nicht unterstuetzt (skip)"
2146 )
2147 .map_err(fmt_err)?;
2148 continue;
2149 }
2150 if !typespec_supported(&m.type_spec) {
2151 writeln!(
2152 out,
2153 " // xcdr2: member '{name}' nicht unterstuetzt (skip)"
2154 )
2155 .map_err(fmt_err)?;
2156 continue;
2157 }
2158 if is_optional {
2159 writeln!(out, " {{").map_err(fmt_err)?;
2160 writeln!(
2161 out,
2162 " uint8_t __present = ::dds::topic::xcdr2::read_u8(__buf, __pos, __len);"
2163 )
2164 .map_err(fmt_err)?;
2165 writeln!(out, " if (__present) {{").map_err(fmt_err)?;
2166 emit_value_read(
2167 out,
2168 &m.type_spec,
2169 &format!("__v.{name}"),
2170 origin,
2171 " ",
2172 true,
2173 )?;
2174 writeln!(out, " }} else {{").map_err(fmt_err)?;
2175 writeln!(out, " __v.{name}(std::nullopt);").map_err(fmt_err)?;
2176 writeln!(out, " }}").map_err(fmt_err)?;
2177 writeln!(out, " }}").map_err(fmt_err)?;
2178 } else {
2179 emit_value_read(
2180 out,
2181 &m.type_spec,
2182 &format!("__v.{name}"),
2183 origin,
2184 " ",
2185 false,
2186 )?;
2187 }
2188 }
2189 Ok(())
2190}
2191
2192fn emit_value_read(
2193 out: &mut String,
2194 ts: &TypeSpec,
2195 setter: &str,
2196 origin: &str,
2197 indent: &str,
2198 is_opt: bool,
2199) -> Result<(), CppGenError> {
2200 let wrap_opt = |v: String| -> String {
2201 if is_opt {
2202 format!("std::optional<decltype({v})>({v})")
2203 } else {
2204 v
2205 }
2206 };
2207 let _ = wrap_opt;
2208 match ts {
2209 TypeSpec::Primitive(PrimitiveType::Boolean) => {
2210 writeln!(
2211 out,
2212 "{indent}{setter}(::dds::topic::xcdr2::read_bool(__buf, __pos, __len));"
2213 )
2214 .map_err(fmt_err)?;
2215 }
2216 TypeSpec::Primitive(PrimitiveType::Octet) => {
2217 writeln!(
2218 out,
2219 "{indent}{setter}(::dds::topic::xcdr2::read_u8(__buf, __pos, __len));"
2220 )
2221 .map_err(fmt_err)?;
2222 }
2223 TypeSpec::Primitive(p) => {
2224 let cpp_ty = primitive_to_cpp(*p);
2225 writeln!(
2226 out,
2227 "{indent}{setter}(::dds::topic::xcdr2::read_le_origin<{cpp_ty}>(__buf, __pos, __len, {origin}));"
2228 )
2229 .map_err(fmt_err)?;
2230 }
2231 TypeSpec::String(s) if !s.wide => {
2232 writeln!(
2233 out,
2234 "{indent}{setter}(::dds::topic::xcdr2::read_string_origin(__buf, __pos, __len, {origin}));"
2235 )
2236 .map_err(fmt_err)?;
2237 }
2238 TypeSpec::Sequence(seq) => {
2239 let elem_cpp_ty: String = match &*seq.elem {
2240 TypeSpec::Primitive(PrimitiveType::Boolean) => "bool".to_string(),
2241 TypeSpec::Primitive(p) => primitive_to_cpp(*p).to_string(),
2242 TypeSpec::String(s) if !s.wide => "std::string".to_string(),
2243 _ => {
2244 writeln!(
2245 out,
2246 "{indent}// xcdr2: nested seq-elem nicht unterstuetzt (skip)"
2247 )
2248 .map_err(fmt_err)?;
2249 return Ok(());
2250 }
2251 };
2252 writeln!(out, "{indent}{{").map_err(fmt_err)?;
2253 writeln!(out, "{indent} auto __cnt = ::dds::topic::xcdr2::read_le_origin<uint32_t>(__buf, __pos, __len, {origin});").map_err(fmt_err)?;
2254 writeln!(out, "{indent} std::vector<{elem_cpp_ty}> __seq;").map_err(fmt_err)?;
2255 writeln!(out, "{indent} __seq.reserve(__cnt);").map_err(fmt_err)?;
2256 writeln!(
2257 out,
2258 "{indent} for (uint32_t __i = 0; __i < __cnt; ++__i) {{"
2259 )
2260 .map_err(fmt_err)?;
2261 match &*seq.elem {
2262 TypeSpec::Primitive(PrimitiveType::Boolean) => {
2263 writeln!(out, "{indent} __seq.push_back(::dds::topic::xcdr2::read_bool(__buf, __pos, __len));").map_err(fmt_err)?;
2264 }
2265 TypeSpec::Primitive(PrimitiveType::Octet) => {
2266 writeln!(out, "{indent} __seq.push_back(::dds::topic::xcdr2::read_u8(__buf, __pos, __len));").map_err(fmt_err)?;
2267 }
2268 TypeSpec::Primitive(p) => {
2269 let cpp_ty = primitive_to_cpp(*p);
2270 writeln!(out, "{indent} __seq.push_back(::dds::topic::xcdr2::read_le_origin<{cpp_ty}>(__buf, __pos, __len, {origin}));").map_err(fmt_err)?;
2271 }
2272 TypeSpec::String(s) if !s.wide => {
2273 writeln!(out, "{indent} __seq.push_back(::dds::topic::xcdr2::read_string_origin(__buf, __pos, __len, {origin}));").map_err(fmt_err)?;
2274 }
2275 _ => {}
2276 }
2277 writeln!(out, "{indent} }}").map_err(fmt_err)?;
2278 writeln!(out, "{indent} {setter}(std::move(__seq));").map_err(fmt_err)?;
2279 writeln!(out, "{indent}}}").map_err(fmt_err)?;
2280 }
2281 _ => {}
2282 }
2283 Ok(())
2284}
2285
2286fn emit_mutable_member_decode_case(out: &mut String, m: &Member) -> Result<(), CppGenError> {
2287 if !member_codegen_supported(m) {
2288 return Ok(());
2289 }
2290 let id_override = find_uint_annotation(&m.annotations, "id");
2291 let is_optional = has_optional_annotation(&m.annotations);
2292 let _ = is_optional; for decl in &m.declarators {
2294 let name = &decl.name().text;
2295 if !matches!(decl, Declarator::Simple(_)) {
2296 continue;
2297 }
2298 if !typespec_supported(&m.type_spec) {
2299 continue;
2300 }
2301 let id_expr = match id_override {
2302 Some(id) => id.to_string(),
2303 None => format!("0x{:x}u", auto_id_for(name)),
2304 };
2305 writeln!(out, " case {id_expr}: {{").map_err(fmt_err)?;
2306 match &m.type_spec {
2307 TypeSpec::Primitive(PrimitiveType::Boolean) => {
2308 writeln!(out, " uint8_t __b = ::dds::topic::xcdr2::read_u8(__buf, __pos, __len);").map_err(fmt_err)?;
2309 if has_optional_annotation(&m.annotations) {
2310 writeln!(
2311 out,
2312 " __v.{name}(static_cast<bool>(__b));"
2313 )
2314 .map_err(fmt_err)?;
2315 } else {
2316 writeln!(
2317 out,
2318 " __v.{name}(static_cast<bool>(__b));"
2319 )
2320 .map_err(fmt_err)?;
2321 }
2322 }
2323 TypeSpec::Primitive(PrimitiveType::Octet) => {
2324 writeln!(out, " __v.{name}(::dds::topic::xcdr2::read_u8(__buf, __pos, __len));").map_err(fmt_err)?;
2325 }
2326 TypeSpec::Primitive(p) => {
2327 let cpp_ty = primitive_to_cpp(*p);
2328 writeln!(out, " __v.{name}(::dds::topic::xcdr2::read_le_raw<{cpp_ty}>(__buf, __pos, __len));").map_err(fmt_err)?;
2329 }
2330 TypeSpec::String(s) if !s.wide => {
2331 writeln!(out, " auto __n = ::dds::topic::xcdr2::emheader_nextint_read(__buf, __pos, __len);").map_err(fmt_err)?;
2332 writeln!(out, " (void)__n;").map_err(fmt_err)?;
2333 writeln!(out, " auto __body_origin = __pos;")
2334 .map_err(fmt_err)?;
2335 writeln!(out, " __v.{name}(::dds::topic::xcdr2::read_string_origin(__buf, __pos, __len, __body_origin));").map_err(fmt_err)?;
2336 }
2337 TypeSpec::Sequence(seq) => {
2338 let elem_cpp_ty: String = match &*seq.elem {
2339 TypeSpec::Primitive(PrimitiveType::Boolean) => "bool".to_string(),
2340 TypeSpec::Primitive(p) => primitive_to_cpp(*p).to_string(),
2341 TypeSpec::String(s) if !s.wide => "std::string".to_string(),
2342 _ => "uint8_t".to_string(),
2343 };
2344 writeln!(out, " auto __n = ::dds::topic::xcdr2::emheader_nextint_read(__buf, __pos, __len);").map_err(fmt_err)?;
2345 writeln!(out, " (void)__n;").map_err(fmt_err)?;
2346 writeln!(out, " auto __body_origin = __pos;")
2347 .map_err(fmt_err)?;
2348 writeln!(out, " auto __cnt = ::dds::topic::xcdr2::read_le_origin<uint32_t>(__buf, __pos, __len, __body_origin);").map_err(fmt_err)?;
2349 writeln!(out, " std::vector<{elem_cpp_ty}> __seq;")
2350 .map_err(fmt_err)?;
2351 writeln!(out, " __seq.reserve(__cnt);").map_err(fmt_err)?;
2352 writeln!(
2353 out,
2354 " for (uint32_t __i = 0; __i < __cnt; ++__i) {{"
2355 )
2356 .map_err(fmt_err)?;
2357 match &*seq.elem {
2358 TypeSpec::Primitive(PrimitiveType::Boolean) => {
2359 writeln!(out, " __seq.push_back(::dds::topic::xcdr2::read_bool(__buf, __pos, __len));").map_err(fmt_err)?;
2360 }
2361 TypeSpec::Primitive(PrimitiveType::Octet) => {
2362 writeln!(out, " __seq.push_back(::dds::topic::xcdr2::read_u8(__buf, __pos, __len));").map_err(fmt_err)?;
2363 }
2364 TypeSpec::Primitive(p) => {
2365 let cpp_ty = primitive_to_cpp(*p);
2366 writeln!(out, " __seq.push_back(::dds::topic::xcdr2::read_le_origin<{cpp_ty}>(__buf, __pos, __len, __body_origin));").map_err(fmt_err)?;
2367 }
2368 TypeSpec::String(s) if !s.wide => {
2369 writeln!(out, " __seq.push_back(::dds::topic::xcdr2::read_string_origin(__buf, __pos, __len, __body_origin));").map_err(fmt_err)?;
2370 }
2371 _ => {}
2372 }
2373 writeln!(out, " }}").map_err(fmt_err)?;
2374 writeln!(out, " __v.{name}(std::move(__seq));")
2375 .map_err(fmt_err)?;
2376 }
2377 _ => {}
2378 }
2379 writeln!(out, " break;").map_err(fmt_err)?;
2380 writeln!(out, " }}").map_err(fmt_err)?;
2381 }
2382 Ok(())
2383}
2384
2385fn emit_key_hash_fn(
2386 out: &mut String,
2387 cpp_fqn: &str,
2388 s: &StructDef,
2389 is_keyed: bool,
2390) -> Result<(), CppGenError> {
2391 writeln!(
2392 out,
2393 " static std::array<uint8_t, 16> key_hash(const {cpp_fqn}& __v) {{"
2394 )
2395 .map_err(fmt_err)?;
2396 writeln!(out, " (void)__v;").map_err(fmt_err)?;
2397 if !is_keyed {
2398 writeln!(
2399 out,
2400 " return std::array<uint8_t, 16>{{{{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}}};"
2401 )
2402 .map_err(fmt_err)?;
2403 writeln!(out, " }}").map_err(fmt_err)?;
2404 return Ok(());
2405 }
2406 writeln!(out, " std::vector<uint8_t> __out;").map_err(fmt_err)?;
2407 writeln!(out, " const size_t __origin = 0;").map_err(fmt_err)?;
2408 writeln!(out, " (void)__origin;").map_err(fmt_err)?;
2409 for m in &s.members {
2410 if !has_key_annotation(&m.annotations) {
2411 continue;
2412 }
2413 emit_plain_member_encode(out, m, "be", "__origin")?;
2414 }
2415 writeln!(out, " std::array<uint8_t, 16> __h{{}};").map_err(fmt_err)?;
2417 writeln!(out, " if (__out.size() <= 16) {{").map_err(fmt_err)?;
2418 writeln!(
2419 out,
2420 " std::memcpy(__h.data(), __out.data(), __out.size());"
2421 )
2422 .map_err(fmt_err)?;
2423 writeln!(out, " return __h;").map_err(fmt_err)?;
2424 writeln!(out, " }}").map_err(fmt_err)?;
2425 writeln!(out, " return ::dds::topic::xcdr2_md5::md5(__out);").map_err(fmt_err)?;
2426 writeln!(out, " }}").map_err(fmt_err)?;
2427 Ok(())
2428}
2429
2430fn fmt_err(_: core::fmt::Error) -> CppGenError {
2435 CppGenError::Internal("string formatting failed".into())
2436}
2437
2438#[allow(dead_code)]
2439fn _ensure_used() {
2440 let _ = is_reserved("int");
2442}