Adds immediate migration results
This commit is contained in:
2395 files changed
+357322
-2524
No files matched your search
@@ -0,0 +1,558 @@
|
||||
/*
|
||||
(c) Copyright, Real-Time Innovations, 2018.
|
||||
All rights reserved.
|
||||
|
||||
No duplications, whole or partial, manual or electronic, may be made
|
||||
without express written permission. Any such copies, or
|
||||
revisions thereof, must display this notice unaltered.
|
||||
This code contains trade secrets of Real-Time Innovations, Inc.
|
||||
*/
|
||||
|
||||
#ifndef RTI_DDS_FLAT_AGGREGATIONBUILDERS_HPP_
|
||||
#define RTI_DDS_FLAT_AGGREGATIONBUILDERS_HPP_
|
||||
|
||||
#include "rti/flat/BuilderHelper.hpp"
|
||||
#include "rti/flat/Builder.hpp"
|
||||
#include "xcdr/xcdr_interpreter.h"
|
||||
|
||||
namespace rti { namespace flat {
|
||||
|
||||
/*
|
||||
* Parameters describing a member to be added or built.
|
||||
*
|
||||
* The information encoded by this structure is used to set the appropriate
|
||||
* bits in the header that precedes each member of a mutable type.
|
||||
*/
|
||||
struct MemberParams {
|
||||
/*
|
||||
* Indicates whether the member is part of the key.
|
||||
*/
|
||||
bool key;
|
||||
/*
|
||||
* Indicates whether the member is the discriminator of a union.
|
||||
*/
|
||||
bool discriminator;
|
||||
/*
|
||||
* Indicates whether the member has been marked as "must understand".
|
||||
*/
|
||||
bool must_understand;
|
||||
|
||||
MemberParams() : key(false), discriminator(false), must_understand(false)
|
||||
{ }
|
||||
|
||||
/*
|
||||
* Check whether the current XCDR2 compliance mask allows the
|
||||
* specified features.
|
||||
*
|
||||
* This function will cache the value of the compliance mask to avoid
|
||||
* repeated nested function calls at runtime.
|
||||
*
|
||||
* @param check_mask The feature bits to check in the compliance mask.
|
||||
* @param reload_mask If true, force reloading of the compliance mask
|
||||
* from the global configuration.
|
||||
* @return true if all the bits in check_mask are set in the compliance
|
||||
* mask.
|
||||
*/
|
||||
static bool xcdr2_compliant(
|
||||
const RTIXCdrXTypesComplianceMask check_mask,
|
||||
const bool reload_mask)
|
||||
{
|
||||
static bool g_compliance_mask_set = false;
|
||||
static RTIXCdrXTypesComplianceMask g_compliance_mask = 0;
|
||||
|
||||
// Initialize the compliance mask on first use
|
||||
// There could be a data race here but the worst that can happen is
|
||||
// that the compliance mask is read more than once.
|
||||
// The goal is to avoid nested function calls on each and every call.
|
||||
if (reload_mask || !g_compliance_mask_set) {
|
||||
g_compliance_mask =
|
||||
RTIXCdrInterpreter_getGlobalXtypeComplianceMask();
|
||||
g_compliance_mask_set = true;
|
||||
}
|
||||
|
||||
return (g_compliance_mask & check_mask);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the must_understand bit should be set in the header for this
|
||||
* member.
|
||||
*
|
||||
* The bit will always be set if the must_understand attribute is true.
|
||||
* Additionally, if the current XCDR2 compliance mask allows it, the bit
|
||||
* will also be set for key members and discriminators.
|
||||
*
|
||||
* @return true if the must_understand bit should be set.
|
||||
*/
|
||||
bool has_must_understand_bit() const
|
||||
{
|
||||
return must_understand
|
||||
|| (xcdr2_compliant(
|
||||
RTI_XCDR_XTYPES_MUST_UNDERSTAND_IN_KEY_AND_DISCRIMINATOR_BIT,
|
||||
false)
|
||||
&& (key || discriminator));
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Provides the common functionality for builders of aggregated types
|
||||
*
|
||||
* Extends and specializes AbstractBuilder to add or override the functionality
|
||||
* required to build an aggregated type:
|
||||
*
|
||||
* - Add or build members, prepending the member header
|
||||
* - Add dheader at the beginning
|
||||
* - Add parameter-list-end header at the end
|
||||
*/
|
||||
/**
|
||||
* @ingroup RTIFlatBuildersModule
|
||||
* @brief Base class of struct and union builders.
|
||||
*
|
||||
* This class contains implementation details and doesn't add any public function
|
||||
* to AbstractBuilder. See MyFlatMutableBuilder for a concrete example of a
|
||||
* struct builder.
|
||||
*/
|
||||
class AggregationBuilder : public AbstractBuilder {
|
||||
protected:
|
||||
AggregationBuilder() :
|
||||
dheader_position_(NULL),
|
||||
emheader_position_(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
// Create a new top-level builder
|
||||
AggregationBuilder(
|
||||
unsigned char *initial_buffer,
|
||||
offset_t size,
|
||||
bool initialize_members) :
|
||||
AbstractBuilder(initial_buffer, size, initialize_members),
|
||||
dheader_position_(NULL),
|
||||
emheader_position_(NULL)
|
||||
{
|
||||
dheader_position_ = stream().serialize_dheader();
|
||||
if (dheader_position_ == NULL) {
|
||||
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(invalidate());
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
// For internal use and testing
|
||||
//
|
||||
// Modifies the encapsulation set in the constructor with a custom value.
|
||||
// This function can only be called after the construction of a non-nested
|
||||
// Builder, before any member is built or added.
|
||||
//
|
||||
void set_encapsulation_impl(RTIXCdrEncapsulationId encapsulation_id)
|
||||
{
|
||||
// Only a top-level Builder can change the encapsulation
|
||||
RTI_FLAT_CHECK_PRECONDITION(!is_nested(), return);
|
||||
|
||||
char *current_position = (char *) owned_stream_.current_position();
|
||||
// This operation is only valid right after construction, when no
|
||||
// member has been added yet
|
||||
RTI_FLAT_CHECK_PRECONDITION(
|
||||
current_position == dheader_position_ + RTI_XCDR_DHEADER_SIZE,
|
||||
return);
|
||||
|
||||
owned_stream_.skip_back(
|
||||
RTI_XCDR_DHEADER_SIZE + RTI_XCDR_ENCAPSULATION_HEADER_SIZE);
|
||||
|
||||
if (!RTIXCdrFlatSample_initializeEncapsulationAndStream(
|
||||
(char *) owned_stream_.buffer(),
|
||||
&owned_stream_.c_stream(),
|
||||
encapsulation_id,
|
||||
owned_stream_.total_size())) {
|
||||
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(return);
|
||||
}
|
||||
|
||||
stream().serialize_dheader();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
#if defined(RTI_FLAT_DATA_CXX11_RVALUE_REFERENCES)
|
||||
AggregationBuilder(AggregationBuilder&& other) = default;
|
||||
|
||||
AggregationBuilder& operator=(AggregationBuilder&& other)
|
||||
{
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
finish_untyped_impl();
|
||||
|
||||
AbstractBuilder::operator=(static_cast<AbstractBuilder&&>(other));
|
||||
|
||||
dheader_position_ = other.dheader_position_;
|
||||
emheader_position_ = other.emheader_position_;
|
||||
|
||||
return *this;
|
||||
}
|
||||
#else
|
||||
public:
|
||||
// Enable the safe-move-constructor idiom without C++11 move constructors
|
||||
struct UntypedAggregationBuilderMoveProxy : AbstractBuilderMoveProxy {
|
||||
char * dheader_position_;
|
||||
char * emheader_position_;
|
||||
};
|
||||
|
||||
operator UntypedAggregationBuilderMoveProxy () throw() // move-constructor idiom
|
||||
{
|
||||
UntypedAggregationBuilderMoveProxy other;
|
||||
move_to(other);
|
||||
return other;
|
||||
}
|
||||
|
||||
protected:
|
||||
void move_from(UntypedAggregationBuilderMoveProxy& other)
|
||||
{
|
||||
AbstractBuilder::move_from(other);
|
||||
dheader_position_ = other.dheader_position_;
|
||||
emheader_position_ = other.emheader_position_;
|
||||
}
|
||||
|
||||
void move_to(UntypedAggregationBuilderMoveProxy& other)
|
||||
{
|
||||
AbstractBuilder::move_to(other);
|
||||
other.dheader_position_ = dheader_position_;
|
||||
other.emheader_position_ = emheader_position_;
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
virtual ~AggregationBuilder()
|
||||
{
|
||||
// AggregationBuilder::finish_untyped_impl() is specialized
|
||||
// with respect to AbstractBuilder::finish_untyped_impl()
|
||||
finish_untyped_impl();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool add_primitive_member(
|
||||
member_id_t id,
|
||||
T value,
|
||||
const MemberParams& params)
|
||||
{
|
||||
RTI_FLAT_BUILDER_CHECK_VALID(return false);
|
||||
|
||||
rti::xcdr::Stream::Memento stream_memento(stream());
|
||||
|
||||
if (!begin_emheader(id, detail::primitive_lc_code<T>::value, params)) {
|
||||
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(return false);
|
||||
}
|
||||
// no need to call finish_emheader()
|
||||
|
||||
if (!stream().serialize_no_align(value)) {
|
||||
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(return false);
|
||||
}
|
||||
|
||||
stream_memento.discard(); // success: do not restore the current position
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool add_primitive_member(member_id_t id, T value)
|
||||
{
|
||||
static const MemberParams default_params;
|
||||
return add_primitive_member(id, value, default_params);
|
||||
}
|
||||
|
||||
template <typename OffsetType>
|
||||
OffsetType add_member(member_id_t id, const MemberParams& params)
|
||||
{
|
||||
RTI_FLAT_BUILDER_CHECK_VALID(return OffsetType());
|
||||
|
||||
// save state in case of error
|
||||
rti::xcdr::Stream::Memento stream_memento(stream());
|
||||
return add_member<OffsetType>(id, stream_memento, params);
|
||||
}
|
||||
|
||||
template <typename OffsetType>
|
||||
OffsetType add_member(member_id_t id)
|
||||
{
|
||||
RTI_FLAT_BUILDER_CHECK_VALID(return OffsetType());
|
||||
|
||||
// save state in case of error
|
||||
rti::xcdr::Stream::Memento stream_memento(stream());
|
||||
static const MemberParams default_params;
|
||||
return add_member<OffsetType>(id, stream_memento, default_params);
|
||||
}
|
||||
|
||||
template <typename OffsetType>
|
||||
OffsetType add_member(
|
||||
member_id_t id,
|
||||
rti::xcdr::Stream::Memento& stream_memento)
|
||||
{
|
||||
static const MemberParams default_params;
|
||||
return add_member<OffsetType>(id, stream_memento, default_params);
|
||||
}
|
||||
|
||||
template <typename OffsetType>
|
||||
OffsetType add_member(
|
||||
member_id_t id,
|
||||
rti::xcdr::Stream::Memento& stream_memento,
|
||||
const MemberParams& params)
|
||||
{
|
||||
// fixed-size non-primitive members may have a dheader or not; when they
|
||||
// do the LC code is 5, otherwise it is 4.
|
||||
if (!begin_emheader(
|
||||
id,
|
||||
detail::is_fixed_type_w_dheader<OffsetType>::value() ? 5
|
||||
: 4,
|
||||
params)) {
|
||||
return OffsetType();
|
||||
}
|
||||
|
||||
OffsetType result = AbstractBuilder::add_element<OffsetType>();
|
||||
finish_emheader();
|
||||
|
||||
stream_memento.discard(); // success - do not restore the previous state
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename NestedBuilder>
|
||||
NestedBuilder build_member(member_id_t id, const MemberParams& params)
|
||||
{
|
||||
RTI_FLAT_BUILDER_CHECK_VALID(return NestedBuilder());
|
||||
|
||||
// save state in case of error
|
||||
rti::xcdr::Stream::Memento stream_memento(stream());
|
||||
return build_member<NestedBuilder>(id, stream_memento, params);
|
||||
}
|
||||
|
||||
template <typename NestedBuilder>
|
||||
NestedBuilder build_member(member_id_t id)
|
||||
{
|
||||
static const MemberParams default_params;
|
||||
return build_member<NestedBuilder>(id, default_params);
|
||||
}
|
||||
|
||||
template <typename NestedBuilder>
|
||||
NestedBuilder build_member(
|
||||
member_id_t id,
|
||||
rti::xcdr::Stream::Memento& stream_memento)
|
||||
{
|
||||
static const MemberParams default_params;
|
||||
return build_member<NestedBuilder>(id, stream_memento, default_params);
|
||||
}
|
||||
|
||||
template <typename NestedBuilder>
|
||||
NestedBuilder build_member(
|
||||
member_id_t id,
|
||||
rti::xcdr::Stream::Memento& stream_memento,
|
||||
const MemberParams& params)
|
||||
{
|
||||
if (!begin_emheader(
|
||||
id,
|
||||
static_cast<RTIXCdrUnsignedLong>(
|
||||
detail::lc_code<NestedBuilder>::value()),
|
||||
params)) {
|
||||
return NestedBuilder();
|
||||
}
|
||||
|
||||
return AbstractBuilder::build_element_no_align<NestedBuilder>(
|
||||
stream_memento);
|
||||
}
|
||||
|
||||
unsigned char* finish_sample_impl()
|
||||
{
|
||||
RTI_FLAT_BUILDER_CHECK_VALID(return NULL);
|
||||
RTI_FLAT_BUILDER_CHECK_NOT_BOUND(return NULL);
|
||||
RTI_FLAT_BUILDER_CHECK_CAN_FINISH_SAMPLE(return NULL);
|
||||
|
||||
if (!stream().finish_dheader(dheader_position_)) {
|
||||
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(return NULL);
|
||||
}
|
||||
|
||||
unsigned char *return_sample = stream().buffer();
|
||||
invalidate();
|
||||
|
||||
return return_sample;
|
||||
}
|
||||
|
||||
// Same as finish_impl() but it doesn't throw exceptions or return an Offset
|
||||
void finish_untyped_impl()
|
||||
{
|
||||
if (is_valid() && is_nested()) {
|
||||
stream().finish_dheader(dheader_position_);
|
||||
AbstractBuilder::finish_untyped_impl();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename OffsetType>
|
||||
OffsetType finish_impl()
|
||||
{
|
||||
RTI_FLAT_BUILDER_CHECK_VALID(return OffsetType());
|
||||
RTI_FLAT_BUILDER_CHECK_CAN_FINISH(return OffsetType());
|
||||
|
||||
stream().finish_dheader(dheader_position_);
|
||||
return AbstractBuilder::finish_impl<OffsetType>();
|
||||
}
|
||||
|
||||
public:
|
||||
// For internal use; need to use rti::flat::discard_sample(writer, builder)
|
||||
unsigned char * discard_sample_impl()
|
||||
{
|
||||
RTI_FLAT_BUILDER_CHECK_VALID(return NULL);
|
||||
RTI_FLAT_BUILDER_CHECK_NOT_BOUND(return NULL);
|
||||
RTI_FLAT_BUILDER_CHECK_CAN_FINISH_SAMPLE(return NULL);
|
||||
|
||||
unsigned char *buffer = this->buffer();
|
||||
invalidate();
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private:
|
||||
void finish_member() // override noexcept
|
||||
{
|
||||
if (!is_valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
finish_emheader();
|
||||
AbstractBuilder::finish_member();
|
||||
}
|
||||
|
||||
private:
|
||||
bool begin_emheader(
|
||||
member_id_t id,
|
||||
RTIXCdrUnsignedLong lc,
|
||||
const MemberParams& params)
|
||||
{
|
||||
RTI_FLAT_BUILDER_CHECK_NOT_BOUND(return false);
|
||||
|
||||
if (!stream().serialize_emheader(
|
||||
emheader_position_,
|
||||
id,
|
||||
lc,
|
||||
params.has_must_understand_bit())) {
|
||||
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(return false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void finish_emheader() // noexcept
|
||||
{
|
||||
if (emheader_position_ != NULL) {
|
||||
stream().finish_emheader(emheader_position_);
|
||||
emheader_position_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
// Create a nested builder
|
||||
AggregationBuilder(nested_tag_t, AbstractBuilder& parent, unsigned int alignment)
|
||||
: AbstractBuilder(nested_tag_t(), parent, alignment),
|
||||
dheader_position_(NULL),
|
||||
emheader_position_(NULL)
|
||||
{
|
||||
dheader_position_ = stream().serialize_dheader();
|
||||
if (dheader_position_ == NULL) {
|
||||
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(invalidate());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
char * dheader_position_;
|
||||
char * emheader_position_;
|
||||
};
|
||||
|
||||
// Specializes an AggregationBuilder to enforce that only one member can
|
||||
// be set at a time
|
||||
/**
|
||||
* @ingroup RTIFlatBuildersModule
|
||||
* @brief Base class of builders for user-defined mutable unions.
|
||||
*
|
||||
* Union builders can only add or build a single member.
|
||||
*
|
||||
* This class contains implementation details and doesn't add any public function
|
||||
* to AbstractBuilder.
|
||||
*/
|
||||
template <typename Discriminator>
|
||||
class UnionBuilder : public AggregationBuilder {
|
||||
protected:
|
||||
UnionBuilder()
|
||||
{
|
||||
}
|
||||
|
||||
// Create a new top-level builder
|
||||
UnionBuilder(
|
||||
unsigned char *initial_buffer,
|
||||
offset_t size,
|
||||
bool initialize_members) :
|
||||
AggregationBuilder(initial_buffer, size, initialize_members)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool add_primitive_member(member_id_t id, Discriminator disc, T value)
|
||||
{
|
||||
RTI_FLAT_BUILDER_CHECK_VALID(return false);
|
||||
|
||||
// Always start at the beginning and override any member or discriminator
|
||||
// that may have been set
|
||||
move_to_discriminator();
|
||||
rti::xcdr::Stream::Memento stream_memento(stream());
|
||||
|
||||
if (!add_discriminator(disc)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!AggregationBuilder::add_primitive_member(id, value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
stream_memento.discard(); // success: do not restore the current position
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename OffsetType>
|
||||
OffsetType add_member(member_id_t id, Discriminator disc)
|
||||
{
|
||||
RTI_FLAT_BUILDER_CHECK_VALID(return OffsetType());
|
||||
|
||||
move_to_discriminator();
|
||||
rti::xcdr::Stream::Memento stream_memento(stream());
|
||||
if (!add_discriminator(disc)) {
|
||||
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(return OffsetType());
|
||||
}
|
||||
|
||||
return AggregationBuilder::add_member<OffsetType>(id, stream_memento);
|
||||
}
|
||||
|
||||
template <typename NestedBuilder>
|
||||
NestedBuilder build_member(member_id_t id, Discriminator disc)
|
||||
{
|
||||
RTI_FLAT_BUILDER_CHECK_VALID(return NestedBuilder());
|
||||
|
||||
move_to_discriminator();
|
||||
rti::xcdr::Stream::Memento stream_memento(stream());
|
||||
add_discriminator(disc);
|
||||
|
||||
return AggregationBuilder::build_member<NestedBuilder>(id, stream_memento);
|
||||
}
|
||||
|
||||
// Create a nested builder
|
||||
UnionBuilder(nested_tag_t, AbstractBuilder& parent, unsigned int alignment)
|
||||
: AggregationBuilder(nested_tag_t(), parent, alignment)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
void move_to_discriminator()
|
||||
{
|
||||
stream().current_position(begin_position() + RTI_XCDR_DHEADER_SIZE);
|
||||
}
|
||||
|
||||
bool add_discriminator(Discriminator disc)
|
||||
{
|
||||
MemberParams params;
|
||||
params.discriminator = true;
|
||||
return AggregationBuilder::add_primitive_member(0, disc, params);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} }
|
||||
|
||||
#endif // RTI_DDS_FLAT_AGGREGATIONBUILDERS_HPP_
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
(c) Copyright, Real-Time Innovations, 2018.
|
||||
All rights reserved.
|
||||
|
||||
No duplications, whole or partial, manual or electronic, may be made
|
||||
without express written permission. Any such copies, or
|
||||
revisions thereof, must display this notice unaltered.
|
||||
This code contains trade secrets of Real-Time Innovations, Inc.
|
||||
*/
|
||||
|
||||
#ifndef RTI_DDS_FLAT_BUILDERHELPER_HPP_
|
||||
#define RTI_DDS_FLAT_BUILDERHELPER_HPP_
|
||||
|
||||
namespace rti { namespace flat {
|
||||
|
||||
template <typename T>
|
||||
class PrimitiveSequenceBuilder;
|
||||
|
||||
template <typename ElementBuilder>
|
||||
class MutableSequenceBuilder;
|
||||
|
||||
template <typename ElementOffset>
|
||||
class FinalSequenceBuilder;
|
||||
|
||||
template <typename ElementBuilder, unsigned int N>
|
||||
class MutableArrayBuilder;
|
||||
|
||||
class StringBuilder;
|
||||
|
||||
namespace detail {
|
||||
|
||||
//
|
||||
// lc_code:
|
||||
//
|
||||
|
||||
template <size_t PrimitiveSize>
|
||||
struct primitive_lc_code_helper;
|
||||
|
||||
template <>
|
||||
struct primitive_lc_code_helper<1> {
|
||||
enum {
|
||||
single = 0, // LC = 1 => member length is 1
|
||||
sequence = 5 // LC = 5 => member length is also NEXTINT
|
||||
};
|
||||
};
|
||||
|
||||
template <>
|
||||
struct primitive_lc_code_helper<2> {
|
||||
enum {
|
||||
single = 1, // LC = 1 => member length is 2
|
||||
sequence = 4 // LC = 4 => member length is provided as an additional int
|
||||
};
|
||||
};
|
||||
|
||||
template <>
|
||||
struct primitive_lc_code_helper<4> {
|
||||
enum {
|
||||
single = 2, // LC = 2 => member length is 4
|
||||
sequence = 6 // LC = 6 => member length is 4 * NEXTINT
|
||||
};
|
||||
};
|
||||
|
||||
template <>
|
||||
struct primitive_lc_code_helper<8> {
|
||||
enum {
|
||||
single = 3, // LC = 3 => member length is 8
|
||||
sequence = 7 // LC = 7 => member length is 8 * NEXTINT
|
||||
};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct primitive_lc_code {
|
||||
enum { value = primitive_lc_code_helper<sizeof(T)>::single };
|
||||
};
|
||||
|
||||
// In general, use LC = 5, for mutable aggregation types. Final aggregation
|
||||
// types don't use this 'lc_code' trait type. The other cases are specialized
|
||||
// after this.
|
||||
template <typename T, typename Enable = void>
|
||||
struct lc_code {
|
||||
static int value()
|
||||
{
|
||||
return 5; // LC = 5 => member length is provided in NEXTINT/
|
||||
}
|
||||
};
|
||||
|
||||
// For collections of complex types and enums, if we serialize the DHeader
|
||||
// (XCDR2 compliant behavior) we use LC = 5; if we don't (legacy RTI behavior)
|
||||
// we use LC = 4, since we codify the length in the member header.
|
||||
template <typename T>
|
||||
struct lc_code<MutableSequenceBuilder<T> > {
|
||||
static int value()
|
||||
{
|
||||
return is_dheader_required_in_non_primitive_collections() ? 5 : 4;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct lc_code<FinalSequenceBuilder<T> > {
|
||||
static int value()
|
||||
{
|
||||
return is_dheader_required_in_non_primitive_collections() ? 5 : 4;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, unsigned int N>
|
||||
struct lc_code<MutableArrayBuilder<T, N> > {
|
||||
static int value()
|
||||
{
|
||||
return is_dheader_required_in_non_primitive_collections() ? 5 : 4;
|
||||
}
|
||||
};
|
||||
|
||||
#if defined(RTI_FLAT_DATA_NO_EXCEPTIONS)
|
||||
|
||||
// Traditional C++: no support for std::is_enum<T>
|
||||
|
||||
template <typename T>
|
||||
struct lc_code<PrimitiveSequenceBuilder<T> > {
|
||||
static int value()
|
||||
{
|
||||
return primitive_lc_code_helper<sizeof(T)>::sequence;
|
||||
}
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
template <typename T>
|
||||
struct lc_code<
|
||||
PrimitiveSequenceBuilder<T>,
|
||||
typename std::enable_if<!std::is_enum<T>::value>::type> {
|
||||
static int value()
|
||||
{
|
||||
return primitive_lc_code_helper<sizeof(T)>::sequence;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct lc_code<
|
||||
PrimitiveSequenceBuilder<T>,
|
||||
typename std::enable_if<std::is_enum<T>::value>::type> {
|
||||
static int value()
|
||||
{
|
||||
return is_dheader_required_in_enum_collections()
|
||||
? 5
|
||||
: primitive_lc_code_helper<sizeof(T)>::sequence;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
//
|
||||
// Initialization of final types
|
||||
//
|
||||
|
||||
template <typename Offset>
|
||||
struct final_offset_initializer {
|
||||
|
||||
static bool initialize(Offset& offset)
|
||||
{
|
||||
return RTI_XCDR_TRUE == RTIXCdrFlatData_initializeSample(
|
||||
(char *) offset.get_buffer(),
|
||||
offset.get_buffer_size(),
|
||||
rti::xcdr::type_programs<rti::flat::Sample<Offset> >::get());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// This will be specialized for enums in generated code (traditional C++)
|
||||
// and for enum classes in FlatDataTraits.hpp (modern C++)
|
||||
template <typename T, typename Enable = void>
|
||||
struct default_primitive_value {
|
||||
static T get()
|
||||
{
|
||||
return (T) 0;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, unsigned int N>
|
||||
struct final_offset_initializer<rti::flat::PrimitiveArrayOffset<T, N> > {
|
||||
|
||||
static bool initialize(rti::flat::PrimitiveArrayOffset<T, N>& array)
|
||||
{
|
||||
T default_value = default_primitive_value<T>::get();
|
||||
if (default_value == (T) 0) { // don't need to deal with endianness
|
||||
memset(array.get_buffer(), 0, array.get_buffer_size());
|
||||
} else {
|
||||
for (unsigned int i = 0; i < array.element_count(); i++) {
|
||||
array.set_element(i, default_value);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, unsigned int N>
|
||||
struct final_offset_initializer<rti::flat::FinalAlignedArrayOffset<T, N> > {
|
||||
|
||||
static bool initialize(rti::flat::FinalAlignedArrayOffset<T, N>& array)
|
||||
{
|
||||
for (unsigned int i = 0; i < N; i++) {
|
||||
T offset = array.get_element(i);
|
||||
if (offset.is_null()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!final_offset_initializer<T>::initialize(offset)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} } }
|
||||
|
||||
#endif // RTI_DDS_FLAT_BUILDERHELPER_HPP_
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
(c) Copyright, Real-Time Innovations, 2018.
|
||||
All rights reserved.
|
||||
|
||||
No duplications, whole or partial, manual or electronic, may be made
|
||||
without express written permission. Any such copies, or
|
||||
revisions thereof, must display this notice unaltered.
|
||||
This code contains trade secrets of Real-Time Innovations, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef RTI_DDS_FLAT_EXCEPTIONHELPER_HPP_
|
||||
#define RTI_DDS_FLAT_EXCEPTIONHELPER_HPP_
|
||||
|
||||
//
|
||||
// Enable debug assertions (RTI_FLAT_ASSERT) in debug mode
|
||||
//
|
||||
#if !defined(NDEBUG)
|
||||
#define RTI_FLAT_DATA_ENABLE_DEBUG_ASSERTIONS
|
||||
#endif
|
||||
|
||||
//
|
||||
// Error handling without C++ exceptions (traditional C++ and Micro C++ APIs)
|
||||
//
|
||||
#ifdef RTI_FLAT_DATA_NO_EXCEPTIONS
|
||||
// TODO: use MSG
|
||||
#ifdef RTI_FLAT_DATA_ENABLE_DEBUG_ASSERTIONS
|
||||
#define RTI_FLAT_ASSERT(COND, ACTION) \
|
||||
if (!(COND)) { \
|
||||
RTIXCdrFlatData_logPreconditionError(__FILE__, __LINE__, "Assertion failure: " #COND); \
|
||||
ACTION; \
|
||||
}
|
||||
#else
|
||||
#define RTI_FLAT_ASSERT(COND, ACTION)
|
||||
#endif
|
||||
|
||||
// CR decission: Log error messages. Export function for each log kind (serialize, deserialize, precondition, skip)
|
||||
// pass type/field (if possible) to serialization errors
|
||||
#define RTI_FLAT_OFFSET_CHECK_NOT_NULL(ACTION) \
|
||||
if (this->is_null()) { \
|
||||
RTIXCdrFlatData_logPreconditionError(__FILE__, __LINE__, "null offset"); \
|
||||
ACTION; \
|
||||
}
|
||||
|
||||
#define RTI_FLAT_OFFSET_PRECONDITION_ERROR(MSG, ACTION) \
|
||||
RTIXCdrFlatData_logPreconditionError(__FILE__, __LINE__, MSG); \
|
||||
ACTION
|
||||
|
||||
#define RTI_FLAT_CHECK_PRECONDITION(COND, ACTION) \
|
||||
if (!(COND)) { \
|
||||
RTIXCdrFlatData_logPreconditionError(__FILE__, __LINE__, "Precondition failure: " #COND); \
|
||||
ACTION; \
|
||||
}
|
||||
|
||||
#define RTI_FLAT_BUILDER_CHECK_NOT_BOUND(ACTION) \
|
||||
if (this->bind_position_) { \
|
||||
RTIXCdrFlatData_logPreconditionError(__FILE__, __LINE__, "Builder is currently bound - call finish() first"); \
|
||||
ACTION; \
|
||||
}
|
||||
|
||||
#define RTI_FLAT_BUILDER_CHECK_CAN_FINISH(ACTION) \
|
||||
if (!this->is_nested()) { \
|
||||
RTIXCdrFlatData_logPreconditionError(__FILE__, __LINE__, "This is not a member builder; call finish_sample() instead"); \
|
||||
ACTION; \
|
||||
}
|
||||
|
||||
#define RTI_FLAT_BUILDER_CHECK_CAN_FINISH_SAMPLE(ACTION) \
|
||||
if (this->is_nested()) { \
|
||||
RTIXCdrFlatData_logPreconditionError(__FILE__, __LINE__, "This is a member builder; call finish() instead"); \
|
||||
ACTION; \
|
||||
}
|
||||
|
||||
#define RTI_FLAT_BUILDER_CHECK_VALID(ACTION) \
|
||||
if (!this->is_valid()) { \
|
||||
RTIXCdrFlatData_logPreconditionError(__FILE__, __LINE__, "This Builder is not valid"); \
|
||||
ACTION; \
|
||||
}
|
||||
|
||||
#define RTI_FLAT_BUILDER_CHECK_CREATE_BUILDER(BUILDER, ACTION) \
|
||||
if (!BUILDER.is_valid()) { \
|
||||
this->set_failure(); \
|
||||
RTIXCdrFlatData_logCreationError(__FILE__, __LINE__, "Builder"); \
|
||||
ACTION; \
|
||||
}
|
||||
|
||||
#define RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(ACTION) \
|
||||
this->set_failure(); \
|
||||
RTIXCdrFlatData_logBuilderOutOfResources(__FILE__, __LINE__); \
|
||||
ACTION
|
||||
|
||||
#define RTI_FLAT_BUILDER_PRECONDITION_ERROR(MSG, ACTION) \
|
||||
this->set_failure(); \
|
||||
RTIXCdrFlatData_logPreconditionError(__FILE__, __LINE__, MSG); \
|
||||
ACTION
|
||||
|
||||
//
|
||||
// Error handling with C++ exceptions (modern C++ API)
|
||||
//
|
||||
#else // !RTI_FLAT_DATA_NO_EXCEPTIONS
|
||||
|
||||
#ifdef RTI_FLAT_DATA_ENABLE_DEBUG_ASSERTIONS
|
||||
#define RTI_FLAT_ASSERT(COND, ACTION) \
|
||||
if (!(COND)) throw dds::core::PreconditionNotMetError("Assertion failure: " #COND)
|
||||
#else
|
||||
#define RTI_FLAT_ASSERT(COND, ACTION)
|
||||
#endif
|
||||
|
||||
#define RTI_FLAT_OFFSET_CHECK_NOT_NULL(ACTION) \
|
||||
if (this->is_null()) throw dds::core::NullReferenceError("null offset")
|
||||
|
||||
#define RTI_FLAT_OFFSET_PRECONDITION_ERROR(MSG, ACTION) \
|
||||
throw dds::core::PreconditionNotMetError(MSG)
|
||||
|
||||
#define RTI_FLAT_CHECK_PRECONDITION(COND, ACTION) \
|
||||
if (!(COND)) throw dds::core::PreconditionNotMetError("Precondition failure: " #COND)
|
||||
|
||||
#define RTI_FLAT_BUILDER_CHECK_NOT_BOUND(ACTION) \
|
||||
if (bind_position_) \
|
||||
throw dds::core::PreconditionNotMetError( \
|
||||
"Builder is currently bound - call finish() first")
|
||||
|
||||
#define RTI_FLAT_BUILDER_CHECK_CAN_FINISH(ACTION) \
|
||||
if (!is_nested()) \
|
||||
throw dds::core::PreconditionNotMetError( \
|
||||
"This is not a member builder; call finish_sample() instead")
|
||||
|
||||
#define RTI_FLAT_BUILDER_CHECK_CAN_FINISH_SAMPLE(ACTION) \
|
||||
if (is_nested()) \
|
||||
throw dds::core::PreconditionNotMetError( \
|
||||
"This is a member builder; call finish() instead")
|
||||
|
||||
#define RTI_FLAT_BUILDER_CHECK_VALID(ACTION) \
|
||||
if (!is_valid()) \
|
||||
throw dds::core::PreconditionNotMetError("This Builder is not valid")
|
||||
|
||||
// With exceptions, nothing to check (exception would have been thrown)
|
||||
#define RTI_FLAT_BUILDER_CHECK_CREATE_BUILDER(BUILDER, ACTION)
|
||||
|
||||
#define RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(ACTION) \
|
||||
throw dds::core::OutOfResourcesError("builder reached end of buffer")
|
||||
|
||||
#define RTI_FLAT_BUILDER_PRECONDITION_ERROR(MSG, ACTION) \
|
||||
throw dds::core::PreconditionNotMetError(MSG)
|
||||
|
||||
|
||||
#endif // RTI_FLAT_DATA_NO_EXCEPTIONS
|
||||
|
||||
//
|
||||
// Error checking (other than assertions) in release mode is enabled unless
|
||||
// the sources are compiled with -DRTI_FLAT_DATA_DISABLE_ERROR_CHECKING
|
||||
//
|
||||
// In that case, most error checks that deal with preconditions due to incorrect
|
||||
// usage of the API are disabled in release mode. This would lead to undefined
|
||||
// behavior in case of bad usage, but can improve performance.
|
||||
//
|
||||
#if defined(RTI_FLAT_DATA_ENABLE_DEBUG_ASSERTIONS) \
|
||||
|| !defined(RTI_FLAT_DATA_DISABLE_ERROR_CHECKING)
|
||||
#define RTI_FLAT_DATA_ENABLE_ERROR_CHECKING
|
||||
#endif
|
||||
|
||||
#ifndef RTI_FLAT_DATA_ENABLE_ERROR_CHECKING
|
||||
|
||||
#define RTI_FLAT_OFFSET_CHECK_NOT_NULL(ACTION)
|
||||
#define RTI_FLAT_ASSERT(COND, ACTION)
|
||||
#define RTI_FLAT_CHECK_PRECONDITION(COND, ACTION)
|
||||
#define RTI_FLAT_BUILDER_CHECK_NOT_BOUND(ACTION)
|
||||
#define RTI_FLAT_BUILDER_CHECK_CAN_FINISH(ACTION)
|
||||
#define RTI_FLAT_BUILDER_CHECK_CAN_FINISH_SAMPLE(ACTION)
|
||||
#define RTI_FLAT_BUILDER_CHECK_VALID(ACTION)
|
||||
#define RTI_FLAT_BUILDER_CHECK_CREATE_BUILDER(BUILDER, ACTION)
|
||||
|
||||
#endif // RTI_FLAT_DATA_ENABLE_ERROR_CHECKING
|
||||
|
||||
namespace rti { namespace flat { namespace detail {
|
||||
|
||||
} } }
|
||||
|
||||
#endif // RTI_DDS_FLAT_EXCEPTIONHELPER_HPP_
|
||||
|
||||
@@ -0,0 +1,451 @@
|
||||
/*
|
||||
(c) Copyright, Real-Time Innovations, 2018.
|
||||
All rights reserved.
|
||||
|
||||
No duplications, whole or partial, manual or electronic, may be made
|
||||
without express written permission. Any such copies, or
|
||||
revisions thereof, must display this notice unaltered.
|
||||
This code contains trade secrets of Real-Time Innovations, Inc.
|
||||
*/
|
||||
|
||||
#ifndef RTI_DDS_FLAT_FLATSAMPLE_HPP_
|
||||
#define RTI_DDS_FLAT_FLATSAMPLE_HPP_
|
||||
|
||||
// Note: FlatSampleImpl.hpp needs to be included in addition to this header.
|
||||
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
|
||||
#include "xcdr/xcdr_stream_impl.h"
|
||||
#include "xcdr/xcdr_flat_data.h"
|
||||
#include "xcdr/xcdr_interpreter.h"
|
||||
|
||||
#include "rti/xcdr/Interpreter.hpp" // for type_programs
|
||||
|
||||
/**
|
||||
* @defgroup RTIFlatSampleModule FlatData Samples
|
||||
* @ingroup RTIFlatDataModule
|
||||
* @brief A Sample represents an instance of the IDL topic-type and contains
|
||||
* the data in serialized format.
|
||||
*
|
||||
* All FlatData topic-types are instantiations of rti::flat::Sample.
|
||||
* This documentation uses the following three example types to illustrate the
|
||||
* different ways FlatData IDL types map to C++:
|
||||
* - \ref MyFlatFinal, the type generated for a final IDL struct
|
||||
* - \ref MyFlatMutable, the type generated for a mutable IDL struct
|
||||
* - \ref MyFlatUnion, the type generated for an IDL union
|
||||
*/
|
||||
|
||||
namespace rti { namespace flat {
|
||||
|
||||
struct fixed_size_type_tag_t {};
|
||||
struct variable_size_type_tag_t {};
|
||||
|
||||
typedef RTIXCdrUnsignedLong offset_t;
|
||||
typedef RTIXCdrUnsignedLong member_id_t;
|
||||
|
||||
// The untemplated base of Sample<T>
|
||||
class SampleBase {
|
||||
public:
|
||||
|
||||
unsigned char* get_buffer()
|
||||
{
|
||||
return &data[0];
|
||||
}
|
||||
|
||||
const unsigned char* get_buffer() const
|
||||
{
|
||||
return &data[0];
|
||||
}
|
||||
|
||||
unsigned char* get_root_buffer()
|
||||
{
|
||||
return get_buffer() + RTI_XCDR_ENCAPSULATION_SIZE;
|
||||
}
|
||||
|
||||
/*i
|
||||
* @brief Provides the address of the top-level type
|
||||
*/
|
||||
const unsigned char* get_root_buffer() const
|
||||
{
|
||||
return get_buffer() + RTI_XCDR_ENCAPSULATION_SIZE;
|
||||
}
|
||||
|
||||
void initialize_stream(
|
||||
RTIXCdrStream& stream,
|
||||
offset_t offset,
|
||||
offset_t serialized_size) const
|
||||
{
|
||||
RTIXCdrFlatData_initializeStream(
|
||||
&stream,
|
||||
const_cast<unsigned char*>(get_buffer()),
|
||||
offset,
|
||||
serialized_size);
|
||||
}
|
||||
|
||||
protected: // xlc compiler fails when the default constructor is private
|
||||
#if !defined(RTI_FLAT_DATA_CXX11_DELETED_FUNCTIONS)
|
||||
SampleBase() {} // Samples can only be created via Sample::create_data
|
||||
SampleBase(const SampleBase&) {}
|
||||
SampleBase& operator=(const SampleBase&);
|
||||
#else
|
||||
SampleBase() = delete;
|
||||
SampleBase(const SampleBase&) = delete;
|
||||
SampleBase& operator=(const SampleBase&) = delete;
|
||||
#endif
|
||||
|
||||
// The actual size of data (and therefore, the Sample object) is variable,
|
||||
// and equal to the serialized size of the IDL Sample
|
||||
unsigned char data[1];
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup RTIFlatSampleModule
|
||||
* @brief The generic definition of FlatData topic-types
|
||||
*
|
||||
* @tparam OffsetType The Offset to the beginning of the data Sample (the root),
|
||||
* for example MyFlatFinalOffset, or MyFlatMutableOffset.
|
||||
*
|
||||
* For a FlatData IDL type, \nddsgen generates an instantiation of this class,
|
||||
* such as MyFlatFinal or MyFlatMutable. That's the <b>topic-type</b> used to
|
||||
* declare a \idref_Topic and write or read FlatData samples.
|
||||
*
|
||||
* A FlatData Sample owns an inline buffer that contains the serialized data.
|
||||
* To access the Sample data members we use \ref RTIFlatOffsetsModule "Offsets",
|
||||
* iterators that point to the position of a member in the Sample buffer. The
|
||||
* root() is the Offset to the top-level element (for example MyFlatMutableOffset).
|
||||
* From there MyFlatMutableOffset's member functions provide access to its
|
||||
* members.
|
||||
*
|
||||
* The method to create a Sample varies depending on whether the type is final
|
||||
* or mutable.
|
||||
*
|
||||
* Samples of <b>final</b> FlatData types always have the same size, and
|
||||
* can be obtained directly with \idref_FooDataWriter_get_loan().
|
||||
*
|
||||
* Samples of <b>mutable</b> FlatData types need to be "built" using the Builder
|
||||
* returned by rti::flat::build_data() (MyFlatMutableBuilder in this example).
|
||||
* A Builder allows adding each member individually in any order, and sizing
|
||||
* sequences as needed.
|
||||
*
|
||||
* See \ref PublishingFlatData for code snippets.
|
||||
*
|
||||
* Samples created with \idref_FooDataWriter_get_loan() or rti::flat::build_data()
|
||||
* are DataWriter-managed and do not need to be explicitly deleted. See
|
||||
* \idref_FooDataWriter_get_loan() for an explanation of the lifecycle of
|
||||
* DataWriter-managed samples.
|
||||
*
|
||||
* Samples received with a \idref_FooDataReader can be examined by obtaining the
|
||||
* root(), but cannot be modified. See \ref SubscribingFlatData.
|
||||
*
|
||||
* FlatData samples are not value types. They don't provide copy constructors
|
||||
* or assignment operators, because of their definition as an inline buffer that
|
||||
* contains the serialized Sample at all times. A Sample can be cloned with
|
||||
* the static function clone(). A cloned Sample is unmanaged and has to be
|
||||
* deleted with the static function delete_data().
|
||||
*/
|
||||
template <typename OffsetType>
|
||||
class Sample : public SampleBase {
|
||||
public:
|
||||
/**
|
||||
* @brief The related Offset type
|
||||
*
|
||||
* This is the template parameter, OffsetType
|
||||
*/
|
||||
typedef OffsetType Offset;
|
||||
|
||||
/**
|
||||
* @brief The related read-only Offset type
|
||||
*
|
||||
* For example, MyFlatMutableOffset::ConstOffset.
|
||||
*/
|
||||
typedef typename OffsetType::ConstOffset ConstOffset;
|
||||
|
||||
/**
|
||||
* @brief Provides the Offset to the top-level type
|
||||
*
|
||||
* @return An Offset that allows reading and modifying the members of this
|
||||
* Sample, for example MyFlatFinalOffset or MyFlatMutableOffset.
|
||||
*
|
||||
* This overload allows modifying the Sample.
|
||||
*
|
||||
* Example:
|
||||
* \code
|
||||
* MyFlatFinal *my_sample = ...; // for example, created with DataWriter::get_loan()
|
||||
* MyFlatFinalOffset my_sample_root = my_sample->root();
|
||||
* my_sample_root.my_primitive(33);
|
||||
* auto my_member_offset = my_sample_root.my_complex();
|
||||
* // ... modify my_member_offset
|
||||
* \endcode
|
||||
*/
|
||||
Offset root()
|
||||
{
|
||||
return root_impl(typename Offset::offset_kind());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Provides the Offset to the top-level type
|
||||
*
|
||||
* @return An Offset that allows reading the members of this Sample.
|
||||
* For example, MyFlatFinalOffset::ConstOffset or MyFlatMutableOffset::ConstOffset.
|
||||
*
|
||||
* This overload doesn't allow modifying the Sample.
|
||||
*
|
||||
* Example:
|
||||
* \code
|
||||
* const MyFlatMutable *my_sample = ...; // for example, read from a DataReader
|
||||
* MyFlatMutableOffset::ConstOffset my_sample_root = my_sample->root();
|
||||
* std::cout << my_sample_root.my_primitive() << std::endl;
|
||||
* auto my_member_offset = my_sample_root.my_mutable_seq();
|
||||
* // ... read my_member_offset
|
||||
* \endcode
|
||||
*/
|
||||
ConstOffset root() const
|
||||
{
|
||||
return root_impl(typename ConstOffset::offset_kind());
|
||||
}
|
||||
|
||||
/** @private
|
||||
* @brief Returns the endianness in which this Sample is serialized
|
||||
*/
|
||||
RTIXCdrEndian endian() const
|
||||
{
|
||||
RTIXCdrStream tmp_stream;
|
||||
initialize_stream(tmp_stream, 0, RTI_XCDR_ENCAPSULATION_SIZE);
|
||||
return tmp_stream._endian;
|
||||
}
|
||||
|
||||
/** @private
|
||||
* @brief Casts a byte buffer into a Sample of this type
|
||||
*/
|
||||
static Sample<OffsetType>* from_buffer(unsigned char *buffer)
|
||||
{
|
||||
return reinterpret_cast<Sample<OffsetType>*>(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates an unmanaged FlatData Sample
|
||||
*
|
||||
* @note In general on the publication side it is recommended to create
|
||||
* DataWriter-managed samples using \idref_FooDataWriter_get_loan
|
||||
* (for final types) and rti::flat::build_data() (for mutable types).
|
||||
*
|
||||
* If the topic-type is final, the returned Sample can be initialized
|
||||
* by obtaining the root().
|
||||
*
|
||||
* If the topic-type is mutable, the resulting Sample has no members, and
|
||||
* root() returns a \ref rti::flat::OffsetBase::is_null "null" Offset.
|
||||
* The result of create_data() must be used to
|
||||
* create a Builder. This is a rare use case, and rti::flat::build_data()
|
||||
* should be used in most situations.
|
||||
*
|
||||
* In all cases, the returned Sample must be explicitly deleted with
|
||||
* delete_data().
|
||||
*/
|
||||
static Sample<OffsetType>* create_data(); // impl in FlatSampleImpl.hpp
|
||||
|
||||
/**
|
||||
* @brief Clones a Sample, creating an unmanaged Sample
|
||||
*
|
||||
* @warning When this Sample has been created with \idref_FooDataWriter_get_loan
|
||||
* or rti::flat::build_data(), the clone this function creates cannot be
|
||||
* used in \idref_FooDataWriter_write, because the DataWriter is managing
|
||||
* the lifecycle of its samples.
|
||||
*
|
||||
* Creates a new Sample and copies the underlying serialized buffer.
|
||||
*
|
||||
* delete_data() must be called to release the Sample.
|
||||
*/
|
||||
Sample<OffsetType>* clone() const
|
||||
{
|
||||
unsigned char *buffer = RTIXCdrFlatData_cloneSample(
|
||||
get_buffer(),
|
||||
sample_size());
|
||||
|
||||
if (buffer == NULL) {
|
||||
#ifndef RTI_FLAT_DATA_NO_EXCEPTIONS
|
||||
throw std::bad_alloc();
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
return reinterpret_cast<rti::flat::Sample<OffsetType> *>(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Releases an unmanaged Sample
|
||||
*
|
||||
* Unmanaged samples are those created with create_data() or clone().
|
||||
*
|
||||
* @pre \p sample cannot have been created with \idref_FooDataWriter_get_loan
|
||||
* or rti::flat::build_data().
|
||||
*
|
||||
* @post \p sample becomes invalid and shouldn't be used
|
||||
*/
|
||||
static void delete_data(rti::flat::Sample<OffsetType>* sample)
|
||||
{
|
||||
RTIXCdrFlatData_deleteSample(sample);
|
||||
}
|
||||
|
||||
/** @private
|
||||
*/
|
||||
offset_t buffer_size() const
|
||||
{
|
||||
return sample_size() + RTI_XCDR_ENCAPSULATION_SIZE;
|
||||
}
|
||||
|
||||
/** @private
|
||||
*/
|
||||
offset_t sample_size() const
|
||||
{
|
||||
return sample_size_impl(typename OffsetType::offset_kind());
|
||||
}
|
||||
|
||||
private:
|
||||
offset_t sample_size_impl(fixed_size_type_tag_t) const
|
||||
{
|
||||
return OffsetType::serialized_size(0);
|
||||
}
|
||||
|
||||
offset_t sample_size_impl(variable_size_type_tag_t) const
|
||||
{
|
||||
return RTIXCdrFlatSample_getMutableSampleSize(
|
||||
get_buffer(),
|
||||
RTI_XCDR_ENCAPSULATION_SIZE);
|
||||
}
|
||||
|
||||
Offset root_impl(fixed_size_type_tag_t)
|
||||
{
|
||||
return Offset(this, RTI_XCDR_ENCAPSULATION_SIZE);
|
||||
}
|
||||
|
||||
Offset root_impl(variable_size_type_tag_t)
|
||||
{
|
||||
return Offset(this, RTI_XCDR_ENCAPSULATION_SIZE, sample_size());
|
||||
}
|
||||
|
||||
ConstOffset root_impl(fixed_size_type_tag_t) const
|
||||
{
|
||||
return ConstOffset(this, RTI_XCDR_ENCAPSULATION_SIZE);
|
||||
}
|
||||
|
||||
ConstOffset root_impl(variable_size_type_tag_t) const
|
||||
{
|
||||
return ConstOffset(this, RTI_XCDR_ENCAPSULATION_SIZE, sample_size());
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct flat_type_traits;
|
||||
|
||||
} }
|
||||
|
||||
#ifdef DOXYGEN_DOCUMENTATION_ONLY
|
||||
|
||||
/**
|
||||
* @ingroup RTIFlatSampleModule
|
||||
* @brief Represents an arbitrary user-defined FlatData final IDL struct
|
||||
*
|
||||
* This documentation uses the following example IDL definition of MyFlatFinal:
|
||||
*
|
||||
* \code
|
||||
* @language_binding(FLAT_DATA)
|
||||
* @final
|
||||
* struct MyFlatFinal {
|
||||
* long my_primitive;
|
||||
* FlatFinalBar my_complex; // Another arbitrary final FlatData type
|
||||
* long my_primitive_array[10];
|
||||
* FlatFinalBar my_complex_array[10];
|
||||
* };
|
||||
* \endcode
|
||||
*
|
||||
* For this type, \nddsgen generates:
|
||||
* - MyFlatFinal (this instantiation of Sample),
|
||||
* - MyFlatFinalOffset.
|
||||
*
|
||||
* Note that, as a final FlatData type, MyFlatFinal can only contain
|
||||
* fixed-size types, such as primitives, other final FlatData structs, and arrays
|
||||
* of fixed-size types.
|
||||
*
|
||||
* Samples of MyFlatFinal are created with \idref_FooDataWriter_get_loan.
|
||||
* After creating a final Sample, modify its values using the MyFlatFinalOffset
|
||||
* returned by \ref rti::flat::Sample::root "root()".
|
||||
*/
|
||||
typedef rti::flat::Sample<MyFlatFinalOffset> MyFlatFinal;
|
||||
|
||||
/**
|
||||
* @ingroup RTIFlatSampleModule
|
||||
* @brief Represents an arbitrary user-defined flat mutable IDL struct
|
||||
*
|
||||
* This documentation uses the following example IDL definition of MyFlatMutable:
|
||||
*
|
||||
* \code
|
||||
* @language_binding(FLAT_DATA)
|
||||
* @mutable
|
||||
* struct MyFlatMutable {
|
||||
* long my_primitive;
|
||||
* @optional long my_optional_primitive;
|
||||
* long my_primitive_array[10];
|
||||
* sequence<long, 10> my_primitive_seq;
|
||||
*
|
||||
* MyFlatFinal my_final;
|
||||
* MyFlatFinal my_final_array[10];
|
||||
* sequence<MyFlatFinal, 10> my_final_seq;
|
||||
*
|
||||
* FlatMutableBar my_mutable;
|
||||
* FlatMutableBar my_mutable_array[10];
|
||||
* sequence<FlatMutableBar, 10> my_mutable_seq;
|
||||
*
|
||||
* string<255> my_string;
|
||||
* sequence<string<255>, 10> my_string_seq;
|
||||
* };
|
||||
* \endcode
|
||||
*
|
||||
* For this type, \nddsgen generates:
|
||||
* - MyFlatMutable (this instantiation of Sample),
|
||||
* - MyFlatMutableOffset,
|
||||
* - MyFlatMutableBuilder.
|
||||
*
|
||||
* As a mutable FlatData type, MyFlatMutable is not restricted to fixed-size
|
||||
* members. Samples of MyFlatMutable are created with rti::flat::build_data(), which
|
||||
* returns a MyFlatMutableBuilder. Once built, a mutable Sample can't change
|
||||
* in size, but the value of members that already exist can be changed using
|
||||
* the MyFlatMutableOffset returned by \ref rti::flat::Sample::root "root()".
|
||||
*/
|
||||
typedef rti::flat::Sample<MyFlatMutableOffset> MyFlatMutable;
|
||||
|
||||
/**
|
||||
* @ingroup RTIFlatSampleModule
|
||||
* @brief Represents an arbitrary user-defined flat mutable IDL union
|
||||
*
|
||||
* This documentation uses the following example IDL definition of MyFlatUnion:
|
||||
*
|
||||
* \code
|
||||
* @language_binding(FLAT_DATA)
|
||||
* @mutable
|
||||
* union MyFlatUnion switch (long) {
|
||||
* case 0:
|
||||
* long my_primitive;
|
||||
* case 1:
|
||||
* case 2:
|
||||
* MyFlatMutable my_mutable;
|
||||
* case 3:
|
||||
* MyFlatFinal my_final;
|
||||
* };
|
||||
* \endcode
|
||||
*
|
||||
* @note FlatData unions can only be <b>mutable</b> since unions are,
|
||||
* by definition, variable-size types.
|
||||
*
|
||||
* For this type, \nddsgen generates:
|
||||
* - MyFlatUnion (this instantiation of Sample),
|
||||
* - MyFlatUnionOffset,
|
||||
* - MyFlatUnionBuilder.
|
||||
*
|
||||
*/
|
||||
typedef rti::flat::Sample<MyFlatUnionOffset> MyFlatUnion;
|
||||
#endif
|
||||
|
||||
#endif // RTI_DDS_FLAT_FLATSAMPLE_HPP_
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
(c) Copyright, Real-Time Innovations, 2018.
|
||||
All rights reserved.
|
||||
|
||||
No duplications, whole or partial, manual or electronic, may be made
|
||||
without express written permission. Any such copies, or
|
||||
revisions thereof, must display this notice unaltered.
|
||||
This code contains trade secrets of Real-Time Innovations, Inc.
|
||||
*/
|
||||
|
||||
#ifndef RTI_DDS_FLAT_FLATSAMPLEIMPL_HPP_
|
||||
#define RTI_DDS_FLAT_FLATSAMPLEIMPL_HPP_
|
||||
|
||||
// This file includes the implementation of some templates that need to
|
||||
// appear after some types are defined in the generated code. This is a
|
||||
// a restriction on some platforms such as pentiumInty11.pcx64 and other Intys,
|
||||
// sparcSol2.10gcc3.4.2.
|
||||
|
||||
// For example, type_programs<T> needs to be defined (as an specialization
|
||||
// for T) before its used in create_data().
|
||||
|
||||
#include "rti/flat/FlatSample.hpp"
|
||||
|
||||
namespace rti { namespace flat {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename OffsetType>
|
||||
Sample<OffsetType>* create_data_impl(fixed_size_type_tag_t)
|
||||
{
|
||||
unsigned char *buffer = RTIXCdrFlatData_createSampleFinal(
|
||||
OffsetType::serialized_size(0),
|
||||
rti::xcdr::type_programs<Sample<OffsetType> >::get());
|
||||
|
||||
if (buffer == NULL) {
|
||||
#ifndef RTI_FLAT_DATA_NO_EXCEPTIONS
|
||||
throw std::bad_alloc();
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
return reinterpret_cast<rti::flat::Sample<OffsetType> *>(buffer);
|
||||
}
|
||||
|
||||
template <typename OffsetType>
|
||||
Sample<OffsetType>* create_data_impl(variable_size_type_tag_t)
|
||||
{
|
||||
unsigned char *buffer = RTIXCdrFlatData_createSampleMutable(
|
||||
rti::xcdr::type_programs<Sample<OffsetType> >::get());
|
||||
|
||||
if (buffer == NULL) {
|
||||
#ifndef RTI_FLAT_DATA_NO_EXCEPTIONS
|
||||
throw std::bad_alloc();
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
return reinterpret_cast<rti::flat::Sample<OffsetType> *>(buffer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Declared in FlatSample.hpp
|
||||
template <typename OffsetType>
|
||||
Sample<OffsetType>* Sample<OffsetType>::create_data()
|
||||
{
|
||||
return detail::create_data_impl<OffsetType>(
|
||||
typename OffsetType::offset_kind());
|
||||
}
|
||||
|
||||
} }
|
||||
|
||||
#endif // RTI_DDS_FLAT_FLATSAMPLEIMPL_HPP_
|
||||
|
||||
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
(c) Copyright, Real-Time Innovations, 2018.
|
||||
All rights reserved.
|
||||
|
||||
No duplications, whole or partial, manual or electronic, may be made
|
||||
without express written permission. Any such copies, or
|
||||
revisions thereof, must display this notice unaltered.
|
||||
This code contains trade secrets of Real-Time Innovations, Inc.
|
||||
*/
|
||||
|
||||
#ifndef RTI_DDS_FLAT_FLATTYPETRAITS_HPP_
|
||||
#define RTI_DDS_FLAT_FLATTYPETRAITS_HPP_
|
||||
|
||||
#include "rti/flat/Offset.hpp"
|
||||
#include "rti/flat/SequenceOffsets.hpp"
|
||||
#include "rti/flat/AggregationBuilders.hpp"
|
||||
|
||||
namespace rti { namespace flat {
|
||||
|
||||
|
||||
/**
|
||||
* @ingroup RTIFlatSampleModule
|
||||
* @brief Given a Sample, an Offset or a Builder, it allows obtaining the other types.
|
||||
*
|
||||
* @tparam T One of the following:
|
||||
* \li A Sample type, such as \ref MyFlatMutable
|
||||
* \li An Offset type, such as MyFlatMutableOffset
|
||||
* \li A Builder type, such as MyFlatMutableBuilder
|
||||
*
|
||||
* <p>Given \p T, this type provides the following typedefs:
|
||||
* \li \p flat_type_traits<T>::offset, T's related offset type
|
||||
* (undefined if T itself is an Offset)
|
||||
* \li \p flat_type_traits<T>::builder, T's related builder type
|
||||
* (undefined if T itself is a Builder, or the topic-type is not mutable)
|
||||
* \li \p flat_type_traits<T>::flat_type, T's related Sample type
|
||||
* (undefined if T itself is a Sample type)
|
||||
* \li \p flat_type_traits<T>::plain_type, T's equivalent definition as a plain
|
||||
* (non-FlatData) type.
|
||||
*
|
||||
* For example, for \p T = \ref MyFlatMutable, flat_type_traits is defined as follows:
|
||||
*
|
||||
* \code
|
||||
* template <>
|
||||
* struct flat_type_traits<MyFlatMutable> {
|
||||
* typedef MyFlatMutablePlainHelper plain_type;
|
||||
* typedef MyFlatMutableOffset offset;
|
||||
* typedef MyFlatMutableBuilder builder;
|
||||
* };
|
||||
* \endcode
|
||||
*
|
||||
* Or if \p T = MyFlatMutableOffset:
|
||||
*
|
||||
* \code
|
||||
* template <>
|
||||
* struct flat_type_traits<MyFlatMutableOffset> {
|
||||
* typedef MyFlatMutable flat_type;
|
||||
* typedef MyFlatMutablePlainHelper plain_type;
|
||||
* typedef MyFlatMutableBuilder builder;
|
||||
* };
|
||||
* \endcode
|
||||
*
|
||||
* @see rti::flat::plain_cast()
|
||||
*/
|
||||
// For aggregation types, this template is to be specialized in generated code
|
||||
template <typename T>
|
||||
struct flat_type_traits;
|
||||
|
||||
template <typename T, unsigned int N>
|
||||
struct flat_type_traits<FinalArrayOffset<T, N> > {
|
||||
typedef typename flat_type_traits<T>::plain_type plain_type;
|
||||
};
|
||||
|
||||
template <typename T, unsigned int N>
|
||||
struct flat_type_traits<PrimitiveArrayOffset<T, N> > {
|
||||
typedef T plain_type;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct flat_type_traits<PrimitiveSequenceOffset<T> > {
|
||||
typedef T plain_type;
|
||||
};
|
||||
|
||||
template <typename T, unsigned int N>
|
||||
struct flat_type_traits<FinalAlignedArrayOffset<T, N> > {
|
||||
typedef typename flat_type_traits<T>::plain_type plain_type;
|
||||
};
|
||||
|
||||
template <typename T, unsigned int N>
|
||||
struct flat_type_traits<MutableArrayOffset<T, N> > {
|
||||
typedef typename flat_type_traits<T>::plain_type plain_type;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct flat_type_traits<SequenceOffset<T> > {
|
||||
typedef typename flat_type_traits<T>::plain_type plain_type;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct flat_type_traits<StringOffset > :
|
||||
flat_type_traits<PrimitiveSequenceOffset<char> > {
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct flat_type_traits<PrimitiveOffset<T> > {
|
||||
typedef T plain_type;
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup RTIFlatOffsetsModule
|
||||
* @brief Casts into an equivalent plain C++ type
|
||||
*
|
||||
* Some FlatData types can be cast to their equivalent <b>plain</b> definition as
|
||||
* a regular non-FlatData C++ type. This is a more efficient way to access
|
||||
* the data. This function casts, if possible, the member pointed to by the
|
||||
* offset argument to an equivalent plain C++ type. Any changes made through
|
||||
* the plain type are reflected in the FlatData sample.
|
||||
*
|
||||
* @tparam OffsetType The Offset type
|
||||
*
|
||||
* @pre plain_cast requires the type to meet the following restrictions:
|
||||
* - The type must be a final struct, or an array or sequence of members of a
|
||||
* type that meets these restrictions (including primitive types)
|
||||
* - The type must be defined in a way such that the packing of the plain C++ type
|
||||
* doesn't differ from the padding in XCDR2.
|
||||
* - The type may not inherit from another type.
|
||||
* - In addition, the sample must be serialized in the native endianness.
|
||||
* For example, if a subscribing application on a little-endian platform
|
||||
* receives a sample published from a big-endian system, it is not possible
|
||||
* to plain_cast the sample or any of its members, except if the member is a
|
||||
* primitive array or sequence of 1-byte elements.
|
||||
*
|
||||
* \ref rti::flat::OffsetBase::is_cpp_compatible "offset.is_cpp_compatible()"
|
||||
* indicates if the member meets the requirements. If the type doesn't, this
|
||||
* function \if CPP2_LANGUAGE_ONLY throws dds::core::PreconditionNotMetError.
|
||||
* \else returns NULL. \endif
|
||||
*
|
||||
* @param offset The offset to the member to cast.
|
||||
* @return The data that \p offset referred to, cast as a plain C++ type with the
|
||||
* same definition. If \p offset is an array or sequence member, the returned pointer
|
||||
* represents a C++ array with the same number of elements.
|
||||
*
|
||||
* The following table summarizes the possible parameters to this function (assuming
|
||||
* that they meet the previous requirements), and the return type in each case:
|
||||
*
|
||||
* | \p OffsetType | return type
|
||||
* | ------------- | -----------
|
||||
* | Offset to a final struct, such as `MyFlatFinalOffset` | `MyFlatFinalPlainHelper*` (pointer to a single element). This is a type with the same IDL definition, but without `@language_binding(FLAT_DATA)`.
|
||||
* | Offset to an array of final structs, such as `FinalArrayOffset<MyFlatFinalOffset, N>` | `MyFlatFinalPlainHelper*` (array of `N` elements)
|
||||
* | Offset to a sequence of final structs, such as `SequenceOffset<MyFlatFinalOffset>` | `MyFlatFinalPlainHelper*` (array of `offset.element_count()` elements)
|
||||
* | Offset to an array of primitive types, such as `PrimitiveArrayOffset<int32_t, N>` | `int32_t*` (array of `N` elements)
|
||||
* | Offset to a sequence of primitive types, such as `PrimitiveSequenceOffset<int32_t>` | `int32_t*` (array of `offset.element_count()` elements)
|
||||
*
|
||||
*
|
||||
* Due to the performance advantages that \p plain_cast offers, it is recommended
|
||||
* to define FlatData types in a way that their largest member(s) can be
|
||||
* \p plain_cast.
|
||||
*
|
||||
* The following example shows how to use \p plain_cast to cast a MyFlatFinalOffset
|
||||
* into the type with the same IDL definition as \ref MyFlatFinal, but without
|
||||
* the \p \@language_binding(FLAT_DATA) annotation:
|
||||
*
|
||||
* \code
|
||||
* MyFlatMutable *my_sample = ...;
|
||||
* auto my_root = my_sample->root();
|
||||
* auto my_final = my_root.my_final();
|
||||
*
|
||||
* // Get the plain C++ type and modify an array
|
||||
* auto my_final_plain = rti::flat::plain_cast(my_final);
|
||||
* my_final_plain->my_primitive(3); // this is using the plain C++ setter
|
||||
* my_final_plain->my_complex_array()[1].x(20); // this is a std::array
|
||||
*
|
||||
* // The change to my_complex_array is reflected in the FlatData sample
|
||||
* std::cout << my_final.my_primitive() << std::endl; // 3
|
||||
* std::cout << my_final.my_complex_array().get_element(1).x() << std::endl; // 20
|
||||
* \endcode
|
||||
*
|
||||
* This example shows how to use plain_cast to efficiently build a sequence
|
||||
* of final elements and a sequence of integers, both members of a mutable type:
|
||||
*
|
||||
* \code
|
||||
* MyFlatMutableBuilder builder = rti::flat::build_data(writer);
|
||||
*
|
||||
* // 1)
|
||||
* //
|
||||
* // Build a sequence of 5 elements with add_n() instead of 5 calls to
|
||||
* // add_next()
|
||||
* rti::flat::FinalSequenceBuilder<MyFlatFinalOffset> seq_builder =
|
||||
* builder.build_my_final_seq();
|
||||
* seq_builder.add_n(5);
|
||||
* // Finish the member, getting the offset to the member
|
||||
* rti::flat::SequenceOffset<MyFlatFinalOffset> seq_offset = seq_builder.finish();
|
||||
*
|
||||
* // Cast it to an array of plain C++ type and initialize it.
|
||||
* // This way to initialize it is more efficient than add_next().
|
||||
* auto seq_elements = rti::flat::plain_cast(seq_offset);
|
||||
* for (int i = 0; i < 5; i++) {
|
||||
* seq_elements[i].my_primitive(i);
|
||||
* // ...
|
||||
* }
|
||||
*
|
||||
* // 2)
|
||||
* //
|
||||
* // Build a sequence of 1000 integers
|
||||
* auto seq_builder = builder.build_my_primitive_seq();
|
||||
* // make space for 1000 elements, but leave them uninitialized
|
||||
* seq_builder.add_n(1000);
|
||||
* auto seq_offset = seq_builder.finish();
|
||||
*
|
||||
* // Initialize the elements:
|
||||
* int32_t *elements = rti::flat::plain_cast(seq_offset);
|
||||
* for (int i = 0; i < 1000; i++) {
|
||||
* elements[i] = ...;
|
||||
* }
|
||||
*
|
||||
* // ... continue building the sample using 'builder'
|
||||
* \endcode
|
||||
*
|
||||
*/
|
||||
template <typename OffsetType>
|
||||
typename flat_type_traits<OffsetType>::plain_type* plain_cast(OffsetType& offset)
|
||||
{
|
||||
RTI_FLAT_CHECK_PRECONDITION(offset.is_cpp_compatible(), return NULL);
|
||||
return reinterpret_cast<typename flat_type_traits<OffsetType>::plain_type*>(
|
||||
offset.get_buffer());
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup RTIFlatOffsetsModule
|
||||
* @brief Const version of plain_cast
|
||||
*/
|
||||
template <typename OffsetType>
|
||||
const typename flat_type_traits<OffsetType>::plain_type* plain_cast(const OffsetType& offset)
|
||||
{
|
||||
RTI_FLAT_CHECK_PRECONDITION(offset.is_cpp_compatible(), return NULL);
|
||||
return reinterpret_cast<const typename flat_type_traits<OffsetType>::plain_type*>(
|
||||
offset.get_buffer());
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename OffsetKind>
|
||||
struct offset_kind_is_fixed_size {
|
||||
enum { value = 0 };
|
||||
};
|
||||
|
||||
template <>
|
||||
struct offset_kind_is_fixed_size<rti::flat::fixed_size_type_tag_t> {
|
||||
enum { value = 1 };
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
// Utility to determine if a Flat Data type is fixed size (final) or variable
|
||||
// size (mutable)
|
||||
template <typename T>
|
||||
struct is_fixed_size_type
|
||||
: detail::offset_kind_is_fixed_size<typename T::Offset::offset_kind>
|
||||
{
|
||||
};
|
||||
|
||||
} }
|
||||
|
||||
#endif // RTI_DDS_FLAT_FLATSAMPLE_HPP_
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,952 @@
|
||||
/*
|
||||
(c) Copyright, Real-Time Innovations, 2018-2024.
|
||||
All rights reserved.
|
||||
|
||||
No duplications, whole or partial, manual or electronic, may be made
|
||||
without express written permission. Any such copies, or
|
||||
revisions thereof, must display this notice unaltered.
|
||||
This code contains trade secrets of Real-Time Innovations, Inc.
|
||||
*/
|
||||
|
||||
#ifndef RTI_DDS_FLAT_SEQUENCEBUILDERS_HPP_
|
||||
#define RTI_DDS_FLAT_SEQUENCEBUILDERS_HPP_
|
||||
|
||||
#include "rti/flat/Builder.hpp"
|
||||
|
||||
//
|
||||
// Builders for sequences, strings, and arrays.
|
||||
//
|
||||
// Type hierarchy (- indicates an abstract builder; * a concrete, usable builder)
|
||||
//
|
||||
// - AbstractListBuilder
|
||||
// * MutableArrayBuilder
|
||||
// - AbstractSequenceBuilder
|
||||
// * MutableSequenceBuilder
|
||||
// * FinalSequenceBuilder
|
||||
// * PrimitiveSequenceBuilder
|
||||
// * StringBuilder
|
||||
//
|
||||
// Notes:
|
||||
// - there's no "FinalArrayBuilder" because that's a fixed-size type and
|
||||
// doesn't need a builder (it's constructed via an "add" operation, not a "build"
|
||||
// operation).
|
||||
// - MutableSequenceBuilder "builds" its elements
|
||||
// - FinalSequenceBuilder "adds" its elements
|
||||
//
|
||||
|
||||
namespace rti { namespace flat {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <bool IsForEnum>
|
||||
struct DHeaderGeneratorImpl {
|
||||
static bool reserve_dheader(rti::xcdr::Stream& stream)
|
||||
{
|
||||
if (collection_dheader_traits<IsForEnum>::dheader_required()) {
|
||||
return stream.serialize_dheader() != NULL;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool finish_dheader(
|
||||
rti::xcdr::Stream& stream,
|
||||
unsigned char *dheader_position)
|
||||
{
|
||||
if (collection_dheader_traits<IsForEnum>::dheader_required()) {
|
||||
return stream.finish_dheader((char *) dheader_position);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool move_after_dheader(
|
||||
rti::xcdr::Stream& stream,
|
||||
unsigned char *begin_position)
|
||||
{
|
||||
stream.current_position(begin_position);
|
||||
|
||||
if (collection_dheader_traits<IsForEnum>::dheader_required()) {
|
||||
return stream.skip(RTI_XCDR_DHEADER_SIZE);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct NoDHeaderGenerator {
|
||||
static bool reserve_dheader(rti::xcdr::Stream&)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool finish_dheader(rti::xcdr::Stream&, unsigned char *)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool move_after_dheader(
|
||||
rti::xcdr::Stream& stream,
|
||||
unsigned char *begin_position)
|
||||
{
|
||||
stream.current_position(begin_position);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
typedef DHeaderGeneratorImpl<false> DHeaderGenerator;
|
||||
typedef DHeaderGeneratorImpl<true> DHeaderGeneratorForEnum;
|
||||
|
||||
// Maybe specialized for enums to select DHeaderGenerator
|
||||
template <typename T, typename Enable = void>
|
||||
struct primitive_sequence_dheader_gen {
|
||||
typedef NoDHeaderGenerator type;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/*i
|
||||
* Extends and specializes AbstractBuilder to add or override the
|
||||
* functionality required to build a collection of consecutive elements
|
||||
* (sequence or array):
|
||||
*
|
||||
* - Add or build elements
|
||||
* - Keep track of the element count
|
||||
* - Handle element alignment
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* @ingroup RTIFlatBuildersModule
|
||||
* @brief Base class of all array and sequence builders
|
||||
*/
|
||||
template <typename DHeaderGen>
|
||||
class AbstractListBuilder : public AbstractBuilder {
|
||||
protected:
|
||||
AbstractListBuilder() : element_count_(0)
|
||||
{ }
|
||||
|
||||
AbstractListBuilder(
|
||||
nested_tag_t,
|
||||
AbstractBuilder& parent,
|
||||
unsigned int alignment)
|
||||
: AbstractBuilder(nested_tag_t(), parent, alignment),
|
||||
element_count_(0)
|
||||
{
|
||||
DHeaderGen::reserve_dheader(stream());
|
||||
}
|
||||
|
||||
template <typename ElementBuilder>
|
||||
ElementBuilder build_next()
|
||||
{
|
||||
rti::xcdr::Stream::Memento stream_memento(stream());
|
||||
return build_element<ElementBuilder>(stream_memento);
|
||||
}
|
||||
|
||||
template <typename ElementOffset>
|
||||
ElementOffset add_next()
|
||||
{
|
||||
RTI_FLAT_BUILDER_CHECK_VALID(return ElementOffset());
|
||||
|
||||
// save state in case of error
|
||||
rti::xcdr::Stream::Memento stream_memento(stream());
|
||||
|
||||
if (!stream().align(ElementOffset::required_alignment)) {
|
||||
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(return ElementOffset());
|
||||
}
|
||||
|
||||
ElementOffset element = add_element<ElementOffset>();
|
||||
if (element.is_null()) {
|
||||
return ElementOffset();
|
||||
}
|
||||
|
||||
// Success: clear memento, increment element count
|
||||
stream_memento.discard();
|
||||
element_count_++;
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
template <typename ElementOffset>
|
||||
void add_n(unsigned int count)
|
||||
{
|
||||
RTI_FLAT_BUILDER_CHECK_VALID(return);
|
||||
|
||||
if (count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!stream().align(ElementOffset::required_alignment)) {
|
||||
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(return);
|
||||
}
|
||||
|
||||
offset_t elements_size = ElementOffset::serialized_size(0)
|
||||
+ ElementOffset::serialized_size_w_padding() * (count - 1);
|
||||
if (!stream().skip(elements_size)) {
|
||||
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(return);
|
||||
}
|
||||
|
||||
element_count_ += count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the current number of elements that have been added
|
||||
*/
|
||||
unsigned int element_count() const
|
||||
{
|
||||
return element_count_;
|
||||
}
|
||||
|
||||
#if defined(RTI_FLAT_DATA_CXX11_RVALUE_REFERENCES)
|
||||
AbstractListBuilder(AbstractListBuilder&& other) = default;
|
||||
AbstractListBuilder& operator=(AbstractListBuilder&& other)
|
||||
{
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
finish_untyped_impl();
|
||||
|
||||
AbstractBuilder::operator=(static_cast<AbstractBuilder&&>(other));
|
||||
|
||||
element_count_ = other.element_count_;
|
||||
other.element_count_ = 0;
|
||||
|
||||
return *this;
|
||||
}
|
||||
#else
|
||||
public:
|
||||
// Enables the safe-move-constructor idiom without C++11 move constructors
|
||||
struct AbstractListBuilderMoveProxy : AbstractBuilderMoveProxy {
|
||||
unsigned int element_count_;
|
||||
};
|
||||
|
||||
operator AbstractListBuilderMoveProxy () throw() // move-constructor idiom
|
||||
{
|
||||
AbstractListBuilderMoveProxy other;
|
||||
move_to(other);
|
||||
return other;
|
||||
}
|
||||
|
||||
protected:
|
||||
void move_from(AbstractListBuilderMoveProxy& other)
|
||||
{
|
||||
AbstractBuilder::move_from(other);
|
||||
element_count_ = other.element_count_;
|
||||
}
|
||||
|
||||
void move_to(AbstractListBuilderMoveProxy& other)
|
||||
{
|
||||
AbstractBuilder::move_to(other);
|
||||
other.element_count_ = element_count_;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
private:
|
||||
friend class AbstractBuilder;
|
||||
|
||||
virtual void finish_member() // override
|
||||
{
|
||||
RTI_FLAT_BUILDER_CHECK_VALID(return);
|
||||
|
||||
// Increment element count after the nested element builder finishes,
|
||||
// instead of on build_next(). This makes it easier to roll back the
|
||||
// state if the element building doesn't complete.
|
||||
element_count_++;
|
||||
AbstractBuilder::finish_member();
|
||||
}
|
||||
|
||||
protected:
|
||||
bool finish_dheader()
|
||||
{
|
||||
return DHeaderGen::finish_dheader(stream(), begin_position());
|
||||
}
|
||||
|
||||
unsigned int element_count_;
|
||||
};
|
||||
|
||||
// --- Array: -----------------------------------------------------------------
|
||||
//
|
||||
// (Note: there is no FinalArrayBuilder, because a final array's size is fixed,
|
||||
// and is handled directly returning its offset with add_*; on the other hand,
|
||||
// sequences are never fixed-size.)
|
||||
|
||||
/**
|
||||
* @ingroup RTIFlatBuildersModule
|
||||
* @brief Builds an array member of variable-size elements
|
||||
*
|
||||
* @tparam ElementBuilder The Builder type for the elements of the array
|
||||
* @tparam N The array bound; the exact number of elements this array builder
|
||||
* must build. For multidimensional arrays, \p N is the product
|
||||
* of each dimension bound.
|
||||
*
|
||||
* Each element of this array needs to be built using the ElementBuilder returned
|
||||
* by build_next(). N elements exactly must be built. Unlike a sequence Builder,
|
||||
* it is not possible to finish an array with less than N elements.
|
||||
*
|
||||
* This example shows how to use a MutableArrayBuilder to build an array member
|
||||
* of MyFlatMutableBuilder:
|
||||
*
|
||||
* \code
|
||||
* MyFlatMutableBuilder builder = rti::flat::build_data(writer);
|
||||
* auto array_builder = builder.build_my_mutable_array();
|
||||
* for (int i = 0; i < 10; i++) {
|
||||
* FlatMutableBarBuilder element_builder = array_builder.build_next();
|
||||
* // ... build element
|
||||
* element_builder.finish();
|
||||
* }
|
||||
* array_builder.finish();
|
||||
* \endcode
|
||||
*
|
||||
* Note that Builder types are not necessary for arrays of fixed-size elements,
|
||||
* since they are added at once (see MyFlatMutableBuilder::add_my_final_array()).
|
||||
*/
|
||||
template <typename ElementBuilder, unsigned int N>
|
||||
class MutableArrayBuilder : public AbstractListBuilder<detail::DHeaderGenerator> {
|
||||
public:
|
||||
/**
|
||||
* @brief The related Offset type
|
||||
*/
|
||||
typedef MutableArrayOffset<typename ElementBuilder::Offset, N> Offset;
|
||||
typedef AbstractListBuilder<detail::DHeaderGenerator> Base;
|
||||
|
||||
MutableArrayBuilder()
|
||||
{ }
|
||||
|
||||
// The destructor does the same as finish_impl() but doesn't need to return
|
||||
// an Offset
|
||||
~MutableArrayBuilder()
|
||||
{
|
||||
finish_untyped_impl();
|
||||
}
|
||||
|
||||
#if defined(RTI_FLAT_DATA_CXX11_RVALUE_REFERENCES)
|
||||
MutableArrayBuilder(MutableArrayBuilder&& other) = default;
|
||||
|
||||
MutableArrayBuilder& operator=(MutableArrayBuilder&& other)
|
||||
{
|
||||
finish_untyped_impl();
|
||||
Base::operator=(static_cast<Base&&>(other));
|
||||
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
void finish_untyped_impl()
|
||||
{
|
||||
if (!Base::is_valid() || !Base::is_nested()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Base::finish_dheader();
|
||||
Base::finish_untyped_impl();
|
||||
}
|
||||
|
||||
private:
|
||||
friend class AbstractBuilder; // to allow access to the constructor
|
||||
|
||||
MutableArrayBuilder(
|
||||
nested_tag_t,
|
||||
AbstractBuilder& parent,
|
||||
unsigned int alignment = 0)
|
||||
: AbstractListBuilder(nested_tag_t(), parent, alignment)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Begins building the next element
|
||||
*
|
||||
* Before calling build_next() to create a new element, the element Builder
|
||||
* returned by a previous call to build_next must have been finished.
|
||||
*/
|
||||
ElementBuilder build_next()
|
||||
{
|
||||
if (element_count() == N) {
|
||||
RTI_FLAT_BUILDER_PRECONDITION_ERROR(
|
||||
"Array builder build_next: too many elements",
|
||||
return ElementBuilder());
|
||||
}
|
||||
|
||||
return AbstractListBuilder::build_next<ElementBuilder>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finishes building the array
|
||||
*
|
||||
* @pre build_next() must have been succesfully called \p N times exactly.
|
||||
* @return The Offset to the member that has been built.
|
||||
* @see discard()
|
||||
*/
|
||||
Offset finish()
|
||||
{
|
||||
RTI_FLAT_BUILDER_CHECK_CAN_FINISH(return Offset());
|
||||
|
||||
if (element_count() != N) {
|
||||
RTI_FLAT_BUILDER_PRECONDITION_ERROR(
|
||||
"Cannot finish array builder: too few elements",
|
||||
return Offset());
|
||||
}
|
||||
|
||||
if (!finish_dheader()) {
|
||||
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(return Offset());
|
||||
}
|
||||
|
||||
return finish_impl<Offset>();
|
||||
}
|
||||
|
||||
RTI_FLAT_BUILDER_DEFINE_MOVE_OPERATIONS_IMPL(\
|
||||
MutableArrayBuilder, AbstractListBuilder, AbstractListBuilderMoveProxy)
|
||||
|
||||
};
|
||||
|
||||
// --- Sequences: -------------------------------------------------------------
|
||||
|
||||
// Specializes AbstractListBuilder to handle the length header serialization
|
||||
// common to all sequences (which arrays don't have)
|
||||
/**
|
||||
* @ingroup RTIFlatBuildersModule
|
||||
* @brief Base class of Builders for sequence members.
|
||||
*
|
||||
* This class contains only implementation details and doesn't add any public
|
||||
* function to AbstractListBuilder.
|
||||
*/
|
||||
template <typename DHeaderGen>
|
||||
class AbstractSequenceBuilder : public AbstractListBuilder<DHeaderGen> {
|
||||
public:
|
||||
typedef AbstractListBuilder<DHeaderGen> Base;
|
||||
using Base::element_count;
|
||||
using Base::is_nested;
|
||||
using Base::is_valid;
|
||||
using Base::stream;
|
||||
using typename Base::nested_tag_t;
|
||||
#if !defined(RTI_FLAT_DATA_CXX11_RVALUE_REFERENCES)
|
||||
using typename Base::AbstractListBuilderMoveProxy;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
|
||||
AbstractSequenceBuilder()
|
||||
{
|
||||
}
|
||||
|
||||
AbstractSequenceBuilder(
|
||||
nested_tag_t,
|
||||
AbstractBuilder& parent,
|
||||
unsigned int alignment)
|
||||
: Base(nested_tag_t(), parent, alignment)
|
||||
{
|
||||
// leave space for sequence length
|
||||
if (!stream().check_size(sizeof(rti::xcdr::length_t))) {
|
||||
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(this->invalidate());
|
||||
} else {
|
||||
// No need to align; the base constructor already aligned to 4 the
|
||||
// begin_position
|
||||
stream().template serialize_fast<rti::xcdr::length_t>(0);
|
||||
}
|
||||
}
|
||||
|
||||
// The destructor does the same as finish_impl() but doesn't need to return
|
||||
// an Offset
|
||||
~AbstractSequenceBuilder()
|
||||
{
|
||||
finish_untyped_impl();
|
||||
}
|
||||
|
||||
#if defined(RTI_FLAT_DATA_CXX11_RVALUE_REFERENCES)
|
||||
AbstractSequenceBuilder(AbstractSequenceBuilder&& other) = default;
|
||||
AbstractSequenceBuilder& operator=(AbstractSequenceBuilder&& other)
|
||||
{
|
||||
finish_untyped_impl();
|
||||
Base::operator=(static_cast<Base&&>(other));
|
||||
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
void finish_untyped_impl()
|
||||
{
|
||||
if (!Base::is_valid() || !Base::is_nested()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Base::finish_dheader();
|
||||
finish_length();
|
||||
Base::finish_untyped_impl();
|
||||
}
|
||||
|
||||
// Concrete sequences must call this method in their own finish() function
|
||||
template <typename Offset>
|
||||
Offset finish_impl()
|
||||
{
|
||||
RTI_FLAT_BUILDER_CHECK_VALID(return Offset());
|
||||
RTI_FLAT_BUILDER_CHECK_CAN_FINISH(return Offset());
|
||||
|
||||
Base::finish_dheader();
|
||||
finish_length(); // serialize the length header
|
||||
return Base::template finish_impl<Offset>();
|
||||
}
|
||||
|
||||
private:
|
||||
// Serializes the sequence length when finishing the builder.
|
||||
void finish_length()
|
||||
{
|
||||
rti::xcdr::Stream::Memento stream_memento(stream());
|
||||
DHeaderGen::move_after_dheader(stream(), Base::begin_position());
|
||||
|
||||
// Coverity found examples where element_count() is checked, but
|
||||
// that's only done in Array builders, which are unrelated to this
|
||||
// class, AbstractSequenceBuilder.
|
||||
// coverity[CHECKED_RETURN : FALSE]
|
||||
stream().template serialize_fast<rti::xcdr::length_t>(element_count());
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup RTIFlatBuildersModule
|
||||
* @brief Builds a sequence member of variable-size elements
|
||||
*
|
||||
* @tparam ElementBuilder The Builder type for the elements of the sequence
|
||||
*
|
||||
* To build the elements use the ElementBuilder returned
|
||||
* by build_next(). An empty sequence can be built by calling finish() without
|
||||
* any call to build_next().
|
||||
*
|
||||
* This class doesn't enforce the sequence bound set in IDL.
|
||||
*
|
||||
* The following example uses a MutableSequenceBuilder to initialize a sequence
|
||||
* member of MyFlatMutableBuilder with two elements:
|
||||
*
|
||||
* \code
|
||||
* MyFlatMutableBuilder builder = rti::flat::build_data(writer);
|
||||
* auto seq_builder = builder.build_my_mutable_seq();
|
||||
* auto element_builder = seq_builder.build_next();
|
||||
* // ... build the first element
|
||||
* element_builder.finish();
|
||||
* element_builder = seq_builder.build_next();
|
||||
* // ... build the second element
|
||||
* element_builder.finish();
|
||||
*
|
||||
* seq_builder.finish();
|
||||
* \endcode
|
||||
*/
|
||||
template <typename ElementBuilder>
|
||||
class MutableSequenceBuilder
|
||||
: public AbstractSequenceBuilder<detail::DHeaderGenerator> {
|
||||
public:
|
||||
/**
|
||||
* @brief The related Offset type
|
||||
*/
|
||||
typedef SequenceOffset<typename ElementBuilder::Offset> Offset;
|
||||
|
||||
MutableSequenceBuilder()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
typedef AbstractSequenceBuilder<detail::DHeaderGenerator> Base;
|
||||
|
||||
friend class AbstractBuilder; // to allow access to the constructor
|
||||
|
||||
MutableSequenceBuilder(
|
||||
nested_tag_t,
|
||||
AbstractBuilder& parent,
|
||||
unsigned int alignment = RTI_XCDR_DHEADER_ALIGNMENT)
|
||||
: Base(nested_tag_t(), parent, alignment)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Begins building the next element
|
||||
*
|
||||
* Before calling build_next() to create a new element, the application must
|
||||
* have called finish() on the previous element Builder.
|
||||
*/
|
||||
ElementBuilder build_next()
|
||||
{
|
||||
return AbstractListBuilder::build_next<ElementBuilder>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finishes building the sequence
|
||||
*
|
||||
* @return An Offset to the member that has been built.
|
||||
*
|
||||
* @see discard()
|
||||
*/
|
||||
Offset finish()
|
||||
{
|
||||
return finish_impl<Offset>();
|
||||
}
|
||||
|
||||
RTI_FLAT_BUILDER_DEFINE_MOVE_OPERATIONS_IMPL(
|
||||
MutableSequenceBuilder,
|
||||
Base,
|
||||
AbstractListBuilderMoveProxy)
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup RTIFlatBuildersModule
|
||||
* @brief Builds a sequence member of fixed-size elements
|
||||
*
|
||||
* @tparam ElementOffset The Offset type for the elements of the sequence
|
||||
*
|
||||
* To add an element, call add_next() and use the ElementOffset it returns to
|
||||
* initialize the element's values. An empty sequence can be built by
|
||||
* calling finish() without any call to add_next().
|
||||
*
|
||||
* This class doesn't enforce the sequence bound set in IDL.
|
||||
*
|
||||
* The following example uses a FinalSequenceBuilder to initialize a sequence
|
||||
* member of MyFlatMutableBuilder with two elements:
|
||||
*
|
||||
* \code
|
||||
* MyFlatMutableBuilder builder = rti::flat::build_data(writer);
|
||||
* auto seq_builder = builder.build_my_final_seq();
|
||||
* MyFlatFinalOffset element = seq_builder.add_next();
|
||||
* element.my_primitive(1);
|
||||
* // ... continue initializing the first element
|
||||
*
|
||||
* element = seq_builder.add_next();
|
||||
* element.my_primitive(2);
|
||||
* // ... continue initializing the second element
|
||||
*
|
||||
* seq_builder.finish();
|
||||
* \endcode
|
||||
*
|
||||
* If the element type meets certain requirements, rti::flat::plain_cast()
|
||||
* provides a more efficient way to initialize a sequence of final elements.
|
||||
*/
|
||||
template <typename ElementOffset>
|
||||
class FinalSequenceBuilder
|
||||
: public AbstractSequenceBuilder<detail::DHeaderGenerator> {
|
||||
public:
|
||||
typedef SequenceOffset<ElementOffset> Offset;
|
||||
|
||||
FinalSequenceBuilder()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
friend class AbstractBuilder; // to allow access to the constructor
|
||||
|
||||
FinalSequenceBuilder(
|
||||
nested_tag_t,
|
||||
AbstractBuilder& parent,
|
||||
unsigned int alignment = RTI_XCDR_SEQ_LENGTH_ALIGNMENT)
|
||||
: AbstractSequenceBuilder(nested_tag_t(), parent, alignment)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Adds the next element
|
||||
*
|
||||
* @return The Offset that can be used to set the element values
|
||||
*/
|
||||
ElementOffset add_next()
|
||||
{
|
||||
return AbstractListBuilder::add_next<ElementOffset>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a number of elements at once
|
||||
*
|
||||
* This is an alternative to add_next().
|
||||
*
|
||||
* To initialize the elements, call finish() and use the Offset it returns
|
||||
* to access the elements.
|
||||
*/
|
||||
FinalSequenceBuilder& add_n(unsigned int count)
|
||||
{
|
||||
AbstractListBuilder::add_n<ElementOffset>(count);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finishes building the sequence
|
||||
*
|
||||
* @return An Offset to the member that has been built.
|
||||
* @see discard()
|
||||
*/
|
||||
Offset finish()
|
||||
{
|
||||
return finish_impl<Offset>();
|
||||
}
|
||||
|
||||
RTI_FLAT_BUILDER_DEFINE_MOVE_OPERATIONS_IMPL(\
|
||||
FinalSequenceBuilder, AbstractSequenceBuilder, AbstractListBuilderMoveProxy)
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup RTIFlatBuildersModule
|
||||
* @brief Builds a sequence of primitive members
|
||||
*
|
||||
* The elements can be added one by one with add_next() or all at once with
|
||||
* add_n().
|
||||
*/
|
||||
template <typename T>
|
||||
class PrimitiveSequenceBuilder
|
||||
: public AbstractSequenceBuilder<
|
||||
typename detail::primitive_sequence_dheader_gen<T>::type> {
|
||||
public:
|
||||
typedef PrimitiveSequenceOffset<T> Offset;
|
||||
typedef AbstractSequenceBuilder<
|
||||
typename detail::primitive_sequence_dheader_gen<T>::type>
|
||||
Base;
|
||||
using Base::is_valid;
|
||||
using Base::stream;
|
||||
using typename Base::nested_tag_t;
|
||||
#if !defined(RTI_FLAT_DATA_CXX11_RVALUE_REFERENCES)
|
||||
using typename Base::AbstractListBuilderMoveProxy;
|
||||
#endif
|
||||
|
||||
PrimitiveSequenceBuilder()
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
friend class AbstractBuilder; // to allow access to the constructor
|
||||
|
||||
PrimitiveSequenceBuilder(
|
||||
nested_tag_t,
|
||||
AbstractBuilder& parent,
|
||||
unsigned int alignment = RTI_XCDR_SEQ_LENGTH_ALIGNMENT)
|
||||
: Base(nested_tag_t(), parent, alignment)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Adds the next element
|
||||
*
|
||||
* @param value The primitive element to add
|
||||
*/
|
||||
PrimitiveSequenceBuilder& add_next(T value)
|
||||
{
|
||||
RTI_FLAT_BUILDER_CHECK_VALID(return *this);
|
||||
|
||||
if (!stream().template serialize<T>(value)) {
|
||||
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(return *this);
|
||||
}
|
||||
|
||||
Base::element_count_++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds all the elements in an array
|
||||
*
|
||||
* @param array The array containing the values to add
|
||||
* @param count The size of the array
|
||||
*
|
||||
* @pre array must not be NULL
|
||||
*/
|
||||
PrimitiveSequenceBuilder& add_n(const T *array, unsigned int count)
|
||||
{
|
||||
RTI_FLAT_BUILDER_CHECK_VALID(return *this);
|
||||
RTI_FLAT_ASSERT(array != NULL, return *this);
|
||||
|
||||
if (RTIXCdrUnsignedLong_MAX / static_cast<unsigned int>(sizeof(T))
|
||||
< count) {
|
||||
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(return *this);
|
||||
}
|
||||
|
||||
if (!stream().check_size(static_cast<unsigned int>(sizeof(T)) * count)) {
|
||||
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(return *this);
|
||||
}
|
||||
|
||||
if (stream().needs_byte_swap() && sizeof(T) > 1) {
|
||||
for (unsigned int i = 0; i < count; i++) {
|
||||
stream().template serialize_fast<T>(array[i]);
|
||||
}
|
||||
} else {
|
||||
stream().serialize_fast((void *) array, count * static_cast<unsigned int>(sizeof(T)));
|
||||
}
|
||||
|
||||
Base::element_count_ += count;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a number of elements with the same value
|
||||
*
|
||||
* This overload initializes all the elements to a value.
|
||||
*
|
||||
* @note This operation is O(\p count). The other overloads,
|
||||
* add_n(unsigned int) and add_n(const T*, unsigned int), are more
|
||||
* efficient
|
||||
*
|
||||
*
|
||||
* @param count The number of elements to add
|
||||
* @param value The value to set for each element
|
||||
*/
|
||||
PrimitiveSequenceBuilder& add_n(unsigned int count, T value)
|
||||
{
|
||||
RTI_FLAT_BUILDER_CHECK_VALID(return *this);
|
||||
|
||||
if (RTIXCdrUnsignedLong_MAX / static_cast<unsigned int>(sizeof(T))
|
||||
< count) {
|
||||
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(return *this);
|
||||
}
|
||||
|
||||
if (!stream().check_size(static_cast<unsigned int>(sizeof(T)) * count)) {
|
||||
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(return *this);
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < count; i++) {
|
||||
stream().template serialize_fast<T>(value);
|
||||
}
|
||||
Base::element_count_ += count;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a number of uninitialized elements
|
||||
*
|
||||
* This operation is O(1), since it leaves the elements uninitialized.
|
||||
* They can be initialized using the Offset returned by finish() and
|
||||
* rti::flat::plain_cast(). For example:
|
||||
*
|
||||
* \code
|
||||
* MyFlatMutableBuilder builder = ...;
|
||||
* auto seq_builder = builder.build_my_primitive_seq();
|
||||
* seq_builder.add_n(1000);
|
||||
* auto seq_offset = seq_builder.finish();
|
||||
* int32_t *elements = rti::flat::plain_cast(seq_offset);
|
||||
* for (int i = 0; i < 1000; i++) {
|
||||
* elements[i] = ...;
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* @param count The number of elements to add
|
||||
*/
|
||||
PrimitiveSequenceBuilder& add_n(unsigned int count)
|
||||
{
|
||||
RTI_FLAT_BUILDER_CHECK_VALID(return *this);
|
||||
|
||||
if (RTIXCdrUnsignedLong_MAX / static_cast<unsigned int>(sizeof(T))
|
||||
< count) {
|
||||
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(return *this);
|
||||
}
|
||||
|
||||
if (!stream().skip(static_cast<unsigned int>(sizeof(T)) * count)) {
|
||||
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(return *this);
|
||||
}
|
||||
|
||||
Base::element_count_ += count;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Finishes building the sequence
|
||||
*
|
||||
* @return An Offset to the member that has been built.
|
||||
* @see discard()
|
||||
*/
|
||||
Offset finish()
|
||||
{
|
||||
this->finish_dheader();
|
||||
return Base::template finish_impl<Offset>();
|
||||
}
|
||||
|
||||
private:
|
||||
RTI_FLAT_BUILDER_DEFINE_MOVE_OPERATIONS_IMPL(\
|
||||
PrimitiveSequenceBuilder, Base, AbstractListBuilderMoveProxy)
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup RTIFlatBuildersModule
|
||||
* @brief Builds a string
|
||||
*
|
||||
* A StringBuilder only provides one method, set_string(), so it can be
|
||||
* typically used as follows:
|
||||
*
|
||||
* \code
|
||||
* MyFlatMutableBuilder my_builder = ...;
|
||||
* my_builder.build_my_string().set_string("Hello!");
|
||||
* \endcode
|
||||
*
|
||||
* Note that by relying on the builder destructor, there is no need to call
|
||||
* finish() on the object returned by build_my_string().
|
||||
*/
|
||||
class StringBuilder : public PrimitiveSequenceBuilder<char> {
|
||||
public:
|
||||
typedef StringOffset Offset;
|
||||
typedef PrimitiveSequenceBuilder<char> Base;
|
||||
using Base::is_valid;
|
||||
using Base::stream;
|
||||
using typename Base::nested_tag_t;
|
||||
|
||||
StringBuilder()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
friend class AbstractBuilder; // to allow access to the constructor
|
||||
|
||||
StringBuilder(
|
||||
nested_tag_t,
|
||||
AbstractBuilder& parent,
|
||||
unsigned int alignment = RTI_XCDR_SEQ_LENGTH_ALIGNMENT)
|
||||
: Base(nested_tag_t(), parent, alignment)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Sets the string value
|
||||
*
|
||||
* @param value The string value to set.
|
||||
*
|
||||
* @pre value must not be NULL.
|
||||
*/
|
||||
StringBuilder& set_string(const char *value)
|
||||
{
|
||||
RTI_FLAT_BUILDER_CHECK_VALID(return *this);
|
||||
RTI_FLAT_ASSERT(value != NULL, return *this);
|
||||
|
||||
// if set_string is called more than once we override the string
|
||||
// that was set before
|
||||
if (Base::element_count_ != 0) {
|
||||
stream().current_position(begin_position());
|
||||
stream().serialize_fast<rti::xcdr::length_t>(0);
|
||||
Base::element_count_ = 0;
|
||||
}
|
||||
|
||||
unsigned int length =
|
||||
static_cast<unsigned int>(RTIXCdrString_getLength(value)) + 1;
|
||||
add_n(value, length); // if this fail, error has been reported
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finishes building the string
|
||||
*
|
||||
* @return An Offset to the member that has been built.
|
||||
* @see discard()
|
||||
*/
|
||||
Offset finish()
|
||||
{
|
||||
RTI_FLAT_BUILDER_CHECK_VALID(return Offset());
|
||||
|
||||
if (element_count_ == 0) {
|
||||
add_next('\0'); // build empty string if no string was built
|
||||
}
|
||||
|
||||
Base::finish_dheader();
|
||||
return finish_impl<Offset>();
|
||||
}
|
||||
|
||||
RTI_FLAT_BUILDER_DEFINE_MOVE_OPERATIONS_IMPL(\
|
||||
StringBuilder, Base, AbstractListBuilderMoveProxy)
|
||||
};
|
||||
|
||||
// Wide strings are treated as a sequence of octets
|
||||
typedef PrimitiveSequenceBuilder<unsigned char> WStringBuilder;
|
||||
|
||||
} }
|
||||
|
||||
#endif // RTI_DDS_FLAT_SEQUENCEBUILDERS_HPP_
|
||||
@@ -0,0 +1,345 @@
|
||||
/*
|
||||
(c) Copyright, Real-Time Innovations, 2018.
|
||||
All rights reserved.
|
||||
|
||||
No duplications, whole or partial, manual or electronic, may be made
|
||||
without express written permission. Any such copies, or
|
||||
revisions thereof, must display this notice unaltered.
|
||||
This code contains trade secrets of Real-Time Innovations, Inc.
|
||||
*/
|
||||
|
||||
#ifndef RTI_DDS_FLAT_SEQUENCEITERATOR_HPP_
|
||||
#define RTI_DDS_FLAT_SEQUENCEITERATOR_HPP_
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include "rti/flat/Offset.hpp"
|
||||
|
||||
namespace rti { namespace flat {
|
||||
|
||||
/**
|
||||
* @ingroup RTIFlatOffsetsModule
|
||||
* @brief Iterator for collections of Offsets
|
||||
*
|
||||
* @tparam E The Offset type of the elements
|
||||
* @tparam OffsetKind (implementation detail)
|
||||
*
|
||||
* @note This type should not be declared directly. For example, for a
|
||||
* SequenceOffset, use SequenceOffset::iterator (or simply \p auto).
|
||||
*
|
||||
* A SequenceIterator moves through the Offset to elements of a sequence or
|
||||
* array \ref RTIFlatOffsetsModule "Offset", and provides the functionality of
|
||||
* a standard \p forward_iterator.
|
||||
*
|
||||
* Note that a dereferenced SequenceIterator returns <b>by value</b> the Offset
|
||||
* to the current element; it doesn't return it by reference.
|
||||
*
|
||||
* \if CPP_LANGUAGE_ONLY
|
||||
* Since the Traditional C++ API doesn't use exceptions, it is recommended
|
||||
* to use the function advance() instead of operator++ to check for errors
|
||||
* \endif
|
||||
*
|
||||
*/
|
||||
template <typename E, typename OffsetKind>
|
||||
class SequenceIterator {
|
||||
public:
|
||||
/**
|
||||
* @brief The iterator category
|
||||
*/
|
||||
typedef std::forward_iterator_tag iterator_category;
|
||||
|
||||
/**
|
||||
* @brief The element type
|
||||
*/
|
||||
typedef E value_type;
|
||||
|
||||
/**
|
||||
* @brief The reference type is the same as the value type, an Offset
|
||||
*/
|
||||
typedef value_type reference;
|
||||
|
||||
/**
|
||||
* @brief The pointer type is the same as the value type, an Offset
|
||||
*/
|
||||
typedef value_type pointer;
|
||||
|
||||
/**
|
||||
* @brief The difference type
|
||||
*/
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
|
||||
/**
|
||||
* @brief Constructs an invalid iterator
|
||||
*/
|
||||
SequenceIterator() : sample_(NULL), current_offset_(0), max_offset_(0)
|
||||
{
|
||||
}
|
||||
|
||||
SequenceIterator(
|
||||
rti::flat::SampleBase *sample,
|
||||
offset_t initial_offset,
|
||||
offset_t max_offset)
|
||||
: sample_(sample),
|
||||
current_offset_(initial_offset),
|
||||
max_offset_(max_offset)
|
||||
{
|
||||
RTI_FLAT_ASSERT(sample != NULL, return);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns whether the iterator is invalid
|
||||
*/
|
||||
bool is_null() const
|
||||
{
|
||||
return sample_ == NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the Offset of the current element
|
||||
*
|
||||
* @return The Offset to the current element. Note that this function returns
|
||||
* by value, not by reference. This Offset may be
|
||||
* \ref rti::flat::OffsetBase::is_null "null" if the iterator has surpassed
|
||||
* the length of the collection. See \ref OffsetErrorManagement.
|
||||
*/
|
||||
value_type operator*() const
|
||||
{
|
||||
return get_impl(OffsetKind());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the Offset of the current element
|
||||
*/
|
||||
value_type operator->() const
|
||||
{
|
||||
return get_impl(OffsetKind());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Advances to the next element, reporting any errors by returning false
|
||||
*
|
||||
* \if CPP_LANGUAGE_ONLY
|
||||
* Since the Traditional C++ API doesn't support exceptions, this function
|
||||
* is recommended instead of operator++.
|
||||
* \endif
|
||||
* \if CPP2_LANGUAGE_ONLY
|
||||
* Unlike operator++, which throws an exception in case of error, advance()
|
||||
* returns false
|
||||
* \endif
|
||||
*
|
||||
* @return True if the function succeeds, or false if there was an error
|
||||
*/
|
||||
bool advance()
|
||||
{
|
||||
// Unlike operator++, advance() allows checking for errors when
|
||||
// exceptions are disabled (traditional C++)
|
||||
return advance_impl(OffsetKind());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Advances to the next element
|
||||
*
|
||||
* \if CPP_LANGUAGE_ONLY
|
||||
* @warning Since the Traditional C++ API doesn't use exceptions, use
|
||||
* advance() instead of this operator to check for errors.
|
||||
* \endif
|
||||
*/
|
||||
SequenceIterator& operator++()
|
||||
{
|
||||
bool can_advance_iter = advance_impl(OffsetKind());
|
||||
RTI_FLAT_CHECK_PRECONDITION(can_advance_iter, return *this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Advances to the next element
|
||||
*
|
||||
* \if CPP_LANGUAGE_ONLY
|
||||
* @warning Since the Traditional C++ API doesn't use exceptions, use
|
||||
* advance() instead of this operator to check for errors.
|
||||
* \endif
|
||||
*/
|
||||
SequenceIterator operator++(int)
|
||||
{
|
||||
SequenceIterator tmp(*this);
|
||||
++(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares two iterators
|
||||
*/
|
||||
friend bool operator<(
|
||||
const SequenceIterator & s1,
|
||||
const SequenceIterator & s2)
|
||||
{
|
||||
return s1.get_position() < s2.get_position();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares two iterators
|
||||
*/
|
||||
friend bool operator > (
|
||||
const SequenceIterator & s1,
|
||||
const SequenceIterator & s2)
|
||||
{
|
||||
return s1.get_position() > s2.get_position();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares two iterators
|
||||
*/
|
||||
friend bool operator <= (
|
||||
const SequenceIterator & s1,
|
||||
const SequenceIterator & s2)
|
||||
{
|
||||
return s1.get_position() <= s2.get_position();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares two iterators
|
||||
*/
|
||||
friend bool operator >= (
|
||||
const SequenceIterator & s1,
|
||||
const SequenceIterator & s2)
|
||||
{
|
||||
return s1.get_position() >= s2.get_position();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares two iterators
|
||||
*/
|
||||
friend bool operator == (
|
||||
const SequenceIterator & s1,
|
||||
const SequenceIterator & s2)
|
||||
{
|
||||
return s1.get_position() == s2.get_position();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares two iterators
|
||||
*/
|
||||
friend bool operator != (
|
||||
const SequenceIterator & s1,
|
||||
const SequenceIterator & s2)
|
||||
{
|
||||
return !(s1 == s2);
|
||||
}
|
||||
|
||||
unsigned char * get_position() const
|
||||
{
|
||||
if (is_null()) {
|
||||
return NULL;
|
||||
} else {
|
||||
return sample_->get_buffer() + current_offset_;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
E get_impl(variable_size_type_tag_t) const
|
||||
{
|
||||
if (is_null()) {
|
||||
return E();
|
||||
}
|
||||
|
||||
// The iterator points to the end of the previous element. If elements
|
||||
// need padding, we have to add it here (required alignment is 4 because
|
||||
// mutable types (variable_size_type_tag_t) are always aligned to 4)
|
||||
offset_t offset = RTIXCdrAlignment_alignSizeUp(current_offset_, 4);
|
||||
|
||||
if (offset >= max_offset_) {
|
||||
return E();
|
||||
}
|
||||
|
||||
// Calculate the size of the current element; if E is a struct type,
|
||||
// this will be quick (just deserialize the DHeader); if we're itearating
|
||||
// over an array of sequences (E is a sequence), this will require
|
||||
// skipping over the current sequence.
|
||||
offset_t size = E::Helper::calculate_serialized_size(
|
||||
sample_,
|
||||
offset,
|
||||
max_offset_);
|
||||
// size 0 indicates an error; a variable-size type E cannot have size
|
||||
// zero because it will always have a header (DHeader for mutable structs,
|
||||
// length for sequences)
|
||||
if (size == 0) {
|
||||
return E();
|
||||
}
|
||||
|
||||
return E(sample_, offset, size);
|
||||
}
|
||||
|
||||
E get_impl(fixed_size_type_tag_t) const
|
||||
{
|
||||
if (is_null()) {
|
||||
return E();
|
||||
}
|
||||
|
||||
// The iterator points to the end of the previous element. If elements
|
||||
// need padding, we have to add it here.
|
||||
offset_t offset = RTIXCdrAlignment_alignSizeUp(
|
||||
current_offset_,
|
||||
E::required_alignment);
|
||||
if (offset >= max_offset_) {
|
||||
return E();
|
||||
}
|
||||
|
||||
return E(sample_, offset);
|
||||
}
|
||||
|
||||
bool advance_impl(variable_size_type_tag_t)
|
||||
{
|
||||
if (is_null()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
current_offset_ = RTIXCdrAlignment_alignSizeUp(current_offset_, 4);
|
||||
offset_t size = E::Helper::calculate_serialized_size(
|
||||
sample_, // beginning of the buffer
|
||||
current_offset_, // absolute offset to the current element
|
||||
max_offset_); // maximum offset in the list
|
||||
if (size == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
current_offset_ += size;
|
||||
|
||||
if (current_offset_ > max_offset_) {
|
||||
current_offset_ = max_offset_;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool advance_impl(fixed_size_type_tag_t)
|
||||
{
|
||||
if (is_null()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The iterator points to the end of the previous element. If elements
|
||||
// need padding, we have to add it here. We do that to keep the end()
|
||||
// iterator consistent, becuase the last element may not need padding.
|
||||
current_offset_ = RTIXCdrAlignment_alignSizeUp(
|
||||
current_offset_,
|
||||
E::required_alignment);
|
||||
current_offset_ += E::serialized_size(0);
|
||||
|
||||
if (current_offset_ > max_offset_) {
|
||||
current_offset_ = max_offset_;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
rti::flat::SampleBase *sample_;
|
||||
offset_t current_offset_;
|
||||
offset_t max_offset_;
|
||||
};
|
||||
|
||||
} }
|
||||
|
||||
#endif // RTI_DDS_FLAT_SEQUENCEITERATOR_HPP_
|
||||
|
||||
@@ -0,0 +1,844 @@
|
||||
/*
|
||||
(c) Copyright, Real-Time Innovations, 2018.
|
||||
All rights reserved.
|
||||
|
||||
No duplications, whole or partial, manual or electronic, may be made
|
||||
without express written permission. Any such copies, or
|
||||
revisions thereof, must display this notice unaltered.
|
||||
This code contains trade secrets of Real-Time Innovations, Inc.
|
||||
*/
|
||||
|
||||
#ifndef RTI_DDS_FLAT_SEQUENCEOFFSETS_HPP_
|
||||
#define RTI_DDS_FLAT_SEQUENCEOFFSETS_HPP_
|
||||
|
||||
#include "rti/flat/Offset.hpp"
|
||||
#include "rti/flat/SequenceIterator.hpp"
|
||||
|
||||
namespace rti { namespace flat {
|
||||
|
||||
/**
|
||||
* @ingroup RTIFlatOffsetsModule
|
||||
* @brief Base class for Offsets to sequences and arrays of primitive types
|
||||
*
|
||||
* @tparam T The primitive type
|
||||
*
|
||||
*/
|
||||
template <typename T>
|
||||
class AbstractPrimitiveList : public OffsetBase {
|
||||
public:
|
||||
typedef T value_type;
|
||||
|
||||
protected:
|
||||
AbstractPrimitiveList()
|
||||
{
|
||||
}
|
||||
|
||||
AbstractPrimitiveList(
|
||||
rti::flat::SampleBase *sample,
|
||||
offset_t offset,
|
||||
offset_t sequence_size)
|
||||
: OffsetBase(sample, offset, sequence_size)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
bool is_cpp_compatible() const
|
||||
{
|
||||
RTI_FLAT_OFFSET_CHECK_NOT_NULL(return false);
|
||||
return sizeof(T) == 1 || !stream_.needs_byte_swap();
|
||||
}
|
||||
|
||||
|
||||
/*@private
|
||||
* @brief Gets direct access to the elements
|
||||
*
|
||||
* Returns a pointer to the elements of this sequence.
|
||||
*
|
||||
* @note If the Sample that contains this PrimitiveSequenceOffset has an endianess
|
||||
* different from the native one, the element bytes may be swapped. This can
|
||||
* happen if this Sample was published by a DataWriter running on a platform
|
||||
* with a different endianess and received by a DataReader running locally.
|
||||
*
|
||||
* This function is not for public use. See rti::flat::plain_cast() instead.
|
||||
*/
|
||||
T * get_elements()
|
||||
{
|
||||
return reinterpret_cast<T*>(this->get_buffer());
|
||||
}
|
||||
|
||||
/*@private
|
||||
* @brief Gets direct access to the array elements
|
||||
*
|
||||
* (Const overload)
|
||||
*/
|
||||
const T * get_elements() const
|
||||
{
|
||||
return reinterpret_cast<const T*>(this->get_buffer());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an element by index
|
||||
*
|
||||
* @param i The zero-based index of the element
|
||||
*
|
||||
* @see rti::flat::plain_cast() for a method to access all the elements at
|
||||
* once
|
||||
*/
|
||||
T get_element(unsigned int i) const
|
||||
{
|
||||
// OffsetBase::serialize only checks this as a debug assertion; in
|
||||
// a sequence or array we need to do it in release mode too
|
||||
if (!stream_.check_size((i + 1) * ((unsigned int) sizeof(T)))) {
|
||||
return T();
|
||||
}
|
||||
|
||||
return OffsetBase::template deserialize<T>(i * (unsigned int) sizeof(T));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets an element by index
|
||||
*
|
||||
* @param i The zero-based index of the element to set
|
||||
* @param value The value to set
|
||||
*
|
||||
* @return true if it was possible to set the element, or false if this
|
||||
* collection has less than i - 1 elements.
|
||||
*
|
||||
* @see rti::flat::plain_cast() for a method to access all the elements at
|
||||
* once
|
||||
*/
|
||||
bool set_element(unsigned int i, T value)
|
||||
{
|
||||
// OffsetBase::serialize only checks this as a debug assertion; in
|
||||
// a sequence or array we need to do it in release mode too
|
||||
if (!stream_.check_size((i + 1) * static_cast<unsigned int>(sizeof(T)))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return OffsetBase::serialize(i * static_cast<unsigned int>(sizeof(T)), value);
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
struct PrimitiveSequenceOffsetHelper;
|
||||
|
||||
template <typename T, typename Enable = void>
|
||||
struct primitive_sequence_header_length {
|
||||
static size_t value()
|
||||
{
|
||||
return sizeof(rti::xcdr::length_t);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* @ingroup RTIFlatOffsetsModule
|
||||
* @brief Offset to a sequence of primitive elements
|
||||
*
|
||||
* @tparam T The primitive type
|
||||
*
|
||||
*/
|
||||
template <typename T>
|
||||
class PrimitiveSequenceOffset : public AbstractPrimitiveList<T> {
|
||||
public:
|
||||
typedef variable_size_type_tag_t offset_kind;
|
||||
typedef detail::PrimitiveSequenceOffsetHelper<T> Helper;
|
||||
|
||||
PrimitiveSequenceOffset() : element_count_(0)
|
||||
{
|
||||
}
|
||||
|
||||
PrimitiveSequenceOffset(
|
||||
rti::flat::SampleBase *sample,
|
||||
offset_t offset,
|
||||
offset_t serialized_size)
|
||||
: AbstractPrimitiveList<T>(
|
||||
// passing NULL causes the base constructors to fail
|
||||
serialized_size >= static_cast<offset_t>(header_length()) ? sample : NULL,
|
||||
offset + static_cast<offset_t>(header_length()),
|
||||
serialized_size - static_cast<offset_t>(header_length())),
|
||||
element_count_(0)
|
||||
{
|
||||
#ifdef RTI_FLAT_DATA_NO_EXCEPTIONS
|
||||
if (!this->is_null()) { // without exceptions, we need to check if the base ctor failed
|
||||
#endif
|
||||
rti::xcdr::Stream::Memento stream_memento(this->stream_);
|
||||
this->stream_.skip_back(sizeof(rti::xcdr::length_t));
|
||||
element_count_ = this->stream_.template deserialize_fast<rti::xcdr::length_t>();
|
||||
#ifdef RTI_FLAT_DATA_NO_EXCEPTIONS
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of elements
|
||||
*/
|
||||
unsigned int element_count() const
|
||||
{
|
||||
RTI_FLAT_OFFSET_CHECK_NOT_NULL(return 0);
|
||||
|
||||
return element_count_;
|
||||
}
|
||||
|
||||
private:
|
||||
static size_t header_length()
|
||||
{
|
||||
return detail::primitive_sequence_header_length<T>::value();
|
||||
}
|
||||
|
||||
unsigned int element_count_;
|
||||
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
struct PrimitiveSequenceOffsetHelper {
|
||||
static offset_t calculate_serialized_size(
|
||||
rti::flat::SampleBase *sample,
|
||||
offset_t absolute_offset,
|
||||
offset_t max_size)
|
||||
{
|
||||
RTI_FLAT_ASSERT(sample != NULL, return 0);
|
||||
RTI_FLAT_ASSERT(max_size > sizeof(rti::xcdr::length_t), return 0);
|
||||
|
||||
PrimitiveSequenceOffset<T> tmp(sample, absolute_offset, max_size);
|
||||
|
||||
// elements + header
|
||||
if ((RTIXCdrUnsignedLong_MAX
|
||||
- static_cast<unsigned int>(sizeof(rti::xcdr::length_t)))
|
||||
/ static_cast<unsigned int>(sizeof(T))
|
||||
< tmp.element_count()) {
|
||||
RTI_FLAT_OFFSET_PRECONDITION_ERROR(
|
||||
"Serialized size of a sequence of primitive elements "
|
||||
"exceeds maximum representable 32-bit unsigned integer.",
|
||||
return 0);
|
||||
}
|
||||
return (tmp.element_count() * static_cast<unsigned int>(sizeof(T)))
|
||||
+ static_cast<unsigned int>(sizeof(rti::xcdr::length_t));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* @ingroup RTIFlatOffsetsModule
|
||||
* @brief Offset to an array of primitive elements
|
||||
*
|
||||
* @tparam T The primitive type
|
||||
* @tparam N The array bound. For multidimensional arrays, \p N is the product
|
||||
* of each dimension bound.
|
||||
*
|
||||
* A PrimitiveArrayOffset may meet the requirements to be cast to an array of
|
||||
* the equivalent plain C++ element type (see rti::flat::plain_cast()).
|
||||
*/
|
||||
template <typename T, unsigned int N>
|
||||
class PrimitiveArrayOffset : public AbstractPrimitiveList<T> {
|
||||
public:
|
||||
typedef fixed_size_type_tag_t offset_kind;
|
||||
|
||||
PrimitiveArrayOffset()
|
||||
{
|
||||
}
|
||||
|
||||
PrimitiveArrayOffset(rti::flat::SampleBase *sample, offset_t offset)
|
||||
: AbstractPrimitiveList<T>(
|
||||
sample,
|
||||
offset + static_cast<offset_t>(header_length()),
|
||||
serialized_size(0))
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Returns the number of elements, \p N
|
||||
*
|
||||
*/
|
||||
unsigned int element_count() const
|
||||
{
|
||||
return N;
|
||||
}
|
||||
|
||||
static offset_t serialized_size(offset_t)
|
||||
{
|
||||
return sizeof(T) * N;
|
||||
}
|
||||
|
||||
private:
|
||||
static size_t header_length()
|
||||
{
|
||||
return detail::primitive_sequence_header_length<T>::value()
|
||||
- sizeof(rti::xcdr::length_t);
|
||||
}
|
||||
};
|
||||
|
||||
// Note: the modern C++ API defines a PrimitiveArrayOffset specializations for
|
||||
// safe_enum and wchar_t
|
||||
|
||||
struct StringOffsetHelper;
|
||||
|
||||
/**
|
||||
* @ingroup RTIFlatOffsetsModule
|
||||
* @brief Offset to a string
|
||||
*
|
||||
*/
|
||||
class StringOffset : public PrimitiveSequenceOffset<char> {
|
||||
public:
|
||||
typedef variable_size_type_tag_t offset_kind;
|
||||
typedef StringOffsetHelper Helper;
|
||||
|
||||
StringOffset()
|
||||
{
|
||||
}
|
||||
|
||||
StringOffset(
|
||||
SampleBase *sample,
|
||||
offset_t absolute_offset,
|
||||
offset_t serialized_size)
|
||||
: PrimitiveSequenceOffset<char>(
|
||||
sample,
|
||||
absolute_offset,
|
||||
serialized_size)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the string
|
||||
*
|
||||
* The string returned can be modified as long as its size doesn't change.
|
||||
*/
|
||||
char * get_string()
|
||||
{
|
||||
return reinterpret_cast<char*>(this->get_buffer());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the string (const)
|
||||
*
|
||||
*/
|
||||
const char * get_string() const
|
||||
{
|
||||
return reinterpret_cast<const char*>(this->get_buffer());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of characters
|
||||
*
|
||||
* This number doesn't include the null terminating character.
|
||||
*/
|
||||
unsigned int element_count() const
|
||||
{
|
||||
RTI_FLAT_CHECK_PRECONDITION(
|
||||
PrimitiveSequenceOffset<char>::element_count() > 0,
|
||||
return 0);
|
||||
|
||||
return PrimitiveSequenceOffset<char>::element_count() - 1;
|
||||
}
|
||||
};
|
||||
|
||||
struct StringOffsetHelper {
|
||||
static offset_t calculate_serialized_size(
|
||||
rti::flat::SampleBase *sample,
|
||||
offset_t absolute_offset,
|
||||
offset_t max_size)
|
||||
{
|
||||
StringOffset tmp(sample, absolute_offset, max_size);
|
||||
return tmp.element_count()
|
||||
+ 1 // null terminator
|
||||
+ (unsigned int) sizeof(rti::xcdr::length_t); // length header
|
||||
}
|
||||
};
|
||||
|
||||
// Wide strings are treated as a sequence of octets
|
||||
typedef PrimitiveSequenceOffset<unsigned char> WStringOffset;
|
||||
|
||||
// The base class of SequenceOffset, and FinalAlignedArrayOffset
|
||||
//
|
||||
// This encapsulates the functionality that allows iterating through a list
|
||||
// of elements (a sequence or an array). The list must be always aligned to 4
|
||||
// (that excludes FinalArrayOffset)
|
||||
//
|
||||
/**
|
||||
* @ingroup RTIFlatOffsetsModule
|
||||
* @brief Base class of Offsets to sequences and arrays of non-primitive members
|
||||
*
|
||||
* @tparam ElementOffset The Offset type of the elements
|
||||
*/
|
||||
template <typename ElementOffset>
|
||||
class AbstractAlignedList : public OffsetBase {
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief The iterator type, SequenceIterator
|
||||
*/
|
||||
typedef SequenceIterator<ElementOffset, typename ElementOffset::offset_kind>
|
||||
iterator;
|
||||
|
||||
protected:
|
||||
AbstractAlignedList()
|
||||
{
|
||||
}
|
||||
|
||||
AbstractAlignedList(
|
||||
rti::flat::SampleBase *sample,
|
||||
offset_t offset,
|
||||
offset_t sequence_size)
|
||||
: OffsetBase(
|
||||
sample,
|
||||
offset,
|
||||
sequence_size)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
bool is_cpp_compatible() const // override
|
||||
{
|
||||
RTI_FLAT_OFFSET_CHECK_NOT_NULL(return false);
|
||||
return !stream_.needs_byte_swap()
|
||||
&& rti::xcdr::has_cpp_friendly_cdr_layout_collection<
|
||||
typename rti::flat::flat_type_traits<ElementOffset>::flat_type>();
|
||||
}
|
||||
|
||||
ElementOffset get_element(unsigned int i)
|
||||
{
|
||||
RTI_FLAT_OFFSET_CHECK_NOT_NULL(return ElementOffset());
|
||||
|
||||
return get_element_impl<ElementOffset>(
|
||||
i,
|
||||
typename ElementOffset::offset_kind());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets an iterator to the first Offset
|
||||
*
|
||||
* begin() and end() enable the use of range-for loops, for example:
|
||||
* \code
|
||||
* SequenceOffset<MyFlatMutableOffset> sequence_offset = my_type_offset.my_sequence();
|
||||
* for (auto element : sequence_offset) {
|
||||
* std::cout << element.x() << std::endl;
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
*/
|
||||
iterator begin()
|
||||
{
|
||||
RTI_FLAT_OFFSET_CHECK_NOT_NULL(return iterator(NULL, 0, 0));
|
||||
|
||||
return iterator(
|
||||
sample_,
|
||||
absolute_offset_,
|
||||
absolute_offset_ + get_buffer_size());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets an iterator to the past-the-end element
|
||||
*/
|
||||
iterator end()
|
||||
{
|
||||
RTI_FLAT_OFFSET_CHECK_NOT_NULL(return iterator(NULL, 0, 0));
|
||||
|
||||
return iterator(
|
||||
sample_,
|
||||
absolute_offset_ + get_buffer_size(),
|
||||
absolute_offset_ + get_buffer_size());
|
||||
}
|
||||
|
||||
private:
|
||||
// for variable size types we skip element by element
|
||||
template <typename E>
|
||||
E get_element_impl(unsigned int i, variable_size_type_tag_t)
|
||||
{
|
||||
iterator it = begin();
|
||||
while (it != end() && i > 0) {
|
||||
if (!it.advance()) {
|
||||
return E();
|
||||
}
|
||||
i--;
|
||||
}
|
||||
|
||||
return *it;
|
||||
}
|
||||
|
||||
// for fixed-size types we support random access
|
||||
template <typename E>
|
||||
E get_element_impl(unsigned int i, fixed_size_type_tag_t)
|
||||
{
|
||||
offset_t size = i * E::serialized_size_w_padding();
|
||||
|
||||
// Ensure that the stream has space for i + 1 elements
|
||||
if (!stream_.check_size(size + E::serialized_size(0))) {
|
||||
return E();
|
||||
}
|
||||
|
||||
return E(this->sample_, this->absolute_offset_ + size);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ElementOffset>
|
||||
struct SequenceOffsetHelper;
|
||||
|
||||
/**
|
||||
* @ingroup RTIFlatOffsetsModule
|
||||
* @brief Offset to a sequence of non-primitive elements
|
||||
*
|
||||
* @tparam ElementOffset The Offset type, for example MyFlatMutableOffset,
|
||||
* MyFlatFinalOffset, or StringOffset.
|
||||
*
|
||||
* Represents an Offset to a sequence member and allows getting an Offset to
|
||||
* each of its elements.
|
||||
*
|
||||
* A SequenceOffset may meet the requirements to be cast to an array of
|
||||
* the equivalent plain C++ element type (see rti::flat::plain_cast), if the
|
||||
* \p ElementOffset is a final type.
|
||||
*/
|
||||
template <typename ElementOffset>
|
||||
class SequenceOffset : public AbstractAlignedList<ElementOffset> {
|
||||
public:
|
||||
typedef variable_size_type_tag_t offset_kind;
|
||||
typedef SequenceOffsetHelper<ElementOffset> Helper;
|
||||
|
||||
SequenceOffset() : element_count_(0)
|
||||
{
|
||||
}
|
||||
|
||||
SequenceOffset(
|
||||
rti::flat::SampleBase *sample,
|
||||
offset_t offset,
|
||||
offset_t sequence_size)
|
||||
: AbstractAlignedList<ElementOffset>(
|
||||
// Member size in bytes must be > 4, otherwise let base ctor
|
||||
// fail
|
||||
sequence_size >= header_length() ? sample : NULL,
|
||||
// The offset begins after any padding to align to 4;
|
||||
// padding may be needed for example for an array of
|
||||
// sequence of final types; the end position of the previous
|
||||
// sequence may not be aligned
|
||||
RTIXCdrAlignment_alignSizeUp(
|
||||
offset,
|
||||
RTI_XCDR_SEQ_LENGTH_ALIGNMENT)
|
||||
+ header_length(),
|
||||
sequence_size - header_length()),
|
||||
element_count_(0)
|
||||
{
|
||||
#ifdef RTI_FLAT_DATA_NO_EXCEPTIONS
|
||||
if (!this->is_null()) { // without exceptions, we need to check if the base ctor failed
|
||||
#endif
|
||||
rti::xcdr::Stream::Memento stream_memento(this->stream_);
|
||||
this->stream_.skip_back(sizeof(rti::xcdr::length_t));
|
||||
element_count_ = this->stream_.template deserialize_fast<rti::xcdr::length_t>();
|
||||
#ifdef RTI_FLAT_DATA_NO_EXCEPTIONS
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the Offset to an element
|
||||
*
|
||||
* @param i The zero-based index to the element
|
||||
* @return The Offset to the element in the i-th position
|
||||
*/
|
||||
ElementOffset get_element(unsigned int i)
|
||||
{
|
||||
RTI_FLAT_OFFSET_CHECK_NOT_NULL(return ElementOffset());
|
||||
|
||||
if (i >= element_count_) {
|
||||
return ElementOffset();
|
||||
}
|
||||
return AbstractAlignedList<ElementOffset>::get_element(i);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The number of elements
|
||||
*/
|
||||
unsigned int element_count() const
|
||||
{
|
||||
RTI_FLAT_OFFSET_CHECK_NOT_NULL(return 0);
|
||||
|
||||
return element_count_;
|
||||
}
|
||||
|
||||
private:
|
||||
static offset_t header_length()
|
||||
{
|
||||
return static_cast<offset_t>(
|
||||
detail::collection_dheader_traits<false>::dheader_size()
|
||||
+ sizeof(rti::xcdr::length_t));
|
||||
}
|
||||
|
||||
|
||||
unsigned int element_count_;
|
||||
};
|
||||
|
||||
template <typename E>
|
||||
struct SequenceOffsetHelper {
|
||||
// Calculates the serialized size of a Sequence by skipping each element
|
||||
static offset_t calculate_serialized_size(
|
||||
rti::flat::SampleBase *sample,
|
||||
offset_t absolute_offset,
|
||||
offset_t max_offset)
|
||||
{
|
||||
// Create a SequenceOffset beginning at sample + absolute_offset,
|
||||
// with a size we don't know, but no greater than
|
||||
// max_offset - absolute_offset
|
||||
SequenceOffset<E> tmp(
|
||||
sample,
|
||||
absolute_offset,
|
||||
max_offset - absolute_offset);
|
||||
unsigned int count = tmp.element_count();
|
||||
typename SequenceOffset<E>::iterator it = tmp.begin();
|
||||
for (unsigned int i = 0; i < count; i++) {
|
||||
if (!it.advance()) {
|
||||
return 0; // error
|
||||
}
|
||||
}
|
||||
|
||||
return detail::ptrdiff(it.get_position(), sample->get_buffer())
|
||||
- absolute_offset;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup RTIFlatOffsetsModule
|
||||
* @brief Offset to an array of variable-size elements
|
||||
*
|
||||
* @tparam ElementOffset An Offset for a type of variable size, such as a
|
||||
* mutable struct (MyFlatMutableOffset), union, or StringOffset.
|
||||
* @tparam N The array bound. For multidimensional arrays, \p N is the product
|
||||
* of each dimension bound.
|
||||
*
|
||||
* Represents an Offset to an array member and allows getting an Offset to
|
||||
* each of its elements.
|
||||
*
|
||||
* @see FinalArrayOffset encapsulates arrays of fixed-size elements
|
||||
*/
|
||||
template <typename ElementOffset, unsigned int N>
|
||||
class MutableArrayOffset : public AbstractAlignedList<ElementOffset> {
|
||||
public:
|
||||
typedef variable_size_type_tag_t offset_kind;
|
||||
|
||||
MutableArrayOffset()
|
||||
{
|
||||
}
|
||||
|
||||
MutableArrayOffset(
|
||||
rti::flat::SampleBase *sample,
|
||||
offset_t offset,
|
||||
offset_t sequence_size)
|
||||
: AbstractAlignedList<ElementOffset>(
|
||||
sequence_size >= header_length() ? sample : NULL,
|
||||
offset + header_length(),
|
||||
sequence_size - header_length())
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the Offset to an element
|
||||
*
|
||||
* @param i The zero-based index to the element
|
||||
* @return The Offset to the element in the i-th position
|
||||
*/
|
||||
ElementOffset get_element(unsigned int i)
|
||||
{
|
||||
if (i >= N) {
|
||||
return ElementOffset();
|
||||
}
|
||||
|
||||
return AbstractAlignedList<ElementOffset>::get_element(i);
|
||||
}
|
||||
|
||||
private:
|
||||
static offset_t header_length()
|
||||
{
|
||||
return static_cast<offset_t>(
|
||||
detail::collection_dheader_traits<false>::dheader_size());
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup RTIFlatOffsetsModule
|
||||
* @brief Offset to an array of final elements
|
||||
*
|
||||
* @tparam ElementOffset A final struct Offset, such as MyFlatFinalOffset.
|
||||
* @tparam N The array bound. For multidimensional arrays, \p N is the product
|
||||
* of each dimension bound.
|
||||
*
|
||||
* Represents an Offset to an array member and allows getting an Offset to
|
||||
* each of its elements.
|
||||
*
|
||||
* FinalArrayOffset and FinalAlignedArrayOffset provide the same
|
||||
* interface, but have different implementation details. FinalArrayOffset is
|
||||
* used when the array member is part of a final type too, whereas
|
||||
* FinalAlignedArrayOffset corresponds to an array inside a mutable type.
|
||||
*
|
||||
* A FinalArrayOffset may meet the requirements to be cast to an array of
|
||||
* the equivalent plain C++ element type (see rti::flat::plain_cast()).
|
||||
*/
|
||||
template <typename ElementOffset, unsigned int N>
|
||||
class FinalArrayOffset : public OffsetBase {
|
||||
public:
|
||||
typedef fixed_size_type_tag_t offset_kind;
|
||||
|
||||
FinalArrayOffset()
|
||||
{
|
||||
}
|
||||
|
||||
// Constructor used when this array is part of a fixed-size type, and
|
||||
// therefore it's initial alignment is not known ahead of time
|
||||
FinalArrayOffset(
|
||||
rti::flat::SampleBase *sample,
|
||||
offset_t absolute_offset,
|
||||
offset_t first_element_size,
|
||||
offset_t element_size)
|
||||
: OffsetBase(
|
||||
sample,
|
||||
absolute_offset,
|
||||
first_element_size + element_size * (N - 1)),
|
||||
first_element_size_(first_element_size),
|
||||
element_size_(element_size)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the Offset to an element
|
||||
*
|
||||
* @param i The zero-based index to the element
|
||||
* @return The Offset to the element in the i-th position
|
||||
*/
|
||||
ElementOffset get_element(unsigned int i)
|
||||
{
|
||||
RTI_FLAT_OFFSET_CHECK_NOT_NULL(return ElementOffset());
|
||||
|
||||
if (i >= N) {
|
||||
return ElementOffset(); // null offset
|
||||
}
|
||||
|
||||
offset_t element_offset = this->absolute_offset_;
|
||||
if (i > 0) {
|
||||
element_offset += this->first_element_size_
|
||||
+ (i - 1) * this->element_size_;
|
||||
}
|
||||
|
||||
return ElementOffset(this->sample_, element_offset);
|
||||
}
|
||||
|
||||
private:
|
||||
offset_t first_element_size_;
|
||||
offset_t element_size_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup RTIFlatOffsetsModule
|
||||
* @brief Offset to an array of final elements
|
||||
*
|
||||
* @tparam ElementOffset A final struct Offset, such as MyFlatFinalOffset.
|
||||
* @tparam N The array bound. For multidimensional arrays, \p N is the product
|
||||
* of each dimension bound.
|
||||
*
|
||||
* Represents an Offset to an array member and allows getting an Offset to
|
||||
* each of its elements.
|
||||
*
|
||||
* The following code shows how to use a FinalAlignedArrayOffset to initialize
|
||||
* an array member with MyFlatMutableBuilder:
|
||||
*
|
||||
* \code
|
||||
* MyFlatMutableBuilder builder = rti::flat::build_data(writer);
|
||||
* auto array_offset = builder.add_my_final_array();
|
||||
* for (MyFlatFinalOffset element_offset : array_offset) {
|
||||
* element_offset.my_primitive(3);
|
||||
* // ...
|
||||
* }
|
||||
*
|
||||
* // Or access an element directly:
|
||||
* auto element_offset = array_offset.get_element(3);
|
||||
* element_offset.my_primitive(3);
|
||||
* \endcode
|
||||
*
|
||||
* A more efficient way to access a final array, provided it complies with
|
||||
* the required preconditions, is through rti::flat::plain_cast().
|
||||
*
|
||||
* FinalArrayOffset and FinalAlignedArrayOffset provide the same
|
||||
* interface, but have different implementation details. FinalArrayOffset is
|
||||
* used when the array member is part of a final type too, whereas
|
||||
* FinalAlignedArrayOffset corresponds to an array inside a mutable type.
|
||||
*
|
||||
* A FinalAlignedArrayOffset may meet the requirements to be cast to an array of
|
||||
* the equivalent plain C++ element type (see rti::flat::plain_cast()).
|
||||
*
|
||||
* @see MutableArrayOffset encapsulates arrays of variable-size elements
|
||||
*/
|
||||
template <typename ElementOffset, unsigned int N>
|
||||
class FinalAlignedArrayOffset : public AbstractAlignedList<ElementOffset> {
|
||||
public:
|
||||
typedef fixed_size_type_tag_t offset_kind;
|
||||
|
||||
FinalAlignedArrayOffset()
|
||||
{
|
||||
}
|
||||
|
||||
// Constructor used when this array is part of a mutable type, and
|
||||
// therefore it's aligned to 4 always
|
||||
FinalAlignedArrayOffset(
|
||||
rti::flat::SampleBase *sample,
|
||||
offset_t absolute_offset)
|
||||
: AbstractAlignedList<ElementOffset>(
|
||||
sample,
|
||||
absolute_offset
|
||||
+ detail::collection_dheader_traits<
|
||||
false>::dheader_size(),
|
||||
serialized_size(0))
|
||||
{ }
|
||||
|
||||
// serialized_size() only works when this array is part of a mutable type
|
||||
// and we know it's aligned to 4
|
||||
static offset_t serialized_size(offset_t)
|
||||
{
|
||||
// [element1][pad][element2][pad]...[elementN]
|
||||
return ElementOffset::serialized_size_w_padding() * (N - 1)
|
||||
+ ElementOffset::serialized_size(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the Offset to an element
|
||||
*
|
||||
* @param i The zero-based index to the element
|
||||
* @return The Offset to the element in the i-th position
|
||||
*
|
||||
* @see rti::flat::plain_cast() for a method to access all the elements at
|
||||
* once
|
||||
*/
|
||||
ElementOffset get_element(unsigned int i)
|
||||
{
|
||||
RTI_FLAT_OFFSET_CHECK_NOT_NULL(return ElementOffset());
|
||||
|
||||
if (i >= N) {
|
||||
return ElementOffset();
|
||||
}
|
||||
|
||||
return AbstractAlignedList<ElementOffset>::get_element(i);
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
// Arrays of complex elements may need a DHEADER
|
||||
template <typename T, unsigned int N>
|
||||
struct is_fixed_type_w_dheader<FinalAlignedArrayOffset<T, N> > {
|
||||
static bool value()
|
||||
{
|
||||
return is_dheader_required_in_non_primitive_collections();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} // namespace detail
|
||||
|
||||
}} // namespace rti::flat
|
||||
|
||||
#endif // RTI_DDS_FLAT_SEQUENCEOFFSETS_HPP_
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
(c) Copyright, Real-Time Innovations, 2018.
|
||||
All rights reserved.
|
||||
|
||||
No duplications, whole or partial, manual or electronic, may be made
|
||||
without express written permission. Any such copies, or
|
||||
revisions thereof, must display this notice unaltered.
|
||||
This code contains trade secrets of Real-Time Innovations, Inc.
|
||||
*/
|
||||
|
||||
// Note: this file shouldn't be included directly. Include the header for the
|
||||
// corresponding C++ API:
|
||||
//
|
||||
// - Modern C++ API: "rti/topic/flat/FlatData.hpp"
|
||||
// - Traditional C++ API: "ndds/ndds_flat_data_cpp.h"
|
||||
|
||||
#ifndef RTI_DDS_FLAT_RTIFLAT_HPP_
|
||||
#define RTI_DDS_FLAT_RTIFLAT_HPP_
|
||||
|
||||
/**
|
||||
* @defgroup RTIFlatDataModule FlatData Topic-Types
|
||||
* @ingroup DDSTopicModule
|
||||
* @brief @st_ext FlatData Language Binding for IDL topic-types
|
||||
*
|
||||
* @note For a complete description of the FlatData language binding and its
|
||||
* benefits, and a tutorial, see the "Sending Large Data" chapter in the
|
||||
* <b>\ndds User's Manual</b>.
|
||||
*
|
||||
* @note For buildable <b>code examples</b>,
|
||||
* see https://community.rti.com/kb/flatdata-and-zerocopy-examples.
|
||||
*
|
||||
* @note The FlatData language binding is available in the Traditional C++ API
|
||||
* and in the Modern C++ API.
|
||||
*
|
||||
* RTI FlatData™ is a language binding for IDL types in which the in-memory
|
||||
* representation of a sample matches the wire representation. Therefore, the
|
||||
* cost of serialization/deserialization is zero.
|
||||
*
|
||||
* To select FlatData as the language binding of a type, annotate it with
|
||||
* \p \@language_binding(FLAT_DATA) in IDL or apply the attribute
|
||||
* \p languageBinding="flat_data" in XML.
|
||||
*
|
||||
* There are some restrictions regarding the kinds of types to which the
|
||||
* FlatData language binding can be applied.
|
||||
*
|
||||
* - For final types, the FlatData language binding can be applied only to
|
||||
* fixed-size types. A fixed-size type is a type whose wire representation
|
||||
* always has the same size. This includes primitive types, arrays of
|
||||
* fixed-size types, and structs containing only members of fixed-size types.
|
||||
* Unions are not fixed-size types.
|
||||
* - For mutable types, any member is permitted.
|
||||
* - Extensible types cannot be marked as FlatData.
|
||||
*
|
||||
* Final types are more efficient, but more restrictive. In general, a good
|
||||
* compromise is to define mutable top-level types, but making sure their
|
||||
* largest data members are final.
|
||||
*
|
||||
* When a type is marked with the FlatData language binding, its mapping into
|
||||
* C++ is different than that of a regular type (plain language binding). Rather
|
||||
* than a single C++ class with direct access to its members, for a FlatData
|
||||
* type \nddsgen generates the following:
|
||||
*
|
||||
* \li A \ref RTIFlatSampleModule "Sample", the data in serialized format
|
||||
* \li An \ref RTIFlatOffsetsModule "Offset", which allows reading the data
|
||||
* members of that type inside a Sample, and modifying them without changing
|
||||
* the size
|
||||
* \li A \ref RTIFlatBuildersModule "Builder", if the type is mutable, which
|
||||
* allows creating a variable-size Sample
|
||||
*
|
||||
* For example, for the IDL types \ref MyFlatFinal and \ref MyFlatMutable
|
||||
* \nddsgen generates the following C++ types:
|
||||
* \li The Sample types \ref MyFlatFinal and \ref MyFlatMutable
|
||||
* \li The Offset types MyFlatFinalOffset and MyFlatMutableOffset.
|
||||
* \li The Builder type MyFlatMutableBuilder.
|
||||
*
|
||||
* @section PublishingFlatData Publishing FlatData
|
||||
* The typical way to publish FlatData samples includes the following steps:
|
||||
* - Create a \idref_FooDataWriter as you would for a non-FlatData (plain)
|
||||
* topic-type (see \ref DDSWriterExampleModule_writer_setup).
|
||||
*
|
||||
* (For final topic-types such as \ref MyFlatFinal)
|
||||
* - Obtain a FlatData sample with \idref_FooDataWriter_get_loan.
|
||||
* \code
|
||||
* MyFlatFinal *foo_sample = writer.get_loan();
|
||||
* \endcode
|
||||
*
|
||||
* - Initialize the sample, starting from the \ref rti::flat::Sample::root "root()" offset.
|
||||
* \code
|
||||
* MyFlatFinalOffset foo_root = foo_sample->root();
|
||||
* foo_root.my_primitive(3);
|
||||
* auto my_complex_offset = foo_root.my_complex();
|
||||
* // ... initialize my_complex_offset
|
||||
* \endcode
|
||||
*
|
||||
* (For mutable topic-types such as \ref MyFlatMutable)
|
||||
* - Obtain a \ref RTIFlatBuildersModule "Builder" to create a variable-size
|
||||
* sample with rti::flat::build_data().
|
||||
* \code
|
||||
* MyFlatMutableBuilder foo_builder = rti::flat::build_data(writer);
|
||||
* \endcode
|
||||
*
|
||||
* - Use this builder (and possibly nested member builders) to initialize the
|
||||
* members.
|
||||
* \code
|
||||
* foo_builder.add_my_primitive(3);
|
||||
* auto my_mutable_builder = foo_builder.build_my_mutable();
|
||||
* // ... build 'my_mutable' (a member with a mutable type)
|
||||
* my_mutable_builder.finish(); // completes a member
|
||||
* auto my_final_offset = foo_builder.add_my_final();
|
||||
* // ... initialize 'my_final' (a member with a final type)
|
||||
* \endcode
|
||||
*
|
||||
* - Obtain the completed sample with MyFlatMutableBuilder::finish_sample(). After
|
||||
* that, the builder is no longer usable, and the sample size cannot change.
|
||||
* \code
|
||||
* MyFlatMutable *foo_sample = foo_builder.finish_sample();
|
||||
* \endcode
|
||||
* - Optionally, it is possible to change the values of the sample accessing
|
||||
* its root(), as long as the size doesn't change. For example, if the sample
|
||||
* was built with a sequence member with two elements, it is possible to modify
|
||||
* any of those elements, but it's not possible to add a third element.
|
||||
*
|
||||
* (For both final and mutable topic-types)
|
||||
* - Write the sample with \idref_FooDataWriter_write.
|
||||
* \code
|
||||
* writer.write(*foo_sample);
|
||||
* \endcode
|
||||
*
|
||||
* After write, the DataWriter owns the FlatData sample. This allows avoiding
|
||||
* additional copies, the main goal of the FlatData language binding. This
|
||||
* means that the sample cannot be reused. The DataWriter will return it to the
|
||||
* sample pool when appropriate, as described in \idref_FooDataWriter_get_loan.
|
||||
*
|
||||
* @section SubscribingFlatData Subscribing to FlatData
|
||||
*
|
||||
* To subscribe to a topic using a FlatData topic-type:
|
||||
* - Create a \idref_FooDataReader normally (see
|
||||
* \ref DDSReaderExampleModule_reader_setup).
|
||||
* - Read or take the samples using a loaning operation, such as
|
||||
* \idref_FooDataReader_take (copying read/take operations cannot be used
|
||||
* with FlatData types).
|
||||
* - Access the \ref rti::flat::Sample::root "root()" offset, and any of the
|
||||
* sample's members from there.
|
||||
*
|
||||
* Note that the language binding is a local concept. It is possible
|
||||
* to publish with a FlatData topic-type and subscribe to it with a <b>plain</b>
|
||||
* topic-type with the same (or assignable) definition. It is also possible to
|
||||
* use a plain topic-type on the publisher side and subscribe to it using FlatData.
|
||||
* The DataWriter or DataReader of the plain topic-type has to use
|
||||
* \idref_XCDR2_DATA_REPRESENTATION in \idref_DataRepresentationQosPolicy.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef DOXYGEN_DOCUMENTATION_ONLY
|
||||
namespace rti {
|
||||
|
||||
/**
|
||||
* @brief \st_ext Support for \ref RTIFlatDataModule
|
||||
*/
|
||||
namespace flat {
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "rti/flat/ExceptionHelper.hpp"
|
||||
#include "rti/flat/FlatSample.hpp"
|
||||
#include "rti/flat/FlatTypeTraits.hpp"
|
||||
#include "rti/flat/SequenceBuilders.hpp"
|
||||
|
||||
#endif // RTI_DDS_FLAT_RTIFLAT_HPP_
|
||||
|
||||
Reference in new issue
Block a user