I have 2 exiting c++ project where I have Sender and Receiver. They are connected with UDP socket connection and build through CmakeFile.txt
Root
|→ Sender
| |→ src
| |→ include
| |→ Cmakefile.txt
|→ Receiver
| |→ src
| |→ include
| |→ Cmakefile.txt
|→ Cmakefile.txt
Now I want to change UDP with DDS context idl
Root
|→ Sender
| |→ src
| | |→ Publisher.cpp
| |→ include
| |→ Cmakefile.txt
|→ Receiver
| |→ src
| | |→ Subscriber.cpp
| |→ include
| |→ Cmakefile.txt
|→ Cmakefile.txt
Root
|→ Sender
| |→ src
| |→ include
| |→ Cmakefile.txt
|→ Receiver
| |→ src
| |→ include
| |→ Cmakefile.txt
|→ DDSCom
| |→ src
| | |→ Publisher.cpp
| | |→ Subscriber.cpp
| |→ include
| |→ Cmakefile.txt
|→ Cmakefile.txt
what should be my structure?
The project structure you present seems reasonable. In general, I would recommend that the structure of your source code be informed more by the architecture and logical structure of the application, and not so much by the choice of communication mechanism.
However, there are some specific considerations when integrating DDS into a project. In particular, if you are using IDL (or XML) to define your DDS data types for the application, then it often makes sense to locate those files in a 'common' area. The DDS IDL compiler will generate code from those type definition files, and this generated code can be compiled into a library, or simply compiled into each application.
Also, if you are using a version control system (git, svn, etc), then I would recommend that the IDL file[s] be controlled, but not the generated code. [There are some arguments to control the generated code too, but I think that it almost always causes more harm than good.] So, specifically for your project, I would expect to find one or more IDL (or XML) files under DDSCom/src , DDSCom/include, or perhaps DDSCom/idl, as you prefer. A CMake rule can be created to generate type specific source code from the IDL as part of the build process. This guarantees that the generated code is kept up-to-date with the data types, and with upgrades of the DDS implementation.
This approach should apply regardless of the DDS implementation in use; for example, CoreDX DDS, OpenSplice, or RTI.
Regarding the internal code structure (not the 'directory' structure), there are many ways to architect an application that uses DDS for communication. The DDS API allows for synchronous and asynchronous communication patterns.
In general a data producer creates several DDS entities to facilitate the ability to write data (for example, a DDS::DomainParticipant, a DDS::Publisher, a DDS::Topic, and a DDS::DataWriter). The DataWriter entity supports the 'write' call, and the other entities provide various configuration points to affect the communication behavior and structure.
Similarly, a data consumer creates corresponding DDS entities that enable it to 'read' data (for example, a DDS::DomainParticipant, a DDS::Subscriber, a DDS::Topic, and a DDS::DataReader). The DataReader supports many different variants of the 'read' operation to provide access to available data.
Each of the DDS entities acts as a factory for other entities, and each can be configured with various Quality of Service (QoS) policy settings. These QoS settings give DDS a very rich set of configuration options that impact communications. The 'Topic' entity defines a logical grouping of data identified by name, and further specifies the "type" of the data contained within the collection.
In a small project, you may find it easier to create the DDS entities in place where needed in the application (for example, right in the main() routine). Alternatively, for larger systems, it is often beneficial to encapsulate the DDS entities within a component that can be reused among different applications.