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.
This documents the current v1.0.0 API. The API is FFI-safe and ABI-stable.
Installation
# Build from source
git clone https://git.hdds.io/hdds/hdds.git
cd hdds
cargo build --release -p hdds-c
# Install library
sudo cp target/release/libhdds_c.so /usr/local/lib/
sudo cp crates/hdds-c/hdds.h /usr/local/include/
sudo ldconfig
Header
#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);
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 bytestypename_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
Memory Management
- All
hdds_*_create*functions allocate memory - Corresponding
hdds_*_destroymust be called to free constreturn values (likehdds_participant_name()) are internal pointers, valid until entity is destroyed- QoS can be destroyed after entity creation (values are copied)
Next Steps
- Hello World C - Complete tutorial
- hdds_gen - Code generator for typed data
- Rust API - Native Rust API