Basic Publisher/Subscriber Example

This example demonstrates a simple publisher and subscriber using AstuteDDS.

Define Data Type (IDL)

// hello.idl
module HelloWorld {
    struct Message {
        @key long id;
        string text;
    };
};

Compile IDL

./build/tools/idl_compiler/astutedds-idl hello.idl

Publisher

#include "Message.hpp"
#include "Message_TypeSupport.hpp"
#include <astutedds/dcps/domain_participant.hpp>
#include <astutedds/dcps/publisher.hpp>

int main() {
    using namespace astutedds::dcps;

    // Create domain participant using the factory
    auto& factory = DomainParticipantFactory::instance();
    DomainParticipantQos pqos;  // Use default QoS
    auto* participant = factory.create_participant(0, pqos);

    if (!participant) {
        std::cerr << "Failed to create participant" << std::endl;
        return 1;
    }

    // Enable the participant to start discovery
    if (!participant->enable()) {
        std::cerr << "Failed to enable participant" << std::endl;
        return 1;
    }

    // Create topic with topic name and type name
    TopicQos tqos;  // Use default topic QoS
    auto* topic = participant->create_topic("HelloTopic", "HelloWorld::Message", tqos);

    if (!topic) {
        std::cerr << "Failed to create topic" << std::endl;
        return 1;
    }

    // Create publisher
    PublisherQos pubqos;  // Use default publisher QoS
    auto* publisher = participant->create_publisher(pubqos);

    if (!publisher) {
        std::cerr << "Failed to create publisher" << std::endl;
        return 1;
    }

    // Create writer
    DataWriterQos wqos;  // Use default writer QoS
    auto* writer = publisher->create_datawriter(topic, wqos);

    if (!writer) {
        std::cerr << "Failed to create writer" << std::endl;
        return 1;
    }

    // Write data (serialized as XCDR)
    HelloWorld::Message msg;
    msg.id = 1;
    msg.text = "Hello, DDS!";

    // Serialize the message to XCDR bytes
    std::vector<uint8_t> serialized;
    HelloWorld::MessageTypeSupport::serialize(msg, serialized);

    if (writer->write(serialized)) {
        std::cout << "Published: " << msg.text << std::endl;
    } else {
        std::cerr << "Failed to publish message" << std::endl;
    }

    // Cleanup
    publisher->delete_datawriter(writer);
    participant->delete_publisher(publisher);
    participant->delete_topic(topic);
    factory.delete_participant(participant);

    return 0;
}

Subscriber

#include "Message.hpp"
#include "Message_TypeSupport.hpp"
#include <astutedds/dcps/domain_participant.hpp>
#include <astutedds/dcps/subscriber.hpp>
#include <iostream>
#include <thread>
#include <chrono>

int main() {
    using namespace astutedds::dcps;

    // Create domain participant using the factory
    auto& factory = DomainParticipantFactory::instance();
    DomainParticipantQos pqos;  // Use default QoS
    auto* participant = factory.create_participant(0, pqos);

    if (!participant) {
        std::cerr << "Failed to create participant" << std::endl;
        return 1;
    }

    // Enable the participant to start discovery
    if (!participant->enable()) {
        std::cerr << "Failed to enable participant" << std::endl;
        return 1;
    }

    // Create topic with topic name and type name
    TopicQos tqos;  // Use default topic QoS
    auto* topic = participant->create_topic("HelloTopic", "HelloWorld::Message", tqos);

    if (!topic) {
        std::cerr << "Failed to create topic" << std::endl;
        return 1;
    }

    // Create subscriber
    SubscriberQos subqos;  // Use default subscriber QoS
    auto* subscriber = participant->create_subscriber(subqos);

    if (!subscriber) {
        std::cerr << "Failed to create subscriber" << std::endl;
        return 1;
    }

    // Create reader
    DataReaderQos rqos;  // Use default reader QoS
    auto* reader = subscriber->create_datareader(topic, rqos);

    if (!reader) {
        std::cerr << "Failed to create reader" << std::endl;
        return 1;
    }

    // Read data
    SampleInfo info;
    std::vector<uint8_t> serialized;

    while (true) {
        if (reader->read_next_sample(serialized, info) == ReturnCode_t::RETCODE_OK && info.valid_data) {
            // Deserialize the message from XCDR bytes
            HelloWorld::Message msg;
            HelloWorld::MessageTypeSupport::deserialize(msg, serialized);
            std::cout << "Received: " << msg.text << std::endl;
        }

        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }

    // Cleanup (unreachable, but good practice)
    subscriber->delete_datareader(reader);
    participant->delete_subscriber(subscriber);
    participant->delete_topic(topic);
    factory.delete_participant(participant);

    return 0;
}

Build and Run

# Compile publisher
g++ -std=c++20 publisher.cpp -o publisher \
    -I. \
    -I/usr/local/include/astutedds \
    -L/usr/local/lib \
    -lastutedds

# Compile subscriber  
g++ -std=c++20 subscriber.cpp -o subscriber \
    -I. \
    -I/usr/local/include/astutedds \
    -L/usr/local/lib \
    -lastutedds

# Ensure library path is set
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH

# Run in separate terminals
./publisher
./subscriber

Using CMake

Alternatively, use CMake for a more portable build:

# CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
project(HelloWorld)

set(CMAKE_CXX_STANDARD 20)

find_package(AstuteDDS REQUIRED)

add_executable(publisher publisher.cpp)
target_link_libraries(publisher PRIVATE AstuteDDS::astutedds)

add_executable(subscriber subscriber.cpp)
target_link_libraries(subscriber PRIVATE AstuteDDS::astutedds)

Build:

mkdir build && cd build
cmake ..
cmake --build .

./publisher
./subscriber