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