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
For package users, astutedds-idl is installed with the AstuteDDS developer
package.
astutedds-idl --help
If you are building from source, build the tool target:
Compile shapes.idl to current directory
astutedds-idl examples/shapes_demo/shapes.idl mkdir build && cd build cmake .. -DASTUTEDDS_BUILD_TOOLS=ON cmake --build . --target astutedds-idl
Source-build binary location:
build/tools/idl_compiler/astutedds-idl
## Basic Usage
### Command Line Syntax
```bash
astutedds-idl [options] <input.idl> [output_directory]
Simple Example
# Compile shapes.idl to current directory
astutedds-idl examples/shapes_demo/shapes.idl
# Compile to specific output directory
astutedds-idl -o generated examples/shapes_demo/shapes.idl
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
astutedds-idl -o output examples/shapes_demo/shapes.idl
# 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
Minimum Files for First Application
For the first publisher/subscriber workflow, use:
generated/<Type>.hppgenerated/<Type>.cpp
Additional generated files such as *_TypeSupport.hpp and *_XCDR.hpp are
part of broader type support and advanced integration workflows.
Using Generated Code
For an end-to-end generated type workflow (IDL -> C++ -> publisher/subscriber), follow First Application.
The generated headers are then included by your app code:
#include "ShapeType.hpp"
ShapesDemo::ShapeType sample;
// Populate fields and publish using the DCPS API used in First Application.
IDL Language Features
Troubleshooting
Command Not Found
If astutedds-idl is not found, verify the developer package installation and
that your shell PATH includes the package binary directory.
Parse or Syntax Errors
Use verbose mode to pinpoint the failing construct:
astutedds-idl --verbose <input.idl>
Then check for:
- Unclosed braces or semicolons
- Invalid annotations or annotation placement
- Unsupported/invalid IDL tokens
Generated Files Not Found in Build
Verify your CMake target includes both generated artifacts:
generated/<Type>.hppgenerated/<Type>.cpp
Also ensure your target include path contains the generated directory.
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