C++ Tutorial¶
Building DDS C++ Hello World¶
To test your installation, the Hello World example can be used. The code of this application is detailed in the next chapter.
The DDS C++ Hello World example can be found in the
<cyclonedds-cxx-install-location>/share/CycloneDDS CXX/helloworld
directory for both Linux and Windows. This chapter describes the example
build process using CMake.
Building Cyclone DDS CXX applications with CMake¶
The CMake build file for the DDS CXX Hello World example is located
under the helloworld
directory (CMakeLists.txt
).
The content of the CMakeLists.txt
is as follows:
project(helloworld LANGUAGES C CXX)
cmake_minimum_required(VERSION 3.5)
if (NOT TARGET CycloneDDS CXX::ddscxx)
find_package(CycloneDDS CXX REQUIRED)
endif()
# Convenience function, provided by the idlc backend for CXX that generates a CMake
# target for the given IDL file. The function calls idlc to generate
# source files and compiles them into a library.
idlcxx_generate(TARGET ddscxxHelloWorldData_lib FILES HelloWorldData.idl WARNINGS no-implicit-extensibility)
add_executable(ddscxxHelloworldPublisher publisher.cpp)
add_executable(ddscxxHelloworldSubscriber subscriber.cpp)
# Link both executables to IDL data type library and ddscxx.
target_link_libraries(ddscxxHelloworldPublisher ddscxxHelloWorldData_lib CycloneDDS CXX::ddscxx)
target_link_libraries(ddscxxHelloworldSubscriber ddscxxHelloWorldData_lib CycloneDDS CXX::ddscxx)
set_property(TARGET ddscxxHelloworldPublisher PROPERTY CXX_STANDARD 11)
set_property(TARGET ddscxxHelloworldSubscriber PROPERTY CXX_STANDARD 11)
To build a Cyclone DDS CXX based application with CMake, you must link your application business code with:
|var-project-short| C++
libraries that contain the DDS CXX API your application needs.The wrapper classes and structures that represent your datatypes and the customized-DataWriter’s and readers that can handle these data types. The CMake statement generates these classes
idlcxx_generate()
that incepts the IDL file invokes the IDL compiler and packages the datatype wrapper classes in a library (e.g.ddscxxHelloWorldData_lib
).
This process is outlined as follows:

Setting the property for the applications in the CMake
set_property()
statement compiles the application against the
C++ 11
standard.
The application executable (ddscxxHellowordPublisher
) is built with
the CMake target_link_libraries()
statement which links the ddscxx
lib, the datatype wrapper classes lib (e.g ddscxxHelloWorldData_lib
)
and the application code lib.
CMake tries to find the CycloneDDS
and CycloneDDSCXX
CMake packages, the details regarding how to locate those packages are
described in the next section. When the packages are found, every path
and dependencies are automatically set.
Building the Hello World! Example¶
With our CMakeLists.txt
file in hand we can now start the build process.
It’s good practice to build examples or applications out-of-source by creating a build
directory inside
where you copied the Hello World! example. In the terminal that you opened inside the directory with
the Hello World! files run:
mkdir build
cd build
Now you can configure the build environment:
cmake -DCMAKE_PREFIX_PATH=<core-install-location>;<c++-install-location> ..
cmake -DCMAKE_PREFIX_PATH=<core-install-location>;<c++-install-location> ..
cmake -G <generator-name> -DCMAKE_PREFIX_PATH=<core-install-location>;<c++-install-location> ..
On Windows you can build with one of several generators. Usually if you omit the -G <generator-name>
it will pick a sensible default, but if it doesn’t work or picks something unexpected you can go to the
CMake generators documentation.
For example, “Visual Studio 15 2017 Win64” targets a 64-bit build using Visual Studio 2017.
Warning
If manually specifying generators be careful about mixing and matching! If you compiled Cyclone DDS with 32-bit libraries and you try to compile Hello World! as 64-bit or vice-versa it will not work.
Warning
Weird things can happen if you mix -DCMAKE_BUILD_TYPE=Release
, -DCMAKE_BUILD_TYPE=RelWithDebInfo
and -DCMAKE_BUILD_TYPE=Debug
,
try to match what you picked for Cyclone DDS in downstream projects.
CMake uses the CMakeLists.txt in the HelloWorld directory to create “makefiles” that target the native platform.
Our build directory is now prepared to build the actual executables (HelloworldPublisher
and HelloworldSubscriber
in this case):
cmake --build .
Your build directory should now contain your executables (on Windows they might be in a Release
or Debug
subdirectory). You can
execute them in the same way as described in the Test your installation section.
DDS C++ Hello World Code anatomy¶
The previous chapter described the installation process that built implicitly or explicitly the C++ Hello World! Example.
This chapter introduces the structural code of a simple system made by an application that publishes keyed messages and another one that subscribes and reads such data. Each message represents a data object that is uniquely identified with a key and a payload.
Keys steps to build the Hello World! application in C++¶
The Hello World! example has a very simple ‘data layer’ with a data
model made of one data type Msg
which represents keyed messages (c,f
next subsection).
To exchange data, applications’ business logic with Cyclone DDS must:
Declare its subscription and involvement into a DDS domain. A DDS domain is an administrative boundary that defines, scopes, and gathers all the DDS applications, data, and infrastructure that needs to interconnect and share the same data space. Each DDS domain has a unique identifier. Applications declare their participation within a DDS domain by creating a Domain Participant entity.
Create a Data topic with the data type described in the data model. The data types define the structure of the Topic. The Topic is, therefore, an association between the topic name and datatype. QoSs can be optionally added to this association. Thus, a topic categorizes the data into logical classes and streams.
Create at least a Publisher, a Subscriber, and Data Readers and Writers object specific to the topic created earlier. Applications may want to change the default QoSs at this stage. In the Hello world! example, the
ReliabilityQoS
is changed from its default value (Best-effort) to Reliable.Once the previous DDS computational objects are in place, the application logic can start writing or reading the data.
At the application level, readers and writers need not be aware of
each other. The reading application, now designated as application
Subscriber, polls the data reader periodically until a writing
application, designated as application Publisher,
provides the required data into the shared Topic, namely HelloWorldData_Msg
.
The data type is described using the OMG IDL.
Language <http://www.omg.org/gettingstarted/omg_idl.htm>`__ located in
HelloWorldData.idl
file. This IDL file is considered the Data Model
of our example.
This data model is preprocessed and compiled by Cyclone DDS C++
IDL-Compiler to generate a C++ representation of the data as described
in Chapter 6. These generated source and header files are used by the
HelloworldSubscriber.cpp
and HelloworldPublisher.cpp
application programs to share the Hello World! Message instance and
sample.
HelloWorld IDL¶
As explained earlier, the benefits of using IDL language to define
data is to have a data model that is independent of the programming
languages. The HelloWorld.idl
IDL file can
therefore be reused, it is compiled to be used within C++ DDS based
applications.
The HelloWorld data type is described in a language-independent way and stored in the HelloWorldData.idl file.
module HelloWorldData
{
struct Msg
{
@key long userID;
string message;
};
};
The data definition language used for DDS corresponds to a subset of
the OMG Interface Definition Language (IDL). In our simple example, the HelloWorld data
model is made of one module HelloWorldData
. A module can be seen
as a namespace where data with interrelated semantics are represented
together as a logical unit.
The struct Msg is the actual data structure that shapes the data used to build the Topics. As already mentioned, a topic is an association between a data type and a string name. The topic name is not defined in the IDL file but is instead defined by the application business logic at runtime.
In our case, the data type Msg
contains two fields: userID
and
message
payload. The userID
is used to uniquely identify each message
instance. This is done using the @key
annotation.
The Cyclone DDS C++ IDL compiler translates module names into namespaces and structure names into classes.
It also generates code for public accessor functions for all fields mentioned in the IDL struct, separate public constructors, and a destructor:
A default (empty) constructor that recursively invokes the constructors of all fields
A copy-constructor that performs a deep copy from the existing class
A move-constructor that moves all arguments to its members
The destructor recursively releases all fields. It also generates code for assignment operators that recursively construct all fields based on the parameter class (copy and move versions). The following code snippet is provided without warranty: the internal format may change, but the API delivered to your application code is stable.
namespace HelloWorldData
{
class Msg OSPL_DDS_FINAL
{
public:
int32_t userID_;
std::string message_;
public:
Msg() :
userID_(0) {}
explicit Msg(
int32_t userID,
const std::string& message) :
userID_(userID),
message_(message) {}
Msg(const Msg &_other) :
userID_(_other.userID_),
message_(_other.message_) {}
#ifdef OSPL_DDS_C++11
Msg(Msg &&_other) :
userID_(::std::move(_other.userID_)),
message_(::std::move(_other.message_)) {}
Msg& operator=(Msg &&_other)
{
if (this != &_other) {
userID_ = ::std::move(_other.userID_);
message_ = ::std::move(_other.message_);
}
return *this;
}
#endif
Msg& operator=(const Msg &_other)
{
if (this != &_other) {
userID_ = _other.userID_;
message_ = _other.message_;
}
return *this;
}
bool operator==(const Msg& _other) const
{
return userID_ == _other.userID_ &&
message_ == _other.message_;
}
bool operator!=(const Msg& _other) const
{
return !(*this == _other);
}
int32_t userID() const { return this->userID_; }
int32_t& userID() { return this->userID_; }
void userID(int32_t _val_) { this->userID_ = _val_; }
const std::string& message() const { return this->message_; }
std::string& message() { return this->message_; }
void message(const std::string& _val_) { this->message_ = _val_; }
#ifdef OSPL_DDS_C++11
void message(std::string&& _val_) { this->message_ = _val_; }
#endif
};
}
Note: When translated into a different programming language, the data has a different representation specific to the target language. For instance, as shown in chapter 3, in C, the Helloworld data type is represented by a C structure. This highlights the advantage of using neutral language like IDL to describe the data model. It can be translated into different languages that can be shared between various applications written in other programming languages.
The IDL compiler generated files¶
The IDL compiler is a bison-based parser written in pure C and should be
fast and portable. It loads dynamic libraries to support different output
languages, but this is seldom relevant to you as a user. You can use
CMake
recipes as described above or invoke directly:
idlc -l C++ HelloWorldData.idl
This results in the following new files that need to be compiled and their associated object file linked with the Hello World! publisher and subscriber application business logic:
HelloWorldData.hpp
HelloWorldData.cpp
When using CMake to build the application, this step is hidden and is done automatically. For building with CMake, refer to building the HelloWorld example.
HelloWorldData.hpp
and HelloWorldData.cpp
files contain the data
type of messages that are shared.
DDS C++ Hello World Business Logic¶
As well as from the HelloWorldData
data type files that the DDS C++
Hello World example used to send messages, the DDS C++ Hello World!
example also contains two application-level source files
(subscriber.cpp
and publisher.cpp
), containing the business
logic.
DDS C++ Hello World! Subscriber Source Code ^^^^^^^^^^^^^^^^^^^^^^^^^^^=^^^^^^^^^^^^^^^^^^^^^
The Subscriber.cpp
file mainly contains the statements to wait for a
Hello World message and reads it when it receives it.
Note
The read sematic keeps the data sample in the Data Reader cache. The Subscriber application implements the steps defined in Key Steps to build helloworld for C++.
1#include <cstdlib>
2#include <iostream>
3#include <chrono>
4#include <thread>
5
6/* Include the C++ DDS API. */
7#include "dds/dds.hpp"
8
9/* Include data type and specific traits to be used with the C++ DDS API. */
10#include "HelloWorldData.hpp"
11
12using namespace org::eclipse::cyclonedds;
13
14int main() {
15 try {
16 std::cout << "=== [Subscriber] Create reader." << std::endl;
17
18 /* First, a domain participant is needed.
19 * Create one on the default domain. */
20 dds::domain::DomainParticipant participant(domain::default_id());
21
22 /* To subscribe to something, a topic is needed. */
23 dds::topic::Topic<HelloWorldData::Msg> topic(participant, "ddsC++_helloworld_example");
24
25 /* A reader also needs a subscriber. */
26 dds::sub::Subscriber subscriber(participant);
27
28 /* Now, the reader can be created to subscribe to a HelloWorld message. */
29 dds::sub::DataReader<HelloWorldData::Msg> reader(subscriber, topic);
30
31 /* Poll until a message has been read.
32 * It isn't really recommended to do this kind wait in a polling loop.
33 * It's done here just to illustrate the easiest way to get data.
34 * Please take a look at Listeners and WaitSets for much better
35 * solutions, albeit somewhat more elaborate ones. */
36 std::cout << "=== [Subscriber] Wait for message." << std::endl;
37 bool poll = true;
38
39 while (poll) {
40 /* For this example, the reader will return a set of messages (aka
41 * Samples). There are other ways of getting samples from reader.
42 * See the various read() and take() functions that are present. */
43 dds::sub::LoanedSamples<HelloWorldData::Msg> samples;
44
45 /* Try taking samples from the reader. */
46 samples = reader.take();
47
48 /* Are samples read? */
49 if (samples.length() > 0) {
50 /* Use an iterator to run over the set of samples. */
51 dds::sub::LoanedSamples<HelloWorldData::Msg>::const_iterator sample_iter;
52 for (sample_iter = samples.begin();
53 sample_iter < samples.end();
54 ++sample_iter) {
55 /* Get the message and sample information. */
56 const HelloWorldData::Msg& msg = sample_iter->data();
57 const dds::sub::SampleInfo& info = sample_iter->info();
58
59 /* Sometimes a sample is read, only to indicate a data
60 * state change (which can be found in the info). If
61 * that's the case, only the key value of the sample
62 * is set. The other data parts are not.
63 * Check if this sample has valid data. */
64 if (info.valid()) {
65 std::cout << "=== [Subscriber] Message received:" << std::endl;
66 std::cout << " userID : " << msg.userID() << std::endl;
67 std::cout << " Message : \"" << msg.message() << "\"" << std::endl;
68
69 /* Only 1 message is expected in this example. */
70 poll = false;
71 }
72 }
73 } else {
74 std::this_thread::sleep_for(std::chrono::milliseconds(20));
75 }
76 }
77 }
78 catch (const dds::core::Exception& e) {
79 std::cerr << "=== [Subscriber] Exception: " << e.what() << std::endl;
80 return EXIT_FAILURE;
81 }
82
83 std::cout << "=== [Subscriber] Done." << std::endl;
84
85 return EXIT_SUCCESS;
86}
Within the subscriber code, we mainly use the DDS ISOCPP API and the
HelloWorldData::Msg
type. Therefore, the following header files must
be included:
The
dds.hpp
file give access to the DDS APIs,The
HelloWorldData.hpp
is specific to the data type defined in the IDL.
#include "dds/dds.hpp"
#include "HelloWorldData.hpp"
At least four DDS entities are needed, the domain participant, the topic, the subscriber, and the reader.
dds::domain::DomainParticipant participant(domain::default_id());
dds::topic::Topic<HelloWorldData::Msg> topic(participant,"ddsC++_helloworld_example");
dds::sub::Subscriber subscriber(participant);
dds::sub::DataReader<HelloWorldData::Msg> reader(subscriber,topic);
The Cyclone DDS C++ API simplifies and extends how data can be read or
taken. To handle the data some, LoanedSamples
is declared and
created which loan samples from the Service pool. Return of the loan is
implicit and managed by scoping:
dds::sub::LoanedSamples<HelloWorldData::Msg> samples;
dds::sub::LoanedSamples<HelloWorldData::Msg>::const_iterator sample_iter;
As the read( )/take()
operation may return more the one data sample
(if several publishing applications are started
simultaneously to write different message instances), an iterator is
used.
const::HelloWorldData::Msg& msg;
const dds::sub::SampleInfo& info;
In DDS, data and metadata are propagated together. The samples are a
set of data samples (i.e., user-defined data) and metadata describing the
sample state and validity, etc ,,, (info
). We can use iterators to
get the data and metadata from each sample.
try {
// ...
}
catch (const dds::core::Exception& e) {
std::cerr << "=== [Subscriber] Exception: " << e.what() << std::endl;
return EXIT_FAILURE;
}
It is good practice to surround every key verb of the DDS APIs with
try/catch
block to locate issues precisely when they occur. In this
example, one block is used to facilitate the programming model of the
applications and improve their source code readability.
dds::domain::DomainParticipant participant(domain::default_id());
The DDS participant is always attached to a specific DDS domain. In the
Hello World! example, it is part of the Default_Domain, the one
specified in the XML deployment file that you potentially be created
(i.e., the one pointing to $CYCLONEDDS_URI
), please refer to
testing your installation for further details.
Subsequently, create a subscriber attached to your participant.
dds::sub::Subscriber subscriber(participant);
The next step is to create the topic with a given
name(ddsC++_helloworld_example
)and the predefined data
type(HelloWorldData::Msg
). Topics with the same data type
description and with different names are considered different topics.
This means that readers or writers created for a given topic do not
interfere with readers or writers created with another topic, even if
they are the same data type.
dds::topic::Topic<HelloWorldData::Msg> topic(participant,"ddsC++_helloworld_example");
Once the topic is created, we can create and associate to it a data reader.
dds::sub::DataReader<HelloWorldData::Msg> reader(subscriber, topic);
To modify the Data Reader Default Reliability Qos to Reliable:
dds::sub::qos::DataReaderQos drqos = topic.qos() << dds::core::policy::Reliability::Reliable();
dds::sub::DataReader<HelloWorldData::Msg> dr(subscriber, topic, drqos);
To retrieve data in your application code from the data reader’s cache you can either use a pre-allocated buffer to store the data or loan it from the middleware.
If you use a pre-allocated buffer, you create an array/vector-like
like container. If you use the loaned buffer option, you need to be
aware that these buffers are actually ‘owned’ by the middleware,
precisely by the DataReader
. The Cyclone DDS C++ API implicitly
allows you to return the loans through scoping.
In our example, we use the loan samples mode (LoanedSamples
).
Samples
are an unbounded sequence of samples; the sequence
length depends on the amount of data available in the data reader’s
cache.
dds::sub::LoanedSamples<HelloWorldData::Msg> samples;
At this stage, we can attempt to read data by going into a polling loop
that regularly scrutinizes and examines the arrival of a message.
Samples are removed from the reader’s cache when taken with the
take()
.
samples = reader.take();
If you choose to read the samples with read()
, data remains in the
data reader cache. A length() of samples greater than zero indicates
that the data reader cache was not empty.
if (samples.length() > 0)
As sequences are NOT pre-allocated by the user, buffers are ‘loaned’ to
him by the DataReader
.
dds::sub::LoanedSamples<HelloWorldData::Msg>::const_iterator sample_iter;
for (sample_iter = samples.begin();
sample_iter < samples.end();
++sample_iter)
For each sample, cast and extract its user-defined data
(Msg
) and metadate (info
).
const HelloWorldData::Msg& msg = sample_iter->data();
const dds::sub::SampleInfo& info = sample_iter->info();
The SampleInfo (info
) tells us whether the data we are taking is
Valid or Invalid. Valid data means that it contains the payload
provided by the publishing application. Invalid data means that we are
reading the DDS state of the data Instance. The state of a data instance can
be DISPOSED
by the writer, or it is NOT_ALIVE
anymore, which
could happen when the publisher application terminates while the
subscriber is still active. In this case, the sample is not considered
Valid, and its sample info.valid()
field is False.
if (info.valid())
As the sample contains valid data, we can safely display its content.
std::cout << "=== [Subscriber] Message received:" << std::endl;
std::cout << " userID : " << msg.userID() << std::endl;
std::cout << " Message : \"" << msg.message() << "\"" << std::endl;
As we are using the Poll data reading mode, we repeat the above steps every 20 milliseconds.
else {
std::this_thread::sleep_for(std::chrono::milliseconds(20));
}
This example uses the polling mode to read or take data. Cyclone DDS offers waitSet and Listener mechanism to notify the application that data is available in their cache, which avoids polling the cache at a regular intervals. The discretion of these mechanisms is beyond the scope of this document.
All the entities that are created under the participant, such as the Data Reader Subscriber and Topic are automatically deleted by middleware through the scoping mechanism.
DDS C++ Hello World! Publisher Source Code¶
The Publisher.cpp
contains the source that writes a Hello World
message. From the DDS perspective, the publisher application code is
almost symmetrical to the subscriber one, except that you need to create
a Publisher and DataWriter
, respectively, instead of a Subscriber and
Data Reader. A synchronization statement is added to the main thread to
ensure data is only written when Cyclone DDS discovers at least a matching
reader. Synchronizing the main thread until a reader is discovered
assures we can start the publisher or subscriber program in any order.
1#include <cstdlib>
2#include <iostream>
3#include <chrono>
4#include <thread>
5
6/* Include the C++ DDS API. */
7#include "dds/dds.hpp"
8
9/* Include data type and specific traits to be used with the C++ DDS API. */
10#include "HelloWorldData.hpp"
11
12using namespace org::eclipse::cyclonedds;
13
14int main() {
15 try {
16 std::cout << "=== [Publisher] Create writer." << std::endl;
17
18 /* First, a domain participant is needed.
19 * Create one on the default domain. */
20 dds::domain::DomainParticipant participant(domain::default_id());
21
22 /* To publish something, a topic is needed. */
23 dds::topic::Topic<HelloWorldData::Msg> topic(participant, "ddsC++_helloworld_example");
24
25 /* A writer also needs a publisher. */
26 dds::pub::Publisher publisher(participant);
27
28 /* Now, the writer can be created to publish a HelloWorld message. */
29 dds::pub::DataWriter<HelloWorldData::Msg> writer(publisher, topic);
30
31 /* For this example, we'd like to have a subscriber read
32 * our message. This is not always necessary. Also, the way it is
33 * done here is to illustrate the easiest way to do so. However, it is *not*
34 * recommended to do a wait in a polling loop.
35 * Please take a look at Listeners and WaitSets for much better
36 * solutions, albeit somewhat more elaborate ones. */
37 std::cout << "=== [Publisher] Waiting for subscriber." << std::endl;
38 while (writer.publication_matched_status().current_count() == 0) {
39 std::this_thread::sleep_for(std::chrono::milliseconds(20));
40 }
41
42 /* Create a message to write. */
43 HelloWorldData::Msg msg(1, "Hello World");
44
45 /* Write the message. */
46 std::cout << "=== [Publisher] Write sample." << std::endl;
47 writer.write(msg);
48
49 /* With a normal configuration (see dds::pub::qos::DataWriterQos
50 * for various writer configurations), deleting a writer will
51 * dispose of all its related messages.
52 * Wait for the subscriber to have stopped to be sure it received the
53 * message. Again, not normally necessary and not recommended to do
54 * this in a polling loop. */
55 std::cout << "=== [Publisher] Waiting for sample to be accepted." << std::endl;
56 while (writer.publication_matched_status().current_count() > 0) {
57 std::this_thread::sleep_for(std::chrono::milliseconds(50));
58 }
59 }
60 catch (const dds::core::Exception& e) {
61 std::cerr << "=== [Publisher] Exception: " << e.what() << std::endl;
62 return EXIT_FAILURE;
63 }
64
65 std::cout << "=== [Publisher] Done." << std::endl;
66
67 return EXIT_SUCCESS;
68}
We are using the ISOCPP DDS API and the HelloWorldData to receive data. For that, we need to include the appropriate header files.
#include "dds/dds.hpp"
#include "HelloWorldData.hpp"
An exception handling mechanism try/catch
block is used.
try {
// …
}
catch (const dds::core::Exception& e) {
std::cerr << "=== [Subscriber] Exception: " << e.what() << std::endl;
return EXIT_FAILURE;
}
As with the reader in subscriber.cpp
, we need a participant, a
topic, and a publisher to create a writer. We must also
use the same topic name specified in the subscriber.cpp
.
dds::domain::DomainParticipant participant(domain::default_id());
dds::topic::Topic<HelloWorldData::Msg> topic(participant, "ddsC++_helloworld_example");
dds::pub::Publisher publisher(participant);
With these entities ready, the writer can now be created. The writer is
created for a specific topic “ddsC++_helloworld_example”
in the
default DDS domain.
dds::pub::DataWriter<HelloWorldData::Msg> writer(publisher, topic);
To modify the DataWriter
Default Reliability Qos to Reliable:
dds::pub::qos::DataReaderQos dwqos = topic.qos() << dds::core::policy::Reliability::Reliable();
dds::sub::DataWriter<HelloWorldData::Msg> dr(publisher, topic, dwqos);
When Cyclone DDS discovers readers and writers sharing the same data type and topic name, it connects them without the application’s involvement. A rendezvous pattern is required to write data only when a data reader appears. Either can implement such a pattern:
Wait for the publication/subscription matched events, where the Publisher waits and blocks the writing thread until the appropriate publication-matched event is raised, or
Regularly poll the publication matching status. This is the preferred option used in this example. The following line of code instructs Cyclone DDS to listen on the
writer.publication_matched_status()
dds::pub::DataWriter<HelloWorldData::Msg> writer(publisher, topic);
At regular intervals, we get the status change and for a matching publication. In between, the writing thread sleeps for 20 milliseconds.
while (writer.publication_matched_status().current_count() == 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(20));
}
After this loop, we are confident that a matching reader has been discovered. Now, we can commence the writing of the data instance. First, the data must be created and initialized.
HelloWorldData::Msg msg(1, "Hello World");
Send the data instance of the keyed message.
writer.write(msg);
After writing the data to the writer, the DDS C++ Hello World example checks if a matching subscriber(s) is still available. If matching subscribers exist, the example waits for 50ms and starts publishing the data again. If no matching subscriber is found, then the publisher program is ended.
return EXIT_SUCCESS;
Through scoping, all the entities such as topic, writer, etc. are deleted automatically.