17.3.9 Using Builtin Annotations
RTI Code Generator supports the following builtin annotations, which can be used in your IDL File:
- Described in this document:
- @key (17.3.9.1 The @key Annotation)
- @nested (17.3.9.2 The @nested Annotation)
- @default_nested (17.3.9.3 The @default_nested Annotation)
- @value (17.3.9.4 The @value Annotation)
- @external (17.3.9.5 The @external Annotation)
- @topic (17.3.9.6 The @topic Annotation)
- Described in the RTI Connext Core Libraries Extensible Types Guide:
- @extensibility
- @id
- @hashid
- @autoid
- @optional
- @appendable
- @mutable
- @final
- @default
- @default_literal
- @min
- @max
- @range
- @data_representation (or @allowed_data_representation)
These annotations are described in two standard documents: OMG 'Interface Definition Language' specification, version 4.2 and OMG 'Extensible and Dynamic Topic Types for DDS' specification, version 1.3.
In addition, RTI provides the following RTI-specific annotations:
- @copy (17.3.9.7 The @copy and Related Annotations)
- @resolve_name (17.3.9.8 The @resolve_name Annotation)
- @use_vector (17.3.9.9 The @use_vector annotation)
- @top_level (Replaced by @nested. See 17.3.9.2 The @nested Annotation.)
- @transfer_mode (17.3.9.10 The @transfer_mode annotation)
- @language_binding (17.3.9.11 The @language_binding Annotation)
17.3.9.1 The @key Annotation
To declare a key for your data type, insert the @key annotation in the IDL file before one or more fields of the data type.
With each key, Connext associates an internal 16-byte representation, called a key-hash.
If the maximum size of the serialized key is greater than 16 bytes, to generate the key-hash, Connext computes the MD5 key-hash of the serialized key in network-byte order. Otherwise (if the maximum size of the serialized key is <= 16 bytes), the key-hash is the serialized key in network-byte order.
Only struct and valutype definitions in IDL may have key fields. When RTI Code Generator encounters @key, it considers the annotated field in the enclosing structure or valuetype to be part of the key. Table 17.12 Example Keys shows some examples of keys.
Type |
Key Fields |
struct NoKey { |
|
struct SimpleKey { |
member1 |
struct NestedNoKey { |
|
struct NestedKey { |
member1.member1 |
struct NestedKey2 { |
member1.member1 member1.member2 |
valuetype BaseValueKey { |
member1 |
valuetype DerivedValueKey :BaseValueKey { |
member1 member2 |
valuetype DerivedValue : BaseValueKey { |
member1 |
struct ArrayKey { |
member1[0] member1[1] member1[2] |
17.3.9.2 The @nested Annotation
By default, RTI Code Generator generates user-level type-specific methods for all structures/unions found in an IDL file. These methods include the methods used by DataWriters and DataReaders to send and receive data of a given type. General methods for writing and reading that take a void pointer are not offered by Connext because they are not type safe. Instead, type-specific methods must be created to support a particular data type.
We use the term ‘top-level type’ to refer to the data type for which you intend to create a Topic that can be published or subscribed to. For top-level types, RTI Code Generator must create all of the type-specific methods previously described in addition to the code to serialize/deserialize those types. However, some of structures/unions defined in the IDL file are only embedded within higher-level structures and are not meant to be published or subscribed to individually. For non-top-level types, the DataWriters and DataReaders methods to send or receive data of those types are superfluous and do not need to be created. Although the existence of these methods is not a problem in and of itself, code space can be saved if these methods are not generated in the first place.
You can mark non-top-level types in an IDL file with the annotation @nested to tell RTI Code Generator not to generate type-specific methods. Code will still be generated to serialize and deserialize those types, since they may be embedded in top-level types.
The top-level directive can also be used but with the opposite meaning. @top_level or //@top-level (true) indicates that the type is top level, therefore, @top_level (false) would be equivalent to @nested.
In this example, RTI Code Generator will generate DataWriter/DataReader code for TopLevelStruct only:
@nested
struct EmbeddedStruct { int16 member; };
struct TopLevelStruct{ EmbeddedStruct member; };
17.3.9.3 The @default_nested Annotation
As mentioned in 17.3.9.2 The @nested Annotation, by default, Code Generator generates user-level type-specific methods for all structures/unions found in an IDL file. If you want to change this behavior for a module, making all the types and submodules within it nested by default, you can mark non-top-level modules in an IDL file with the annotation @default_nested. This tells Code Generator to not generate type-specific methods for all the structures and unions inside that module.
17.3.9.4 The @value Annotation
The @value annotation can be used to set specific values to members of enumerations. For example:
enum MyEnum { @value (17) e17, @value (2) e2, @value (3) e3 }
It is equivalent to:
enum MyEnum { e17 =17, e2 = 2, e3 =3 }
17.3.9.5 The @external Annotation
A member declared as external using the @external annotation (or the * modifier) within an aggregated type indicates that it is desirable for the implementation to store the member in storage external to the enclosing aggregated type object.
For example:
struct MyStruct { @external int32 member; }
This is equivalent to the following structure, although the usage of the @external annotation is preferred because it is standard:
struct MyStruct {
int32 *member;
};
The @external annotation only has effect in C, C++, Modern C++, and Ada applications where the members will be mapped to references (pointers). In other languages, the annotation is ignored because the members are always mapped as references.
In Modern C++ the annotation maps to the type dds::core::external<T>, a type similar to shared_ptr.
17.3.9.6 The @topic Annotation
You can mark top-level types in an IDL file with the annotation @topic to tell Code Generator to generate type-specific methods. The @topic annotation overrides the scope default behavior.
In the example below, Code Generator will generate DataWriter/DataReader code for TopicStruct only:
@default_nested
module nested_module {
struct EmbeddedStruct {
int16 member;
};
@topic
struct TopicStruct{
int16 member;
};
};
17.3.9.7 The @copy and Related Annotations
To copy a line of text verbatim into the generated code files, use the @copy annotation in the IDL file. The @copy annotation can only be applied using the comment syntax (//@). The @copy annotation is particularly useful when you want your generated code to contain text that is valid in the target programming language but is not valid IDL. It is often used to add user comments or headers or preprocessor commands into the generated code.
//@copy (// Modification History) //@copy (// -------------------- //@copy (// 17Jul05aaa, Created.) //@copy //@copy (// #include “MyTypes.h”)
These variations allow you to use the same IDL file for multiple languages:
@copy-c |
Copies code if the language is C or C++ |
@copy-java |
Copies code if the language is Java. |
@copy-py |
Copies code if the language is Python. |
@copy-cs |
Copies code if the language is C#. |
@copy-ada |
Copies code if the language is Ada. |
For example, to add import statements to generated Java code:
//@copy-java (import java.util.*;)
The above line would be ignored if the same IDL file was used to generate non-Java code.
In C, C++, C#, and Python the lines are copied into all of the foo*.[h, c, cxx, cpp, py] files generated from foo.idl. For Java, the lines are copied into all of the *.java files that were generated from the original “.idl” file. The lines will not be copied into any additional files that are generated using the -example command line option.
@copy-java-begin copies a line of text at the beginning of all the Java files generated for a type. The annotation only applies to the first type that is immediately below in the IDL file. A similar annotation for Ada files is also available, @copy-ada-begin.
If you want RTI Code Generator to copy lines only into the files that declare the data types—foo.h for C, C++ and C# foo.java for Java—use the //@copy*declaration forms of this annotation.
Note that the first whitespace character to follow //@copy is considered a delimiter and will not be copied into generated files. All subsequent text found on the line, including any leading whitespaces will be copied.
//@copy-declaration |
Copies the text into the file where the type is declared (<type>.h for C and C++, or <type>.java for Java), or <type>.cs for C# |
//@copy-c-declaration |
Same as //@copy-declaration, but for C and C++ code |
//@copy-java-declaration |
Same as //@copy-declaration, but for Java-only code |
//@copy-cs-declaration |
Same as //@copy-declaration, but for C#-only code |
//@copy-ada-declaration |
Same as //@copy-declaration, but for Ada-only code |
//@copy-java-declaration-begin |
Same as //@copy-java-declaration, but only copies the text into the file where the type is declared |
//@copy-ada-declaration-begin |
Same as //@copy-java-declaration-begin, but only for Ada-only code |
17.3.9.8 The @resolve_name Annotation
By default, RTI Code Generator tries to resolve all the references to types and constants in an IDL file. For example:
module PackageName { struct Foo { Bar barField; }; };
The compilation of the previous IDL file will report an error like the following:
ERROR com.rti.ndds.nddsgen.Main Foo.idl line x:x member type 'Bar' not found
In most cases, this is the expected behavior. However, in some cases, you may want to skip the resolution step. For example, assume that the Bar type is defined in a separate IDL file and that you are running RTI Code Generator without an external preprocessor by using the command-line option -ppDisable (maybe because the preprocessor is not available in their host platform, see 17.3.8 Preprocessor Directives):
Bar.idl
module PackageName { struct Bar { int32 field; }; };
Foo.idl
#include "Bar.idl" module PackageName { struct Foo { Bar barField; }; };
In this case, compiling Foo.idl would generate the 'not found' error. However, Bar is defined in Bar.idl. To specify that RTI Code Generator should not resolve a type reference, use the //@resolve_name annotation and set the value to false. For example:
#include "Bar.idl" module PackageName { struct Foo { @resolve_name(false) Bar barField; }; };
When this annotation is used, then for the field to which it applies, RTI Code Generator will assume that the type is an unkeyed 'structure' and it will use the type name unmodified in the generated code.
Java mapping:
package PackageName; public class Foo { public Bar barField = Bar.create(); };
C++ mapping:
namespace PackageName { class Foo { public: Bar barField; }; };
It is up to you to include the correct header files (or if using Java, to import the correct packages) so that the compiler resolves the ‘Bar’ type correctly. If needed, this can be done using the copy directives (see 17.3.9.7 The @copy and Related Annotations).
When applied to an aggregated type in IDL, the annotation applies to all types within the type, including the base type if defined. For example:
@resolve_name(false)
struct MyStructure: MyBaseStructure { Foo member1; Bar member2; };
17.3.9.9 The @use_vector annotation
The @use_vector annotation can be used in Modern C++ to indicate that a bounded sequence should be mapped to std::vector; otherwise it will be mapped to rti:core::bounded_sequence.
For example :
struct MyStruct { @use_vector sequence<int32, 10> my_bounded_seq; }
As an alternative, you can use rtiddsgen's -alwaysUseStdVector option to indicate that all bounded sequences should be mapped to std::vector. Unbounded sequences always map to std::vector.
17.3.9.10 The @transfer_mode annotation
The @transfer_mode annotation can be used to indicate how to send a sample of the annotated type. There are two possible values for this annotation: SHMEM_REF and INBAND.
The annotation can be used only while generating code for C and C++ (Traditional and Modern) APIs. For other languages, the annotation is ignored.
@transfer_mode(SHMEM_REF) indicates that a sample can be sent as a shared memory reference instead of sending the serialized sample, when the DataReader(s) are on the same node as the DataWriter writing the sample. See 34.1.5 Zero Copy Transfer Over Shared Memory for more information.
@transfer_mode(INBAND) indicates that a sample is always serialized and sent inband using the underlying transports. This is the default mode when the annotation is not present.
The use of @transfer_mode annotation without a parameter is not allowed and will generate an error during code generation.
It is sufficient to mark only the top-level types with the @transfer_mode annotation. In this example, a sample of type CameraImage can be sent as a shared memory reference, even though the included type Dimension is not explicitly annotated:
struct Dimension {
int32 height;
int32 width;
};
@transfer_mode(SHMEM_REF)
struct CameraImage {
int64 timestamp;
Dimension dimension;
octet data[8294400][4];
};
RTI Code Generator will return an error while parsing the IDL file if the following requirements are not met:
- All fixed and appendable types (described in RTI Connext Core Libraries Extensible Types Guide) annotated with @transfer_mode(SHMEM_REF) should be fixed-size types. A fixed-size type is a type whose wire representation always has the same size. This includes primitive members, arrays of fixed-size types, and structs containing only members of fixed-size types. In the above example, the types CameraImage and Dimension should not contain variable-length members such as strings, sequences, and optional and external members.
- Mutable types annotated with @transfer_mode(SHMEM_REF) can contain variable-length members when the type is also annotated with FLAT_DATA language_binding.
The @transfer_mode annotation can be applied to modules, structs, valuetypes, and unions. When applied to a module, all the types within the module inherit the transfer_mode value specified in the module.
17.3.9.11 The @language_binding Annotation
The @language_binding annotation allows selecting the language binding for a type, either the plain language binding (default option when the annotation is not specified) or the RTI FlatData™ language binding.
PLAIN is the regular language binding that maps IDL types to their regular C or C++ representation as C structs or C++ classes.
FLAT_DATA is a special language binding in which the in-memory representation is the same as the wire representation. See 34.1.4 FlatData Language Binding for a detailed description.
For example:
@language_binding(PLAIN) // or no annotation
struct MyNormalType {
...
};
@language_binding(FLAT_DATA)
struct MyFlatType {
...
};
A few notes about the @language_binding annotation:
- The annotation can be applied to modules, structs, valuetypes, and unions. When applied to a module, all the types within the module inherit the language binding value specified in the module.
- Every member type needs to have the same language binding as the type that contains it. For example, see the IDL in 34.1.4.2.1 Selecting FlatData Language Binding: if CameraImage is marked with FLAT_DATA language binding, Resolution must be marked, too.
- FLAT_DATA is only supported in the Traditional C++ and Modern C++ language APIs. The annotation will be ignored for other languages. See 34.1.4.2.3 Languages Supported by FlatData Language Binding.