File builtin_types.hpp
File List > astutedds > xtypes > builtin_types > builtin_types.hpp
Go to the documentation of this file
//
// Copyright (c) 2026, Astute Systems PTY LTD
//
// This file is part of the Astute DDS developed by Astute Systems.
//
// See the commercial LICENSE file in the project root for full license details.
//
// @file builtin_types.hpp
// @brief DDS-XTypes 1.3 §7.6.5 standard built-in topic types.
//
// Ships pre-generated definitions and XCDR1 / XCDR2 codecs for the four
// standard built-in topic types that every DDS vendor exposes:
//
// - String (§7.6.5.1) — struct { string value; }
// - KeyedString (§7.6.5.2) — struct { @key string key; string value; }
// - Bytes (§7.6.5.3) — struct { sequence<octet> value; }
// - KeyedBytes (§7.6.5.4) — struct { @key string key; sequence<octet> value; }
//
// Each type ships with:
// - A struct with public data members (matches the OMG PSM shape).
// - Static `serialize` / `deserialize` helpers for XCDR1 (`*XCDR1`) and
// XCDR2 (`*XCDR2`, delimited @appendable per §7.4.4.3).
// - A typed `*DataWriter` / `*DataReader` pair returning
// `ReturnCode_t` overloads that accept the built-in struct
// directly.
// - `register_type(dp, name)` — factory registration so
// `Publisher::create_datawriter()` / `Subscriber::create_datareader()`
// return the typed subclass, matching how vendor `String_TypeSupport`
// helpers behave in RTI Connext, Fast DDS and Cyclone.
//
// Usage:
//
// using astutedds::xtypes::builtin::String;
// using astutedds::xtypes::builtin::StringDataWriter;
// String::register_type(dp, "String");
// auto* topic = dp->create_topic("chatter", "String");
// auto* dw = dynamic_cast<StringDataWriter*>(
// publisher->create_datawriter(topic));
// dw->write(String{"hello"});
//
#ifndef ASTUTEDDS_XTYPES_BUILTIN_TYPES_HPP
#define ASTUTEDDS_XTYPES_BUILTIN_TYPES_HPP
#include <astutedds/cdr/cdr_types.hpp>
#include <astutedds/cdr/xcdr1_codec.hpp>
#include <astutedds/cdr/xcdr2_codec.hpp>
#include <astutedds/dcps/dcps.hpp>
#include <astutedds/dcps/typed.hpp>
#include <cstdint>
#include <cstring>
#include <string>
#include <vector>
namespace astutedds::xtypes::builtin
{
// ----------------------------------------------------------------------
// String (§7.6.5.1) — no key, single UTF-8 string field.
// ----------------------------------------------------------------------
struct String
{
std::string value;
String() = default;
String(std::string v) : value(std::move(v)) {}
// Forward-declared; body appears below after codec definitions.
static void register_type(dcps::DomainParticipant* dp, const char* type_name);
};
struct StringXCDR1
{
static bool serialize(cdr::XCDR1Encoder& enc, const String& v)
{
try { enc.encode_string(v.value); return true; }
catch (...) { return false; }
}
static bool deserialize(cdr::XCDR1Decoder& dec, String& v)
{
try { v.value = dec.decode_string(); return true; }
catch (...) { return false; }
}
};
struct StringXCDR2
{
static bool serialize(cdr::XCDR2Encoder& enc, const String& v)
{
try
{
enc.begin_delimited();
enc.encode_string(v.value);
enc.end_delimited();
return true;
}
catch (...) { return false; }
}
static bool deserialize(cdr::XCDR2Decoder& dec, String& v)
{
try
{
dec.begin_delimited();
v.value = dec.decode_string();
dec.end_delimited();
return true;
}
catch (...) { return false; }
}
};
// ----------------------------------------------------------------------
// KeyedString (§7.6.5.2) — @key string key + string value.
// ----------------------------------------------------------------------
struct KeyedString
{
std::string key;
std::string value;
KeyedString() = default;
KeyedString(std::string k, std::string v)
: key(std::move(k)), value(std::move(v)) {}
static void register_type(dcps::DomainParticipant* dp, const char* type_name);
};
struct KeyedStringXCDR1
{
static bool serialize(cdr::XCDR1Encoder& enc, const KeyedString& v)
{
try
{
enc.encode_string(v.key);
enc.encode_string(v.value);
return true;
}
catch (...) { return false; }
}
static bool deserialize(cdr::XCDR1Decoder& dec, KeyedString& v)
{
try
{
v.key = dec.decode_string();
v.value = dec.decode_string();
return true;
}
catch (...) { return false; }
}
};
struct KeyedStringXCDR2
{
static bool serialize(cdr::XCDR2Encoder& enc, const KeyedString& v)
{
try
{
enc.begin_delimited();
enc.encode_string(v.key);
enc.encode_string(v.value);
enc.end_delimited();
return true;
}
catch (...) { return false; }
}
static bool deserialize(cdr::XCDR2Decoder& dec, KeyedString& v)
{
try
{
dec.begin_delimited();
v.key = dec.decode_string();
v.value = dec.decode_string();
dec.end_delimited();
return true;
}
catch (...) { return false; }
}
};
// ----------------------------------------------------------------------
// Bytes (§7.6.5.3) — no key, single sequence<octet> field.
// ----------------------------------------------------------------------
struct Bytes
{
std::vector<uint8_t> value;
Bytes() = default;
Bytes(std::vector<uint8_t> v) : value(std::move(v)) {}
static void register_type(dcps::DomainParticipant* dp, const char* type_name);
};
struct BytesXCDR1
{
static bool serialize(cdr::XCDR1Encoder& enc, const Bytes& v)
{
try
{
enc.encode_uint32(static_cast<uint32_t>(v.value.size()));
for (uint8_t b : v.value) enc.encode_uint8(b);
return true;
}
catch (...) { return false; }
}
static bool deserialize(cdr::XCDR1Decoder& dec, Bytes& v)
{
try
{
uint32_t len = dec.decode_uint32();
v.value.resize(len);
for (uint32_t i = 0; i < len; ++i) v.value[i] = dec.decode_uint8();
return true;
}
catch (...) { return false; }
}
};
struct BytesXCDR2
{
static bool serialize(cdr::XCDR2Encoder& enc, const Bytes& v)
{
try
{
enc.begin_delimited();
enc.encode_uint32(static_cast<uint32_t>(v.value.size()));
for (uint8_t b : v.value) enc.encode_uint8(b);
enc.end_delimited();
return true;
}
catch (...) { return false; }
}
static bool deserialize(cdr::XCDR2Decoder& dec, Bytes& v)
{
try
{
dec.begin_delimited();
uint32_t len = dec.decode_uint32();
v.value.resize(len);
for (uint32_t i = 0; i < len; ++i) v.value[i] = dec.decode_uint8();
dec.end_delimited();
return true;
}
catch (...) { return false; }
}
};
// ----------------------------------------------------------------------
// KeyedBytes (§7.6.5.4) — @key string key + sequence<octet> value.
// ----------------------------------------------------------------------
struct KeyedBytes
{
std::string key;
std::vector<uint8_t> value;
KeyedBytes() = default;
KeyedBytes(std::string k, std::vector<uint8_t> v)
: key(std::move(k)), value(std::move(v)) {}
static void register_type(dcps::DomainParticipant* dp, const char* type_name);
};
struct KeyedBytesXCDR1
{
static bool serialize(cdr::XCDR1Encoder& enc, const KeyedBytes& v)
{
try
{
enc.encode_string(v.key);
enc.encode_uint32(static_cast<uint32_t>(v.value.size()));
for (uint8_t b : v.value) enc.encode_uint8(b);
return true;
}
catch (...) { return false; }
}
static bool deserialize(cdr::XCDR1Decoder& dec, KeyedBytes& v)
{
try
{
v.key = dec.decode_string();
uint32_t len = dec.decode_uint32();
v.value.resize(len);
for (uint32_t i = 0; i < len; ++i) v.value[i] = dec.decode_uint8();
return true;
}
catch (...) { return false; }
}
};
struct KeyedBytesXCDR2
{
static bool serialize(cdr::XCDR2Encoder& enc, const KeyedBytes& v)
{
try
{
enc.begin_delimited();
enc.encode_string(v.key);
enc.encode_uint32(static_cast<uint32_t>(v.value.size()));
for (uint8_t b : v.value) enc.encode_uint8(b);
enc.end_delimited();
return true;
}
catch (...) { return false; }
}
static bool deserialize(cdr::XCDR2Decoder& dec, KeyedBytes& v)
{
try
{
dec.begin_delimited();
v.key = dec.decode_string();
uint32_t len = dec.decode_uint32();
v.value.resize(len);
for (uint32_t i = 0; i < len; ++i) v.value[i] = dec.decode_uint8();
dec.end_delimited();
return true;
}
catch (...) { return false; }
}
};
// ----------------------------------------------------------------------
// Typed DataWriter / DataReader subclasses.
//
// Each pair wraps astutedds::dcps::TypedDataWriter / TypedDataReader
// so the caller works with the built-in struct directly instead of a
// std::vector<uint8_t> payload.
// ----------------------------------------------------------------------
template <typename T, typename Xcdr1, typename Xcdr2>
class BuiltinDataWriter : public dcps::DataWriter
{
public:
BuiltinDataWriter(dcps::Topic* topic, dcps::Publisher* publisher)
: dcps::DataWriter(topic, publisher) {}
dcps::ReturnCode_t write(const T& data)
{
dcps::TypedDataWriter<T, Xcdr2, Xcdr1> typed(this);
return typed.write(data) ? dcps::ReturnCode_t::RETCODE_OK
: dcps::ReturnCode_t::RETCODE_ERROR;
}
};
template <typename T, typename Xcdr1, typename Xcdr2>
class BuiltinDataReader : public dcps::DataReader
{
public:
BuiltinDataReader(dcps::Topic* topic, dcps::Subscriber* subscriber)
: dcps::DataReader(topic, subscriber) {}
dcps::ReturnCode_t take_next_sample(T& out, dcps::SampleInfo& info)
{
dcps::TypedDataReader<T, Xcdr2, Xcdr1> typed(this);
return typed.take_next(out, info);
}
dcps::ReturnCode_t read_next_sample(T& out, dcps::SampleInfo& info)
{
dcps::TypedDataReader<T, Xcdr2, Xcdr1> typed(this);
return typed.read_next(out, info);
}
};
using StringDataWriter = BuiltinDataWriter<String, StringXCDR1, StringXCDR2>;
using StringDataReader = BuiltinDataReader<String, StringXCDR1, StringXCDR2>;
using KeyedStringDataWriter = BuiltinDataWriter<KeyedString, KeyedStringXCDR1, KeyedStringXCDR2>;
using KeyedStringDataReader = BuiltinDataReader<KeyedString, KeyedStringXCDR1, KeyedStringXCDR2>;
using BytesDataWriter = BuiltinDataWriter<Bytes, BytesXCDR1, BytesXCDR2>;
using BytesDataReader = BuiltinDataReader<Bytes, BytesXCDR1, BytesXCDR2>;
using KeyedBytesDataWriter = BuiltinDataWriter<KeyedBytes, KeyedBytesXCDR1, KeyedBytesXCDR2>;
using KeyedBytesDataReader = BuiltinDataReader<KeyedBytes, KeyedBytesXCDR1, KeyedBytesXCDR2>;
// ----------------------------------------------------------------------
// register_type() implementations.
//
// Each installs a factory pair on the DomainParticipant so
// create_datawriter()/create_datareader() return the typed subclass
// for any topic whose type_name() matches.
// ----------------------------------------------------------------------
inline void String::register_type(dcps::DomainParticipant* dp, const char* type_name)
{
dp->register_type_factory(
type_name,
[](dcps::Topic* t, dcps::Publisher* p) -> dcps::DataWriter*
{ return new StringDataWriter(t, p); },
[](dcps::Topic* t, dcps::Subscriber* s) -> dcps::DataReader*
{ return new StringDataReader(t, s); });
}
inline void KeyedString::register_type(dcps::DomainParticipant* dp, const char* type_name)
{
dp->register_type_factory(
type_name,
[](dcps::Topic* t, dcps::Publisher* p) -> dcps::DataWriter*
{ return new KeyedStringDataWriter(t, p); },
[](dcps::Topic* t, dcps::Subscriber* s) -> dcps::DataReader*
{ return new KeyedStringDataReader(t, s); });
}
inline void Bytes::register_type(dcps::DomainParticipant* dp, const char* type_name)
{
dp->register_type_factory(
type_name,
[](dcps::Topic* t, dcps::Publisher* p) -> dcps::DataWriter*
{ return new BytesDataWriter(t, p); },
[](dcps::Topic* t, dcps::Subscriber* s) -> dcps::DataReader*
{ return new BytesDataReader(t, s); });
}
inline void KeyedBytes::register_type(dcps::DomainParticipant* dp, const char* type_name)
{
dp->register_type_factory(
type_name,
[](dcps::Topic* t, dcps::Publisher* p) -> dcps::DataWriter*
{ return new KeyedBytesDataWriter(t, p); },
[](dcps::Topic* t, dcps::Subscriber* s) -> dcps::DataReader*
{ return new KeyedBytesDataReader(t, s); });
}
} // namespace astutedds::xtypes::builtin
#endif // ASTUTEDDS_XTYPES_BUILTIN_TYPES_HPP