IDL Compiler Guide¶
The AstuteDDS IDL compiler (astutedds-idl) translates OMG IDL 4.2 definitions into C++20 code with full X-Types support.
What is IDL?¶
Interface Definition Language (IDL) is a specification language used to define data types in a language-neutral way. The AstuteDDS IDL compiler generates C++ code from IDL definitions for use with DDS.
Installation¶
The IDL compiler is built as part of AstuteDDS:
cd astutedds-cxx
mkdir build && cd build
cmake .. -DASTUTEDDS_BUILD_TOOLS=ON
cmake --build . --target astutedds-idl
The compiler binary is located at:
build/tools/idl_compiler/astutedds-idl
Basic Usage¶
Command Line Syntax¶
astutedds-idl [options] <input.idl> [output_directory]
Simple Example¶
# Compile shapes.idl to current directory
./build/tools/idl_compiler/astutedds-idl examples/shapes_demo/shapes.idl
# Compile to specific output directory
./build/tools/idl_compiler/astutedds-idl examples/shapes_demo/shapes.idl generated/
Command Line Options¶
astutedds-idl --help
Options:
-o, --output-dir <dir> Output directory (default: current)
-l, --language <lang> Target language (cpp, c, python)
--xcdr-version <1|2> XCDR version (default: both)
--namespace <ns> Root namespace override
--type-support Generate TypeSupport only
--verbose Verbose output
--version Show version
-h, --help Show help
Shapes Demo Example¶
The Shapes Demo uses a simple IDL file that demonstrates common DDS patterns.
shapes.idl¶
// File: examples/shapes.idl
module ShapesDemo {
enum ShapeKind {
CIRCLE,
SQUARE,
TRIANGLE
};
@mutable
struct ShapeType {
@key string<128> color;
long x;
long y;
long shapesize;
};
@final
struct Point {
long x;
long y;
};
@appendable
struct Circle {
@key long id;
Point center;
long radius;
};
};
Compiling shapes.idl¶
# Compile the shapes IDL
./build/tools/idl_compiler/astutedds-idl examples/shapes.idl output/
# Generated files:
# output/ShapeKind.hpp - Enum definition
# output/ShapeType.hpp - Main struct
# output/ShapeType_TypeSupport.hpp - DDS TypeSupport
# output/ShapeType_XCDR.hpp - Serialization code
# output/Point.hpp - Point struct
# output/Circle.hpp - Circle struct
Using Generated Code¶
#include "ShapeType.hpp"
#include "ShapeType_TypeSupport.hpp"
#include <astutedds/dcps/domain_participant.hpp>
int main() {
using namespace ShapesDemo;
// Create DDS entities
auto participant = create_participant(0);
auto topic = participant->create_topic<ShapeType>("Circle");
auto writer = create_datawriter(participant, topic);
// Use generated type
ShapeType shape;
shape.color = "BLUE";
shape.x = 120;
shape.y = 135;
shape.shapesize = 30;
// Write sample
writer->write(shape);
return 0;
}
IDL Language Features¶
Modules (Namespaces)¶
Modules map to C++ namespaces:
module Sensors {
module Temperature {
struct Reading {
float celsius;
long timestamp;
};
};
};
Generated C++:
namespace Sensors {
namespace Temperature {
struct Reading {
float celsius;
int32_t timestamp;
};
} // namespace Temperature
} // namespace Sensors
Primitive Types¶
IDL primitive types map to C++ types:
| IDL Type | C++ Type | Size |
|---|---|---|
boolean |
bool |
1 byte |
char |
char |
1 byte |
octet |
uint8_t |
1 byte |
short |
int16_t |
2 bytes |
unsigned short |
uint16_t |
2 bytes |
long |
int32_t |
4 bytes |
unsigned long |
uint32_t |
4 bytes |
long long |
int64_t |
8 bytes |
unsigned long long |
uint64_t |
8 bytes |
float |
float |
4 bytes |
double |
double |
8 bytes |
long double |
long double |
16 bytes |
Strings¶
struct Message {
string text; // Unbounded string
string<256> limited; // Bounded string (max 256 chars)
};
Generated C++:
struct Message {
std::string text;
std::string limited; // Application must enforce length
};
Sequences¶
struct DataSet {
sequence<float> values; // Unbounded sequence
sequence<long, 100> readings; // Bounded sequence (max 100)
};
Generated C++:
struct DataSet {
std::vector<float> values;
std::vector<int32_t> readings; // Max size checked at runtime
};
Arrays¶
struct Matrix {
float data[4][4]; // 2D array
long vector[3]; // 1D array
};
Generated C++:
struct Matrix {
std::array<std::array<float, 4>, 4> data;
std::array<int32_t, 3> vector;
};
Enumerations¶
enum Status {
IDLE,
RUNNING,
STOPPED,
ERROR
};
struct SystemState {
Status current_status;
};
Generated C++:
enum class Status : int32_t {
IDLE = 0,
RUNNING = 1,
STOPPED = 2,
ERROR = 3
};
struct SystemState {
Status current_status{Status::IDLE};
};
Nested Structures¶
struct Point3D {
double x;
double y;
double z;
};
struct Pose {
Point3D position;
Point3D orientation;
};
Generated C++:
struct Point3D {
double x{0.0};
double y{0.0};
double z{0.0};
};
struct Pose {
Point3D position;
Point3D orientation;
};
IDL Annotations¶
Annotations control DDS behavior and code generation.
@key - Instance Identity¶
Marks fields that identify unique instances:
struct SensorData {
@key long sensor_id; // Key field
float temperature;
float humidity;
};
Effect:
- Each unique
sensor_idis a separate instance - Readers can track instances independently
- Used for instance lifecycle management
@optional - Optional Fields¶
Marks fields that may not always be present:
struct Config {
long id;
@optional string description;
@optional float calibration_factor;
};
Generated C++:
struct Config {
int32_t id{0};
std::optional<std::string> description;
std::optional<float> calibration_factor;
};
@id - Member ID¶
Explicitly assigns member IDs for XCDR2 mutable types:
@mutable
struct Extensible {
@id(1) long field_a;
@id(2) string field_b;
@id(10) @optional float field_c;
};
Benefits:
- Forward/backward compatibility
- Fields can be reordered
- New fields can be added
@autoid - Automatic Member IDs¶
Generates member IDs automatically:
@mutable
@autoid(SEQUENTIAL) // or HASH
struct AutoIdType {
long field1; // ID assigned automatically
long field2;
long field3;
};
Extensibility Annotations¶
Control type evolution compatibility:
@final
struct Fixed {
// Cannot add/remove fields
long id;
string name;
};
@appendable
struct Growable {
// Can add fields at end
long id;
string name;
};
@mutable
struct Flexible {
// Can add/remove/reorder fields
@id(1) long id;
@id(2) string name;
};
Complete IDL Example¶
Here's a comprehensive example using various IDL features:
// sensor_system.idl
module SensorSystem {
// Enumeration for sensor types
enum SensorType {
TEMPERATURE,
PRESSURE,
HUMIDITY,
LIGHT
};
// Simple status enumeration
enum Status {
INACTIVE,
ACTIVE,
ERROR
};
// Point structure
struct Location {
double latitude;
double longitude;
@optional double altitude;
};
// Sensor reading (mutable for evolution)
@mutable
struct SensorReading {
@key @id(1) long sensor_id;
@id(2) SensorType type;
@id(3) float value;
@id(4) long long timestamp;
@id(5) @optional Status status;
@id(6) @optional Location location;
};
// Sensor configuration (final - won't change)
@final
struct SensorConfig {
@key long sensor_id;
string<64> name;
SensorType type;
float min_value;
float max_value;
long update_rate_ms;
};
// Aggregated statistics (appendable)
@appendable
struct SensorStatistics {
@key long sensor_id;
float mean_value;
float std_deviation;
long sample_count;
sequence<float, 100> recent_values;
};
};
Compiling the Example¶
./build/tools/idl_compiler/astutedds-idl sensor_system.idl generated/
Using the Generated Types¶
#include "generated/SensorReading.hpp"
#include "generated/SensorReading_TypeSupport.hpp"
#include "generated/SensorConfig.hpp"
#include "generated/SensorConfig_TypeSupport.hpp"
int main() {
using namespace SensorSystem;
// Create sensor reading
SensorReading reading;
reading.sensor_id = 42;
reading.type = SensorType::TEMPERATURE;
reading.value = 25.5f;
reading.timestamp = get_current_time();
reading.status = Status::ACTIVE;
Location loc;
loc.latitude = 51.5074;
loc.longitude = -0.1278;
loc.altitude = 11.0;
reading.location = loc;
// Create sensor configuration
SensorConfig config;
config.sensor_id = 42;
config.name = "TempSensor-001";
config.type = SensorType::TEMPERATURE;
config.min_value = -50.0f;
config.max_value = 150.0f;
config.update_rate_ms = 1000;
// Publish via DDS
auto participant = create_participant(0);
auto reading_topic = participant->create_topic<SensorReading>("SensorReadings");
auto reading_writer = create_datawriter(participant, reading_topic);
reading_writer->write(reading);
auto config_topic = participant->create_topic<SensorConfig>("SensorConfigs");
auto config_writer = create_datawriter(participant, config_topic);
config_writer->write(config);
return 0;
}
Generated Files¶
For each IDL type, the compiler generates:
1. Type Header (TypeName.hpp)¶
Contains the C++ struct definition:
#ifndef SENSORREADING_HPP
#define SENSORREADING_HPP
#include <cstdint>
#include <string>
#include <optional>
namespace SensorSystem {
struct SensorReading {
int32_t sensor_id{0};
SensorType type{SensorType::TEMPERATURE};
float value{0.0f};
int64_t timestamp{0};
std::optional<Status> status;
std::optional<Location> location;
bool operator==(const SensorReading&) const = default;
};
} // namespace SensorSystem
#endif
2. TypeSupport Header (TypeName_TypeSupport.hpp)¶
Provides DDS integration:
#ifndef SENSORREADING_TYPESUPPORT_HPP
#define SENSORREADING_TYPESUPPORT_HPP
#include "SensorReading.hpp"
#include <astutedds/xtypes/type_object.hpp>
namespace SensorSystem {
class SensorReadingTypeSupport {
public:
static astutedds::xtypes::CompleteTypeObject get_type_object();
static astutedds::xtypes::TypeIdentifier get_type_identifier();
static bool register_type(
astutedds::dcps::DomainParticipant& participant,
const std::string& type_name = "SensorSystem::SensorReading"
);
};
} // namespace SensorSystem
#endif
3. XCDR Codec Header (TypeName_XCDR.hpp)¶
Serialization/deserialization code:
#ifndef SENSORREADING_XCDR_HPP
#define SENSORREADING_XCDR_HPP
#include "SensorReading.hpp"
#include <astutedds/cdr/cdr_types.hpp>
namespace SensorSystem {
class SensorReadingXCDR1 {
public:
static bool serialize(
astutedds::cdr::CDRBuffer& buffer,
const SensorReading& value,
astutedds::cdr::EncodingKind encoding
);
static bool deserialize(
astutedds::cdr::CDRBuffer& buffer,
SensorReading& value,
astutedds::cdr::EncodingKind encoding
);
};
class SensorReadingXCDR2 {
public:
static bool serialize(
astutedds::cdr::CDRBuffer& buffer,
const SensorReading& value,
astutedds::cdr::EncodingKind encoding
);
static bool deserialize(
astutedds::cdr::CDRBuffer& buffer,
SensorReading& value,
astutedds::cdr::EncodingKind encoding
);
};
} // namespace SensorSystem
#endif
Advanced Features¶
Type Inheritance¶
struct BaseMessage {
long sequence_number;
long long timestamp;
};
struct SensorMessage : BaseMessage {
long sensor_id;
float value;
};
Unions¶
union Data switch (long) {
case 1:
float temperature;
case 2:
long humidity;
case 3:
string message;
default:
octet raw_data;
};
Constants¶
const long MAX_SENSORS = 100;
const float PI = 3.14159;
const string<32> VERSION = "1.0.0";
struct Config {
long max_count; // Can use MAX_SENSORS
};
Best Practices¶
- Use modules: Organize types in namespaces
- Add @key annotations: Define instance identity clearly
- Use @mutable for evolution: Allow future changes
- Bound collections: Use
sequence<T, N>instead ofsequence<T> - Document types: Add comments for clarity
- Version your IDL: Track changes over time
- Test generated code: Compile and run tests
Troubleshooting¶
Common Errors¶
Error: Unknown type 'Foo'
- Solution: Define types before using them, or use forward declarations
Error: Circular dependency detected
- Solution: Break circular references using forward declarations
Error: Invalid @key annotation
- Solution: Only use @key on struct members, not nested types
Compiler Output¶
Enable verbose mode for detailed information:
./build/tools/idl_compiler/astutedds-idl --verbose examples/shapes.idl
Next Steps¶
- Explore QoS Usage with generated types
- Try the Shapes Demo
- Review IDL Examples
- Read the IDL 4.2 Specification