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