Aller au contenu principal

C API Reference

HDDS provides C bindings through hdds-c, offering a stable ABI for C applications and FFI integration. The API operates on raw CDR-encoded bytes; use hdds_gen to generate typed serialization code.

Version 1.0.0

This documents the current v1.0.0 API with 265+ FFI functions. The API is FFI-safe and ABI-stable.

Installation

# Build from source (default features: xtypes, qos-loaders)
git clone https://git.hdds.io/hdds/hdds.git
cd hdds
cargo build --release -p hdds-c

# With optional features
cargo build --release -p hdds-c --features "security,rpc"

# Install library
sudo cp target/release/libhdds_c.so /usr/local/lib/
sudo cp crates/hdds-c/hdds.h /usr/local/include/
sudo ldconfig

Optional features:

  • security -- DDS Security v1.1 (certificates, encryption, audit)
  • rpc -- DDS-RPC request/reply (requires tokio runtime)
#include "hdds.h"

Error Codes

Error codes are organized by category for easier debugging:

typedef enum HddsError {
// Generic (0-9)
HDDS_OK = 0, // Success
HDDS_ERROR = 1, // Generic error
HDDS_INVALID_ARGUMENT = 2, // Invalid argument passed
HDDS_NOT_FOUND = 3, // Resource not found
HDDS_OUT_OF_MEMORY = 4, // Memory allocation failed
HDDS_TIMEOUT = 5, // Operation timed out
HDDS_WOULD_BLOCK = 6, // Non-blocking operation would block
HDDS_ALREADY_EXISTS = 7, // Resource already exists
HDDS_NOT_INITIALIZED = 8, // Not initialized
HDDS_SHUTDOWN = 9, // System shutting down

// Configuration (10-19)
HDDS_INVALID_QOS = 10, // Invalid QoS configuration
HDDS_QOS_MISMATCH = 11, // QoS incompatibility
HDDS_INVALID_DOMAIN = 12, // Invalid domain ID
HDDS_INVALID_TOPIC = 13, // Invalid topic name
HDDS_CONFIG_ERROR = 14, // Configuration file error

// I/O (20-29)
HDDS_NETWORK_ERROR = 20, // Network communication error
HDDS_BIND_FAILED = 21, // Socket bind failed
HDDS_SEND_FAILED = 22, // Send operation failed
HDDS_RECEIVE_FAILED = 23, // Receive operation failed
HDDS_SERIALIZATION_ERROR = 24, // Serialization/deserialization failed

// Type (30-39)
HDDS_TYPE_MISMATCH = 30, // Type mismatch between endpoints
HDDS_TYPE_NOT_REGISTERED = 31, // Type not registered
HDDS_INVALID_DATA = 32, // Invalid data format

// QoS (40-49)
HDDS_DEADLINE_MISSED = 40, // Deadline QoS missed
HDDS_LIVELINESS_LOST = 41, // Liveliness QoS lost
HDDS_SAMPLE_REJECTED = 42, // Sample rejected (resource limits)
HDDS_SAMPLE_LOST = 43, // Sample lost (reliability)
HDDS_INCONSISTENT_POLICY = 44, // Inconsistent QoS policies

// Security (50-59)
HDDS_PERMISSION_DENIED = 50, // Access control denied
HDDS_AUTHENTICATION_FAILED = 51, // Authentication failed
HDDS_ENCRYPTION_ERROR = 52, // Encryption/decryption failed
HDDS_CERTIFICATE_ERROR = 53, // Certificate validation failed
HDDS_SECURITY_NOT_ENABLED = 54, // Security not enabled
} HddsError;

Error Handling Example

enum HddsError result = hdds_writer_write(writer, data, len);

switch (result) {
case HDDS_OK:
// Success
break;
case HDDS_PERMISSION_DENIED:
fprintf(stderr, "Access denied by security policy\n");
break;
case HDDS_WOULD_BLOCK:
fprintf(stderr, "Buffer full, try again later\n");
break;
case HDDS_NETWORK_ERROR:
fprintf(stderr, "Network communication error\n");
break;
default:
fprintf(stderr, "Error: %d\n", result);
break;
}

Transport Modes

typedef enum HddsTransportMode {
INTRA_PROCESS = 0, // No network, same-process only (fastest)
UDP_MULTICAST = 1, // Network via UDP multicast (DDS interop)
} HddsTransportMode;

Participant

The entry point to HDDS. All writers and readers are created from a participant.

Creation

// Create with default transport (UDP multicast)
struct HddsParticipant *participant = hdds_participant_create("my_app");

// Create with specific transport
struct HddsParticipant *participant =
hdds_participant_create_with_transport("my_app", UDP_MULTICAST);

// Intra-process only (no network)
struct HddsParticipant *participant =
hdds_participant_create_with_transport("my_app", INTRA_PROCESS);

Properties

// Get participant name
const char *name = hdds_participant_name(participant);

// Get domain ID (default: 0)
uint32_t domain_id = hdds_participant_domain_id(participant);

// Get participant ID (unique within domain)
uint8_t pid = hdds_participant_id(participant);

Cleanup

hdds_participant_destroy(participant);

DataWriter

Writers publish data to a topic. Data is raw bytes (use hdds_gen for typed serialization).

Creation

// Create with default QoS
struct HddsDataWriter *writer = hdds_writer_create(participant, "my/topic");

// Create with custom QoS
struct HddsQoS *qos = hdds_qos_reliable();
hdds_qos_set_transient_local(qos);
hdds_qos_set_history_depth(qos, 10);

struct HddsDataWriter *writer =
hdds_writer_create_with_qos(participant, "my/topic", qos);

hdds_qos_destroy(qos); // QoS can be freed after writer creation

Writing Data

// Write raw bytes
const char *message = "Hello, DDS!";
enum HddsError result = hdds_writer_write(writer, message, strlen(message));

if (result != OK) {
fprintf(stderr, "Write failed: %d\n", result);
}

Properties

// Get topic name
char topic_buf[256];
size_t topic_len;
hdds_writer_topic_name(writer, topic_buf, sizeof(topic_buf), &topic_len);

Cleanup

hdds_writer_destroy(writer);

DataReader

Readers receive data from a topic.

Creation

// Create with default QoS
struct HddsDataReader *reader = hdds_reader_create(participant, "my/topic");

// Create with custom QoS
struct HddsQoS *qos = hdds_qos_reliable();
hdds_qos_set_history_depth(qos, 100);

struct HddsDataReader *reader =
hdds_reader_create_with_qos(participant, "my/topic", qos);

hdds_qos_destroy(qos);

Taking Data

// Take single sample (non-blocking)
char buffer[1024];
size_t len_read;

enum HddsError result = hdds_reader_take(reader, buffer, sizeof(buffer), &len_read);

if (result == OK) {
// Process data (len_read bytes in buffer)
buffer[len_read] = '\0'; // If treating as string
printf("Received: %s\n", buffer);
} else if (result == NOT_FOUND) {
// No data available
}

Properties

// Get topic name
char topic_buf[256];
size_t topic_len;
hdds_reader_topic_name(reader, topic_buf, sizeof(topic_buf), &topic_len);

Cleanup

hdds_reader_destroy(reader);

QoS Configuration

QoS profiles control delivery semantics.

Creation

// Predefined profiles
struct HddsQoS *qos = hdds_qos_default(); // Best effort, volatile
struct HddsQoS *qos = hdds_qos_best_effort(); // Explicit best effort
struct HddsQoS *qos = hdds_qos_reliable(); // Reliable delivery
struct HddsQoS *qos = hdds_qos_rti_defaults(); // RTI Connext compatibility

// Load from FastDDS XML profile
struct HddsQoS *qos = hdds_qos_from_xml("/path/to/profile.xml");
struct HddsQoS *qos = hdds_qos_load_fastdds_xml("/path/to/fastdds.xml");

// Clone existing QoS
struct HddsQoS *qos2 = hdds_qos_clone(qos);

Reliability

hdds_qos_set_reliable(qos);     // Guaranteed delivery
hdds_qos_set_best_effort(qos); // Fire and forget

bool reliable = hdds_qos_is_reliable(qos);

Durability

hdds_qos_set_volatile(qos);        // No persistence
hdds_qos_set_transient_local(qos); // Persist for late joiners
hdds_qos_set_persistent(qos); // Disk persistence

bool transient = hdds_qos_is_transient_local(qos);

History

hdds_qos_set_history_depth(qos, 10);  // KEEP_LAST with depth
hdds_qos_set_history_keep_all(qos); // KEEP_ALL (unbounded)

uint32_t depth = hdds_qos_get_history_depth(qos);

Deadline & Lifespan

// Deadline: expected update frequency
hdds_qos_set_deadline_ns(qos, 100 * 1000 * 1000); // 100ms

// Lifespan: how long data remains valid
hdds_qos_set_lifespan_ns(qos, 5 * 1000 * 1000 * 1000); // 5 seconds

uint64_t deadline = hdds_qos_get_deadline_ns(qos);
uint64_t lifespan = hdds_qos_get_lifespan_ns(qos);

Liveliness

// Automatic liveliness
hdds_qos_set_liveliness_automatic_ns(qos, 1000000000); // 1 second lease

// Manual by participant
hdds_qos_set_liveliness_manual_participant_ns(qos, 500000000);

// Manual by topic
hdds_qos_set_liveliness_manual_topic_ns(qos, 250000000);

enum HddsLivelinessKind kind = hdds_qos_get_liveliness_kind(qos);
uint64_t lease = hdds_qos_get_liveliness_lease_ns(qos);

Ownership

hdds_qos_set_ownership_shared(qos);             // Multiple writers
hdds_qos_set_ownership_exclusive(qos, 100); // Single writer, strength=100

bool exclusive = hdds_qos_is_ownership_exclusive(qos);
int32_t strength = hdds_qos_get_ownership_strength(qos);

Partitions

hdds_qos_add_partition(qos, "sensor_data");
hdds_qos_add_partition(qos, "control");

Other Policies

// Time-based filter (throttle high-frequency data)
hdds_qos_set_time_based_filter_ns(qos, 10000000); // Min 10ms between samples

// Latency budget
hdds_qos_set_latency_budget_ns(qos, 50000000); // 50ms budget

// Transport priority
hdds_qos_set_transport_priority(qos, 10);

// Resource limits
hdds_qos_set_resource_limits(qos,
1000, // max_samples
100, // max_instances
10); // max_samples_per_instance

// Getters
uint64_t filter = hdds_qos_get_time_based_filter_ns(qos);
uint64_t budget = hdds_qos_get_latency_budget_ns(qos);
int32_t priority = hdds_qos_get_transport_priority(qos);
size_t max_samples = hdds_qos_get_max_samples(qos);
size_t max_instances = hdds_qos_get_max_instances(qos);
size_t max_per_instance = hdds_qos_get_max_samples_per_instance(qos);

Cleanup

hdds_qos_destroy(qos);

WaitSet

Event-driven waiting for data availability.

Creation

struct HddsWaitSet *waitset = hdds_waitset_create();

Status Conditions

// Get reader's status condition
const struct HddsStatusCondition *condition =
hdds_reader_get_status_condition(reader);

// Attach to waitset
hdds_waitset_attach_status_condition(waitset, condition);

Guard Conditions

// Create guard condition (for custom signaling)
const struct HddsGuardCondition *guard = hdds_guard_condition_create();

// Attach to waitset
hdds_waitset_attach_guard_condition(waitset, guard);

// Trigger guard condition
hdds_guard_condition_set_trigger(guard, true);

Waiting

const void *triggered[10];
size_t triggered_count;
int64_t timeout_ns = 5LL * 1000 * 1000 * 1000; // 5 seconds

enum HddsError result = hdds_waitset_wait(
waitset,
timeout_ns,
triggered,
10, // max conditions to return
&triggered_count
);

if (result == OK && triggered_count > 0) {
// Process triggered conditions
for (size_t i = 0; i < triggered_count; i++) {
if (triggered[i] == condition) {
// Reader has data
while (hdds_reader_take(reader, buffer, sizeof(buffer), &len) == OK) {
// Process data
}
}
}
}

Cleanup

hdds_waitset_detach_condition(waitset, condition);
hdds_status_condition_release(condition);
hdds_guard_condition_release(guard);
hdds_waitset_destroy(waitset);

Publisher & Subscriber (Grouping)

Publishers and subscribers group writers/readers with shared QoS policies.

Publisher

// Create publisher with default QoS
struct HddsPublisher *publisher = hdds_publisher_create(participant);

// Create with custom QoS
struct HddsPublisher *publisher =
hdds_publisher_create_with_qos(participant, qos);

// Create writer from publisher (inherits publisher QoS)
struct HddsDataWriter *writer =
hdds_publisher_create_writer(publisher, "my/topic");

// Create writer with custom QoS (overrides publisher defaults)
struct HddsQoS *writer_qos = hdds_qos_reliable();
struct HddsDataWriter *writer =
hdds_publisher_create_writer_with_qos(publisher, "my/topic", writer_qos);
hdds_qos_destroy(writer_qos);

// Cleanup
hdds_writer_destroy(writer);
hdds_publisher_destroy(publisher);

Subscriber

// Create subscriber with default QoS
struct HddsSubscriber *subscriber = hdds_subscriber_create(participant);

// Create with custom QoS
struct HddsSubscriber *subscriber =
hdds_subscriber_create_with_qos(participant, qos);

// Create reader from subscriber (inherits subscriber QoS)
struct HddsDataReader *reader =
hdds_subscriber_create_reader(subscriber, "my/topic");

// Create reader with custom QoS (overrides subscriber defaults)
struct HddsQoS *reader_qos = hdds_qos_reliable();
struct HddsDataReader *reader =
hdds_subscriber_create_reader_with_qos(subscriber, "my/topic", reader_qos);
hdds_qos_destroy(reader_qos);

// Cleanup
hdds_reader_destroy(reader);
hdds_subscriber_destroy(subscriber);

Transport Configuration

For advanced participant setup (TCP, TLS, SHM, custom ports), use the builder-style configuration API instead of hdds_participant_create.

Builder Lifecycle

// Create config builder
HddsParticipantConfig *cfg = hdds_config_create("my_app");

// Configure settings
hdds_config_set_domain_id(cfg, 42);
hdds_config_set_participant_id(cfg, 1);

// Build participant (consumes the config)
HddsParticipant *participant = hdds_config_build(cfg);
// cfg is consumed -- do NOT call hdds_config_destroy after build

If you don't call hdds_config_build, free with hdds_config_destroy(cfg).

TCP Transport

HddsParticipantConfig *cfg = hdds_config_create("tcp_app");
hdds_config_set_domain_id(cfg, 0);

// Enable TCP with a listen port (enables UDP discovery + TCP data by default)
hdds_config_enable_tcp(cfg, 7410);

// Set TCP role
hdds_config_set_tcp_role(cfg, HDDS_TCP_ROLE_AUTO); // Auto (default)
hdds_config_set_tcp_role(cfg, HDDS_TCP_ROLE_SERVER_ONLY); // Listen only
hdds_config_set_tcp_role(cfg, HDDS_TCP_ROLE_CLIENT_ONLY); // Connect only

// Add initial peers (for TCP-only or client mode)
hdds_config_add_tcp_peer(cfg, "192.168.1.100:7410");

// Disable Nagle's algorithm
hdds_config_set_tcp_nodelay(cfg, true);

HddsParticipant *p = hdds_config_build(cfg);

TLS

HddsParticipantConfig *cfg = hdds_config_create("tls_app");
hdds_config_enable_tcp(cfg, 7410);
hdds_config_enable_tls(cfg); // Enables TLS on TCP transport
HddsParticipant *p = hdds_config_build(cfg);

Shared Memory

HddsParticipantConfig *cfg = hdds_config_create("shm_app");

// SHM policy
hdds_config_set_shm_policy(cfg, HDDS_SHM_PREFER); // Prefer SHM, fallback UDP (default)
hdds_config_set_shm_policy(cfg, HDDS_SHM_REQUIRE); // Require SHM, fail otherwise
hdds_config_set_shm_policy(cfg, HDDS_SHM_DISABLE); // Always use network

HddsParticipant *p = hdds_config_build(cfg);

Transport Preference

hdds_config_set_transport_preference(cfg, HDDS_TRANSPORT_PREF_UDP_ONLY);                // UDP only (default)
hdds_config_set_transport_preference(cfg, HDDS_TRANSPORT_PREF_TCP_ONLY); // TCP only
hdds_config_set_transport_preference(cfg, HDDS_TRANSPORT_PREF_UDP_DISCOVERY_TCP_DATA); // Hybrid
hdds_config_set_transport_preference(cfg, HDDS_TRANSPORT_PREF_SHM_PREFERRED); // SHM + UDP
hdds_config_set_transport_preference(cfg, HDDS_TRANSPORT_PREF_SHM_LOCAL_TCP_REMOTE); // SHM local, TCP remote

Discovery Ports

// Override default RTPS port formula
hdds_config_set_discovery_ports(cfg,
9400, // SPDP multicast port
9410, // SEDP unicast port
9411); // User data unicast port

// Add static unicast peers (e.g. for embedded devices without multicast)
hdds_config_add_static_peer(cfg, "192.168.1.50:7411");

Security Configuration

remarque

Requires the security feature: cargo build -p hdds-c --features security

Security configuration is created separately and attached to a participant config before building.

Setup

// Create security config
HddsSecurityConfig *sec = hdds_security_config_create();

// Set certificate paths (required)
hdds_security_config_set_identity_cert(sec, "/certs/participant1.pem");
hdds_security_config_set_private_key(sec, "/certs/participant1_key.pem");
hdds_security_config_set_ca_cert(sec, "/certs/ca.pem");

// Optional: governance and permissions
hdds_security_config_set_governance_xml(sec, "/certs/governance.xml");
hdds_security_config_set_permissions_xml(sec, "/certs/permissions.xml");

// Enable AES-256-GCM encryption (default: false)
hdds_security_config_enable_encryption(sec, true);

// Enable audit logging
hdds_security_config_enable_audit_log(sec, true);
hdds_security_config_set_audit_log_path(sec, "/var/log/hdds_audit.log");

// Toggle authentication requirement (default: true)
hdds_security_config_require_authentication(sec, true);

// Enable CRL/OCSP revocation checking (default: false)
hdds_security_config_check_revocation(sec, true);

// Attach to participant config (consumes sec -- do NOT destroy it)
HddsParticipantConfig *cfg = hdds_config_create("secure_app");
hdds_config_set_security(cfg, sec);
HddsParticipant *p = hdds_config_build(cfg);

If you don't attach the security config, free it with hdds_security_config_destroy(sec).

Listeners

Callback-based event notification for DataReaders and DataWriters.

Reader Listener

// Callback: data available
void on_data(const uint8_t *data, size_t len, void *user_data) {
printf("Received %zu bytes\n", len);
}

// Callback: subscription matched
void on_match(const HddsSubscriptionMatchedStatus *status, void *user_data) {
printf("Matched writers: %u (change: %d)\n",
status->current_count, status->current_count_change);
}

// Install full listener
HddsReaderListener listener = {0};
listener.on_data_available = on_data;
listener.on_subscription_matched = on_match;
listener.user_data = my_context;
hdds_reader_set_listener(reader, &listener);

// Or set individual callbacks
hdds_reader_set_on_data_available(reader, on_data, my_context);
hdds_reader_set_on_subscription_matched(reader, on_match, my_context);

// Remove listener
hdds_reader_clear_listener(reader);

Reader listener callbacks:

CallbackEvent
on_data_availableNew data ready to read
on_subscription_matchedWriter matched/unmatched
on_liveliness_changedMatched writer liveliness changed
on_sample_lostSamples lost (sequence gap)
on_sample_rejectedSamples rejected (resource limits)
on_deadline_missedRequested deadline missed
on_incompatible_qosQoS incompatible with matched writer

Writer Listener

void on_pub_match(const HddsPublicationMatchedStatus *status, void *user_data) {
printf("Matched readers: %u\n", status->current_count);
}

HddsWriterListener listener = {0};
listener.on_publication_matched = on_pub_match;
listener.user_data = my_context;
hdds_writer_set_listener(writer, &listener);

// Or set individual callback
hdds_writer_set_on_publication_matched(writer, on_pub_match, my_context);

// Remove listener
hdds_writer_clear_listener(writer);

Writer listener callbacks:

CallbackEvent
on_sample_writtenSample successfully written
on_publication_matchedReader matched/unmatched
on_offered_deadline_missedOffered deadline missed
on_offered_incompatible_qosQoS incompatible with matched reader
on_liveliness_lostLiveliness lost (MANUAL_BY_* only)
Thread Safety

Callbacks are invoked from the DDS receive/write threads. They must be thread-safe and should not block.

Content Filters

SQL-like filter expressions for topic data filtering.

Standalone Filter (Field Map Evaluation)

// Create filter
HddsFilter *f = hdds_filter_create("temperature > %0 AND humidity < %1");

// Set parameters
const char *params[] = {"25.0", "80"};
hdds_filter_set_parameters(f, params, 2);

// Create field map with sample values
HddsFieldMap *m = hdds_field_map_create();
hdds_field_map_set_f64(m, "temperature", 30.0);
hdds_field_map_set_f64(m, "humidity", 60.0);

// Evaluate: 1 = match, 0 = no match, -1 = error
int result = hdds_filter_evaluate(f, m); // returns 1 (match)

hdds_field_map_clear(m); // reuse map
hdds_field_map_destroy(m);
hdds_filter_destroy(f);

Field Map Value Types

HddsFieldMap *m = hdds_field_map_create();
hdds_field_map_set_i64(m, "count", 42);
hdds_field_map_set_u64(m, "id", 12345);
hdds_field_map_set_f64(m, "temperature", 23.5);
hdds_field_map_set_bool(m, "active", true);
hdds_field_map_set_string(m, "name", "sensor_a");
hdds_field_map_destroy(m);

Filter with Parameters

// Create with initial parameters
const char *params[] = {"42"};
HddsFilter *f = hdds_filter_create_with_params("value > %0", params, 1);

// Update parameters at runtime
const char *new_params[] = {"100"};
hdds_filter_set_parameters(f, new_params, 1);

// Get expression string
char buf[256];
size_t len = hdds_filter_get_expression(f, buf, sizeof(buf));

hdds_filter_destroy(f);

Supported operators: >, <, >=, <=, =, !=, <>, LIKE, AND, OR, NOT.

Conditions

ReadCondition

Filter DataReader samples by state masks (sample, view, instance state).

// State mask constants
#define SAMPLE_STATE_ANY 0x03
#define VIEW_STATE_ANY 0x03
#define INSTANCE_STATE_ANY 0x07

HddsReadCondition *cond = hdds_create_read_condition(
reader,
SAMPLE_STATE_ANY,
VIEW_STATE_ANY,
INSTANCE_STATE_ANY);

// Check trigger value
bool triggered = hdds_read_condition_get_trigger(cond);

// Get state masks
uint32_t sample_mask = hdds_read_condition_get_sample_state_mask(cond);
uint32_t view_mask = hdds_read_condition_get_view_state_mask(cond);
uint32_t instance_mask = hdds_read_condition_get_instance_state_mask(cond);

// Attach to WaitSet
hdds_waitset_attach_read_condition(waitset, cond);

hdds_read_condition_delete(cond);

QueryCondition

Extends ReadCondition with SQL-like content filtering.

const char *params[] = {"25.0"};
HddsQueryCondition *qcond = hdds_create_query_condition(
reader,
0x03, 0x03, 0x07, // state masks
"temperature > %0", // query expression
params, 1); // parameters

// Get expression
char buf[256];
hdds_query_condition_get_expression(qcond, buf, sizeof(buf));

// Update parameters at runtime
const char *new_params[] = {"30.0"};
hdds_query_condition_set_parameters(qcond, new_params, 1);

// Attach to WaitSet
hdds_waitset_attach_query_condition(waitset, qcond);

hdds_query_condition_delete(qcond);

ContentFilteredTopic

Create filtered views on topics with SQL-like expressions.

const char *params[] = {"25.0", "80"};
HddsContentFilteredTopic *cft = hdds_create_content_filtered_topic(
participant,
"filtered_sensor", // name
"sensor/data", // related topic
"temperature > %0 AND humidity < %1", // filter expression
params, 2); // parameters

// Create reader from filtered topic
HddsDataReader *filtered_reader = hdds_create_reader_filtered(
participant, cft, NULL /* default QoS */);

// Update filter parameters at runtime
const char *new_params[] = {"30.0", "90"};
hdds_content_filtered_topic_set_params(cft, new_params, 2);

// Get expression
char buf[256];
hdds_content_filtered_topic_get_expression(cft, buf, sizeof(buf));

// Cleanup
hdds_reader_destroy(filtered_reader);
hdds_content_filtered_topic_delete(cft);

Dynamic Types

Runtime type construction and data manipulation without compile-time type knowledge. Useful for generic tools, bridges, and introspection.

Build a Type

HddsTypeBuilder *tb = hdds_type_builder_new("SensorReading");
hdds_type_builder_add_field(tb, "sensor_id", HDDS_PRIM_U32);
hdds_type_builder_add_field(tb, "temperature", HDDS_PRIM_F64);
hdds_type_builder_add_field(tb, "active", HDDS_PRIM_BOOL);
hdds_type_builder_add_string_field(tb, "label");

// Sequence and array fields
hdds_type_builder_add_sequence_field(tb, "readings", HDDS_PRIM_F32);
hdds_type_builder_add_array_field(tb, "coordinates", HDDS_PRIM_F64, 3);

// Build (consumes the builder)
HddsTypeDescriptor *desc = hdds_type_builder_build(tb);
// tb is consumed -- do NOT call hdds_type_builder_destroy after build

// Inspect
size_t field_count = hdds_type_descriptor_get_field_count(desc);
char name_buf[64];
hdds_type_descriptor_get_name(desc, name_buf, sizeof(name_buf));

If you don't call hdds_type_builder_build, free with hdds_type_builder_destroy(tb).

Primitive Types

typedef enum HddsPrimitiveKind {
HDDS_PRIM_BOOL = 0,
HDDS_PRIM_U8 = 1,
HDDS_PRIM_U16 = 2,
HDDS_PRIM_U32 = 3,
HDDS_PRIM_U64 = 4,
HDDS_PRIM_I8 = 5,
HDDS_PRIM_I16 = 6,
HDDS_PRIM_I32 = 7,
HDDS_PRIM_I64 = 8,
HDDS_PRIM_F32 = 9,
HDDS_PRIM_F64 = 10,
HDDS_PRIM_STRING = 11,
} HddsPrimitiveKind;

Create and Populate Data

HddsDynamicData *data = hdds_dynamic_data_new(desc);

// Setters (one per primitive type)
hdds_dynamic_data_set_u32(data, "sensor_id", 42);
hdds_dynamic_data_set_f64(data, "temperature", 23.5);
hdds_dynamic_data_set_bool(data, "active", true);
hdds_dynamic_data_set_string(data, "label", "living_room");

// Getters
uint32_t id;
hdds_dynamic_data_get_u32(data, "sensor_id", &id);

double temp;
hdds_dynamic_data_get_f64(data, "temperature", &temp);

bool active;
hdds_dynamic_data_get_bool(data, "active", &active);

char label[64];
size_t label_len = hdds_dynamic_data_get_string(data, "label", label, sizeof(label));

All setter/getter variants: bool, u8, u16, u32, u64, i8, i16, i32, i64, f32, f64, string.

CDR Encode / Decode

// Encode to CDR2
uint8_t buf[1024];
size_t encoded_len;
hdds_dynamic_data_encode(data, buf, sizeof(buf), &encoded_len);

// Write to DDS
hdds_writer_write(writer, buf, encoded_len);

// Read from DDS and decode
uint8_t read_buf[1024];
size_t read_len;
if (hdds_reader_take(reader, read_buf, sizeof(read_buf), &read_len) == HDDS_OK) {
HddsDynamicData *decoded = hdds_dynamic_data_decode(desc, read_buf, read_len);
// ... use decoded ...
hdds_dynamic_data_destroy(decoded);
}

hdds_dynamic_data_destroy(data);
hdds_type_descriptor_destroy(desc);

RPC (Request/Reply)

remarque

Requires the rpc feature: cargo build -p hdds-c --features rpc

DDS-RPC compliant request/reply communication over DDS topics.

Client

// Create client for a service (timeout in milliseconds)
HddsRpcClient *client = hdds_rpc_client_create(participant, "echo", 5000);

// Send request and wait for reply (blocking)
const uint8_t request[] = {0x01, 0x02, 0x03};
uint8_t *reply = NULL;
size_t reply_len = 0;

HddsError err = hdds_rpc_client_call(
client,
request, sizeof(request), // request payload
5000, // timeout (ms)
&reply, &reply_len); // output reply

if (err == HDDS_OK) {
// Process reply[0..reply_len]
hdds_rpc_reply_free(reply, reply_len); // MUST free reply buffer
}

// Get service name
char name_buf[64];
hdds_rpc_client_service_name(client, name_buf, sizeof(name_buf));

// Shutdown and cleanup
hdds_rpc_client_shutdown(client); // Cancel pending requests
hdds_rpc_client_destroy(client);

Server

The server handler is a C callback with this signature:

// Handler callback type:
// Returns 0 on success, or a RemoteExceptionCode (1-6) on error.
// On success, allocate reply with malloc(). HDDS will free() it after sending.
int32_t my_handler(
const uint8_t *request, size_t request_len,
uint8_t **out_reply, size_t *out_reply_len,
void *user_data)
{
// Echo: copy request to reply
*out_reply = malloc(request_len);
memcpy(*out_reply, request, request_len);
*out_reply_len = request_len;
return 0; // success
}
// Create server
HddsRpcServer *server = hdds_rpc_server_create(
participant, "echo", my_handler, my_context);

// Optional: set request timeout (skip stale requests)
hdds_rpc_server_set_request_timeout(server, 10000); // 10 seconds

// Run server (blocking -- call hdds_rpc_server_shutdown from another thread)
hdds_rpc_server_spin(server);

// Or run in background thread (non-blocking)
hdds_rpc_server_spin_background(server);
// ... application continues ...
bool running = hdds_rpc_server_is_running(server);

// Shutdown and cleanup
hdds_rpc_server_shutdown(server);
hdds_rpc_server_destroy(server);

Service Registry

// Query registered services
size_t count = hdds_rpc_service_count();
for (size_t i = 0; i < count; i++) {
char name[128];
hdds_rpc_service_name(i, name, sizeof(name));
printf("Service: %s\n", name);
}

Exception Codes

Handler return values follow the DDS-RPC specification:

CodeMeaning
0Success
1Unsupported service
2Unsupported method
3Invalid argument
4Service unavailable
5Timeout
6Internal error

Logging

// Initialize with level
hdds_logging_init(INFO); // OFF, ERROR, WARN, INFO, DEBUG, TRACE

// Or use RUST_LOG environment variable with fallback
hdds_logging_init_env(INFO);

// Or with custom filter string
hdds_logging_init_with_filter("hdds=debug,hdds::rtps=trace");

Log levels:

typedef enum HddsLogLevel {
OFF = 0,
ERROR = 1,
WARN = 2,
INFO = 3,
DEBUG = 4,
TRACE = 5,
} HddsLogLevel;

Telemetry

Built-in metrics collection and export.

Initialize

struct HddsMetrics *metrics = hdds_telemetry_init();

// Get existing (if already initialized)
struct HddsMetrics *metrics = hdds_telemetry_get();

Snapshot Metrics

struct HddsMetricsSnapshot snapshot;
hdds_telemetry_snapshot(metrics, &snapshot);

printf("Messages sent: %lu\n", snapshot.MESSAGES_SENT);
printf("Messages received: %lu\n", snapshot.MESSAGES_RECEIVED);
printf("Latency p99: %lu ns\n", snapshot.LATENCY_P99_NS);

Available metrics:

typedef struct HddsMetricsSnapshot {
uint64_t TIMESTAMP_NS;
uint64_t MESSAGES_SENT;
uint64_t MESSAGES_RECEIVED;
uint64_t MESSAGES_DROPPED;
uint64_t BYTES_SENT;
uint64_t LATENCY_P50_NS;
uint64_t LATENCY_P99_NS;
uint64_t LATENCY_P999_NS;
uint64_t MERGE_FULL_COUNT;
uint64_t WOULD_BLOCK_COUNT;
} HddsMetricsSnapshot;

Export Server

Start a server for HDDS Viewer connection:

struct HddsTelemetryExporter *exporter =
hdds_telemetry_start_exporter("0.0.0.0", 4242);

// ... application runs ...

hdds_telemetry_stop_exporter(exporter);

Cleanup

hdds_telemetry_release(metrics);

Utilities

// Get HDDS version string
const char *version = hdds_version();
printf("HDDS version: %s\n", version);

Using with Typed Data

The C API operates on raw bytes. For typed data, use hdds_gen to generate C structs and CDR2 serialization functions:

hddsgen gen c MyTypes.idl -o my_types.h

This generates:

  • C struct definitions
  • typename_encode_cdr2_le() - serialize struct to bytes
  • typename_decode_cdr2_le() - deserialize bytes to struct

Example usage:

#include "my_types.h"
#include "hdds.h"

// Encode before writing
MyType data = { .id = 42, .value = 3.14f };
uint8_t buffer[256];
int len = mytype_encode_cdr2_le(&data, buffer, sizeof(buffer));
hdds_writer_write(writer, buffer, len);

// Decode after reading
uint8_t read_buf[256];
size_t read_len;
if (hdds_reader_take(reader, read_buf, sizeof(read_buf), &read_len) == OK) {
MyType decoded = {0};
mytype_decode_cdr2_le(&decoded, read_buf, read_len);
}

Complete Example

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "hdds.h"

int main(void) {
// Initialize logging
hdds_logging_init(INFO);

// Create participant
struct HddsParticipant *participant =
hdds_participant_create_with_transport("example", UDP_MULTICAST);
if (!participant) {
fprintf(stderr, "Failed to create participant\n");
return 1;
}

// Create QoS
struct HddsQoS *qos = hdds_qos_reliable();
hdds_qos_set_transient_local(qos);
hdds_qos_set_history_depth(qos, 10);

// Create writer
struct HddsDataWriter *writer =
hdds_writer_create_with_qos(participant, "hello/world", qos);
hdds_qos_destroy(qos);

if (!writer) {
fprintf(stderr, "Failed to create writer\n");
hdds_participant_destroy(participant);
return 1;
}

// Wait for discovery
sleep(2);

// Publish messages
for (int i = 0; i < 10; i++) {
char message[64];
int len = snprintf(message, sizeof(message), "Hello #%d!", i);

if (hdds_writer_write(writer, message, len) == OK) {
printf("Published: %s\n", message);
}
sleep(1);
}

// Cleanup
hdds_writer_destroy(writer);
hdds_participant_destroy(participant);

return 0;
}

Thread Safety

  • Participant creation/destruction: NOT thread-safe
  • Writer/Reader creation: NOT thread-safe
  • hdds_writer_write(): Thread-safe (multiple threads can write concurrently)
  • hdds_reader_take(): NOT thread-safe (use external locking or one reader per thread)
  • QoS functions: NOT thread-safe
  • WaitSet: NOT thread-safe
  • Listener callbacks: invoked from DDS background threads -- must be thread-safe
  • RPC client calls: NOT thread-safe (one call at a time per client)
  • hdds_rpc_server_shutdown(): Thread-safe (designed to be called from another thread)
  • Dynamic data/type builder: NOT thread-safe
  • Content filter evaluation: NOT thread-safe per filter instance

Memory Management

  • All hdds_*_create* functions allocate memory
  • Corresponding hdds_*_destroy or hdds_*_delete must be called to free
  • const return values (like hdds_participant_name()) are internal pointers, valid until entity is destroyed
  • QoS can be destroyed after entity creation (values are copied)
  • hdds_config_build and hdds_type_builder_build consume their input -- do NOT call the corresponding destroy
  • hdds_config_set_security consumes the security config handle
  • RPC reply buffers from hdds_rpc_client_call must be freed with hdds_rpc_reply_free
  • RPC server handler reply buffers must be allocated with malloc() -- HDDS will free() them

Next Steps