Skip to main content

zerodds_idl_cpp/
dcps.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! Block-H: DCPS-Entity-Header-Stubs (Spec idl4-cpp-1.0 §7.6 + dds-psm-cxx-1.0 §8.1).
4//!
5//! Emittiert Class-Declarations und Method-Signaturen fuer die DCPS-Kern-
6//! Entitaeten:
7//!
8//! - `dds::domain::DomainParticipant`
9//! - `dds::pub::Publisher` + `dds::pub::DataWriter<T>`
10//! - `dds::sub::Subscriber` + `dds::sub::DataReader<T>`
11//! - `dds::topic::Topic<T>`
12//! - `dds::core::Entity` (Basisklasse)
13//!
14//! Die volle Implementation liegt nicht im Codegen — das ist Aufgabe der
15//! handgeschriebenen DDS-PSM-CXX-Runtime (Cluster C5.2 Templates).
16//! Der Codegen liefert hier nur das Header-Skeleton, gegen das Anwender-
17//! Code kompiliert.
18
19use core::fmt::Write;
20
21use crate::error::CppGenError;
22
23/// Schreibt den vollstaendigen DCPS-Header.
24///
25/// # Errors
26/// Liefert [`CppGenError::Internal`], wenn das Schreiben in den
27/// `String`-Buffer scheitert.
28pub fn emit_dcps_header(out: &mut String) -> Result<(), CppGenError> {
29    writeln!(
30        out,
31        "// Block-H: DCPS-Entity-Header-Stubs (dds-psm-cxx-1.0 §8.1)."
32    )
33    .map_err(fmt_err)?;
34    writeln!(out).map_err(fmt_err)?;
35
36    emit_entity_base(out)?;
37    emit_domain_participant(out)?;
38    emit_topic(out)?;
39    emit_publisher(out)?;
40    emit_subscriber(out)?;
41    emit_data_writer(out)?;
42    emit_data_reader(out)?;
43
44    Ok(())
45}
46
47/// Liefert die Liste der vom Block-H emittierten Klassen-FQN.
48#[must_use]
49pub fn dcps_class_names() -> Vec<&'static str> {
50    vec![
51        "dds::core::Entity",
52        "dds::domain::DomainParticipant",
53        "dds::topic::Topic",
54        "dds::pub::Publisher",
55        "dds::sub::Subscriber",
56        "dds::pub::DataWriter",
57        "dds::sub::DataReader",
58    ]
59}
60
61fn emit_entity_base(out: &mut String) -> Result<(), CppGenError> {
62    writeln!(out, "namespace dds {{ namespace core {{").map_err(fmt_err)?;
63    writeln!(out).map_err(fmt_err)?;
64    writeln!(
65        out,
66        "/// Basisklasse aller DCPS-Entitaeten (dds-psm-cxx-1.0 §7.5.1)."
67    )
68    .map_err(fmt_err)?;
69    writeln!(out, "class Entity {{").map_err(fmt_err)?;
70    writeln!(out, "public:").map_err(fmt_err)?;
71    writeln!(out, "    virtual ~Entity() = default;").map_err(fmt_err)?;
72    writeln!(out, "    void enable();").map_err(fmt_err)?;
73    writeln!(out, "    void close();").map_err(fmt_err)?;
74    writeln!(
75        out,
76        "    const ::dds::core::InstanceHandle& instance_handle() const;"
77    )
78    .map_err(fmt_err)?;
79    writeln!(out, "}};").map_err(fmt_err)?;
80    writeln!(out).map_err(fmt_err)?;
81    writeln!(out, "}} }} // namespace dds::core").map_err(fmt_err)?;
82    writeln!(out).map_err(fmt_err)?;
83    Ok(())
84}
85
86fn emit_domain_participant(out: &mut String) -> Result<(), CppGenError> {
87    writeln!(out, "namespace dds {{ namespace domain {{").map_err(fmt_err)?;
88    writeln!(out).map_err(fmt_err)?;
89    writeln!(out, "/// DomainParticipant (dds-psm-cxx-1.0 §8.1.1).").map_err(fmt_err)?;
90    writeln!(
91        out,
92        "class DomainParticipant : public ::dds::core::Entity {{"
93    )
94    .map_err(fmt_err)?;
95    writeln!(out, "public:").map_err(fmt_err)?;
96    writeln!(out, "    explicit DomainParticipant(int32_t domain_id);").map_err(fmt_err)?;
97    writeln!(
98        out,
99        "    DomainParticipant(int32_t domain_id, const ::dds::domain::qos::DomainParticipantQos& qos);"
100    )
101    .map_err(fmt_err)?;
102    writeln!(out, "    ~DomainParticipant() override;").map_err(fmt_err)?;
103    writeln!(out).map_err(fmt_err)?;
104    writeln!(out, "    int32_t domain_id() const;").map_err(fmt_err)?;
105    writeln!(
106        out,
107        "    const ::dds::domain::qos::DomainParticipantQos& qos() const;"
108    )
109    .map_err(fmt_err)?;
110    writeln!(
111        out,
112        "    void qos(const ::dds::domain::qos::DomainParticipantQos& q);"
113    )
114    .map_err(fmt_err)?;
115    writeln!(out, "    void assert_liveliness();").map_err(fmt_err)?;
116    writeln!(out, "}};").map_err(fmt_err)?;
117    writeln!(out).map_err(fmt_err)?;
118    writeln!(out, "}} }} // namespace dds::domain").map_err(fmt_err)?;
119    writeln!(out).map_err(fmt_err)?;
120    Ok(())
121}
122
123fn emit_topic(out: &mut String) -> Result<(), CppGenError> {
124    writeln!(out, "namespace dds {{ namespace topic {{").map_err(fmt_err)?;
125    writeln!(out).map_err(fmt_err)?;
126    writeln!(out, "/// Topic<T> (dds-psm-cxx-1.0 §8.1.2).").map_err(fmt_err)?;
127    writeln!(out, "template <typename T>").map_err(fmt_err)?;
128    writeln!(out, "class Topic : public ::dds::core::Entity {{").map_err(fmt_err)?;
129    writeln!(out, "public:").map_err(fmt_err)?;
130    writeln!(
131        out,
132        "    Topic(::dds::domain::DomainParticipant& dp, const std::string& name);"
133    )
134    .map_err(fmt_err)?;
135    writeln!(
136        out,
137        "    Topic(::dds::domain::DomainParticipant& dp, const std::string& name, const ::dds::topic::qos::TopicQos& qos);"
138    )
139    .map_err(fmt_err)?;
140    writeln!(out, "    ~Topic() override;").map_err(fmt_err)?;
141    writeln!(out).map_err(fmt_err)?;
142    writeln!(out, "    const std::string& name() const;").map_err(fmt_err)?;
143    writeln!(out, "    const std::string& type_name() const;").map_err(fmt_err)?;
144    writeln!(out, "    const ::dds::topic::qos::TopicQos& qos() const;").map_err(fmt_err)?;
145    writeln!(out, "    void qos(const ::dds::topic::qos::TopicQos& q);").map_err(fmt_err)?;
146    writeln!(out, "}};").map_err(fmt_err)?;
147    writeln!(out).map_err(fmt_err)?;
148    writeln!(out, "}} }} // namespace dds::topic").map_err(fmt_err)?;
149    writeln!(out).map_err(fmt_err)?;
150    Ok(())
151}
152
153fn emit_publisher(out: &mut String) -> Result<(), CppGenError> {
154    writeln!(out, "namespace dds {{ namespace pub {{").map_err(fmt_err)?;
155    writeln!(out).map_err(fmt_err)?;
156    writeln!(out, "/// Publisher (dds-psm-cxx-1.0 §8.1.3).").map_err(fmt_err)?;
157    writeln!(out, "class Publisher : public ::dds::core::Entity {{").map_err(fmt_err)?;
158    writeln!(out, "public:").map_err(fmt_err)?;
159    writeln!(
160        out,
161        "    explicit Publisher(::dds::domain::DomainParticipant& dp);"
162    )
163    .map_err(fmt_err)?;
164    writeln!(
165        out,
166        "    Publisher(::dds::domain::DomainParticipant& dp, const ::dds::pub::qos::PublisherQos& qos);"
167    )
168    .map_err(fmt_err)?;
169    writeln!(out, "    ~Publisher() override;").map_err(fmt_err)?;
170    writeln!(out).map_err(fmt_err)?;
171    writeln!(out, "    const ::dds::pub::qos::PublisherQos& qos() const;").map_err(fmt_err)?;
172    writeln!(out, "    void qos(const ::dds::pub::qos::PublisherQos& q);").map_err(fmt_err)?;
173    writeln!(
174        out,
175        "    void wait_for_acknowledgments(const ::dds::core::Duration& timeout);"
176    )
177    .map_err(fmt_err)?;
178    writeln!(out, "}};").map_err(fmt_err)?;
179    writeln!(out).map_err(fmt_err)?;
180    writeln!(out, "}} }} // namespace dds::pub").map_err(fmt_err)?;
181    writeln!(out).map_err(fmt_err)?;
182    Ok(())
183}
184
185fn emit_subscriber(out: &mut String) -> Result<(), CppGenError> {
186    writeln!(out, "namespace dds {{ namespace sub {{").map_err(fmt_err)?;
187    writeln!(out).map_err(fmt_err)?;
188    writeln!(out, "/// Subscriber (dds-psm-cxx-1.0 §8.1.4).").map_err(fmt_err)?;
189    writeln!(out, "class Subscriber : public ::dds::core::Entity {{").map_err(fmt_err)?;
190    writeln!(out, "public:").map_err(fmt_err)?;
191    writeln!(
192        out,
193        "    explicit Subscriber(::dds::domain::DomainParticipant& dp);"
194    )
195    .map_err(fmt_err)?;
196    writeln!(
197        out,
198        "    Subscriber(::dds::domain::DomainParticipant& dp, const ::dds::sub::qos::SubscriberQos& qos);"
199    )
200    .map_err(fmt_err)?;
201    writeln!(out, "    ~Subscriber() override;").map_err(fmt_err)?;
202    writeln!(out).map_err(fmt_err)?;
203    writeln!(
204        out,
205        "    const ::dds::sub::qos::SubscriberQos& qos() const;"
206    )
207    .map_err(fmt_err)?;
208    writeln!(
209        out,
210        "    void qos(const ::dds::sub::qos::SubscriberQos& q);"
211    )
212    .map_err(fmt_err)?;
213    writeln!(out, "    void notify_datareaders();").map_err(fmt_err)?;
214    writeln!(out, "}};").map_err(fmt_err)?;
215    writeln!(out).map_err(fmt_err)?;
216    writeln!(out, "}} }} // namespace dds::sub").map_err(fmt_err)?;
217    writeln!(out).map_err(fmt_err)?;
218    Ok(())
219}
220
221fn emit_data_writer(out: &mut String) -> Result<(), CppGenError> {
222    writeln!(out, "namespace dds {{ namespace pub {{").map_err(fmt_err)?;
223    writeln!(out).map_err(fmt_err)?;
224    writeln!(out, "/// DataWriter<T> (dds-psm-cxx-1.0 §8.1.5).").map_err(fmt_err)?;
225    writeln!(out, "template <typename T>").map_err(fmt_err)?;
226    writeln!(out, "class DataWriter : public ::dds::core::Entity {{").map_err(fmt_err)?;
227    writeln!(out, "public:").map_err(fmt_err)?;
228    writeln!(
229        out,
230        "    DataWriter(::dds::pub::Publisher& pub, ::dds::topic::Topic<T>& topic);"
231    )
232    .map_err(fmt_err)?;
233    writeln!(
234        out,
235        "    DataWriter(::dds::pub::Publisher& pub, ::dds::topic::Topic<T>& topic, const ::dds::pub::qos::DataWriterQos& qos);"
236    )
237    .map_err(fmt_err)?;
238    writeln!(out, "    ~DataWriter() override;").map_err(fmt_err)?;
239    writeln!(out).map_err(fmt_err)?;
240    writeln!(out, "    void write(const T& sample);").map_err(fmt_err)?;
241    writeln!(
242        out,
243        "    void write(const T& sample, const ::dds::core::Time& src_time);"
244    )
245    .map_err(fmt_err)?;
246    writeln!(
247        out,
248        "    ::dds::core::InstanceHandle register_instance(const T& key);"
249    )
250    .map_err(fmt_err)?;
251    writeln!(
252        out,
253        "    void unregister_instance(const ::dds::core::InstanceHandle& h);"
254    )
255    .map_err(fmt_err)?;
256    writeln!(
257        out,
258        "    void dispose_instance(const ::dds::core::InstanceHandle& h);"
259    )
260    .map_err(fmt_err)?;
261    writeln!(
262        out,
263        "    void wait_for_acknowledgments(const ::dds::core::Duration& timeout);"
264    )
265    .map_err(fmt_err)?;
266    writeln!(
267        out,
268        "    ::dds::core::status::PublicationMatchedStatus publication_matched_status();"
269    )
270    .map_err(fmt_err)?;
271    writeln!(out, "}};").map_err(fmt_err)?;
272    writeln!(out).map_err(fmt_err)?;
273    writeln!(out, "}} }} // namespace dds::pub").map_err(fmt_err)?;
274    writeln!(out).map_err(fmt_err)?;
275    Ok(())
276}
277
278fn emit_data_reader(out: &mut String) -> Result<(), CppGenError> {
279    writeln!(out, "namespace dds {{ namespace sub {{").map_err(fmt_err)?;
280    writeln!(out).map_err(fmt_err)?;
281    writeln!(out, "/// DataReader<T> (dds-psm-cxx-1.0 §8.1.6).").map_err(fmt_err)?;
282    writeln!(out, "template <typename T>").map_err(fmt_err)?;
283    writeln!(out, "class DataReader : public ::dds::core::Entity {{").map_err(fmt_err)?;
284    writeln!(out, "public:").map_err(fmt_err)?;
285    writeln!(
286        out,
287        "    DataReader(::dds::sub::Subscriber& sub, ::dds::topic::Topic<T>& topic);"
288    )
289    .map_err(fmt_err)?;
290    writeln!(
291        out,
292        "    DataReader(::dds::sub::Subscriber& sub, ::dds::topic::Topic<T>& topic, const ::dds::sub::qos::DataReaderQos& qos);"
293    )
294    .map_err(fmt_err)?;
295    writeln!(out, "    ~DataReader() override;").map_err(fmt_err)?;
296    writeln!(out).map_err(fmt_err)?;
297    writeln!(out, "    std::vector<::dds::sub::Sample<T>> take();").map_err(fmt_err)?;
298    writeln!(out, "    std::vector<::dds::sub::Sample<T>> read();").map_err(fmt_err)?;
299    writeln!(
300        out,
301        "    ::dds::core::status::SubscriptionMatchedStatus subscription_matched_status();"
302    )
303    .map_err(fmt_err)?;
304    writeln!(
305        out,
306        "    ::dds::core::status::SampleLostStatus sample_lost_status();"
307    )
308    .map_err(fmt_err)?;
309    writeln!(
310        out,
311        "    ::dds::core::status::SampleRejectedStatus sample_rejected_status();"
312    )
313    .map_err(fmt_err)?;
314    writeln!(out, "}};").map_err(fmt_err)?;
315    writeln!(out).map_err(fmt_err)?;
316    writeln!(out, "}} }} // namespace dds::sub").map_err(fmt_err)?;
317    writeln!(out).map_err(fmt_err)?;
318    Ok(())
319}
320
321fn fmt_err(_: core::fmt::Error) -> CppGenError {
322    CppGenError::Internal("string formatting failed".into())
323}
324
325#[cfg(test)]
326mod tests {
327    #![allow(clippy::expect_used, clippy::panic)]
328    use super::*;
329
330    fn render() -> String {
331        let mut s = String::new();
332        emit_dcps_header(&mut s).expect("emit");
333        s
334    }
335
336    #[test]
337    fn entity_base_class_emitted_in_dds_core() {
338        let s = render();
339        assert!(s.contains("namespace dds { namespace core {"));
340        assert!(s.contains("class Entity {"));
341        assert!(s.contains("virtual ~Entity() = default;"));
342    }
343
344    #[test]
345    fn domain_participant_class_declaration_is_generated() {
346        let s = render();
347        assert!(s.contains("namespace dds { namespace domain {"));
348        assert!(s.contains("class DomainParticipant : public ::dds::core::Entity {"));
349        assert!(s.contains("explicit DomainParticipant(int32_t domain_id);"));
350        assert!(s.contains("int32_t domain_id() const;"));
351    }
352
353    #[test]
354    fn publisher_and_subscriber_emitted() {
355        let s = render();
356        assert!(s.contains("namespace dds { namespace pub {"));
357        assert!(s.contains("class Publisher : public ::dds::core::Entity {"));
358        assert!(s.contains("namespace dds { namespace sub {"));
359        assert!(s.contains("class Subscriber : public ::dds::core::Entity {"));
360    }
361
362    #[test]
363    fn topic_template_with_t_parameter() {
364        let s = render();
365        assert!(s.contains("namespace dds { namespace topic {"));
366        assert!(s.contains("template <typename T>"));
367        assert!(s.contains("class Topic : public ::dds::core::Entity {"));
368    }
369
370    #[test]
371    fn data_writer_and_data_reader_templates() {
372        let s = render();
373        assert!(s.contains("class DataWriter : public ::dds::core::Entity {"));
374        assert!(s.contains("class DataReader : public ::dds::core::Entity {"));
375        assert!(s.contains("void write(const T& sample);"));
376        assert!(s.contains("std::vector<::dds::sub::Sample<T>> take();"));
377        assert!(s.contains("std::vector<::dds::sub::Sample<T>> read();"));
378    }
379
380    #[test]
381    fn data_writer_has_status_accessor_for_publication_matched() {
382        let s = render();
383        assert!(s.contains(
384            "::dds::core::status::PublicationMatchedStatus publication_matched_status();"
385        ));
386    }
387
388    #[test]
389    fn dcps_class_names_count_seven() {
390        let names = dcps_class_names();
391        assert_eq!(names.len(), 7);
392        assert!(names.contains(&"dds::domain::DomainParticipant"));
393        assert!(names.contains(&"dds::topic::Topic"));
394    }
395}