Chapter 4 Generating Code
Most Connext Getting Started Guides (such as the RTI Connext Getting Started Guide or the RTI Connext Core Libraries Getting Started Guide Addendum for Embedded Systems) have you create a simple "Hello World" application to get started. For example, on macOS:
$ rtiddsgen -language c++11 -example x64Darwin17clang9.0 hello_world.idl
Note: Before using a makefile created by Code Generator to compile an application, make sure the ${NDDSHOME} environment variable is set as described in Set Up Environment Variables (rtisetenv), in the RTI Connext Getting Started Guide.
4.1 Input Files (IDL, XML, XSD)
For detailed information about IDL, XML, or XSD support in Connext, including the mapping between these formats, see the "Data Types and DDS Data Samples" chapter in the RTI Connext Core Libraries User's Manual.
4.1.1 IDL Language
In the IDL language, data types are described in a fashion almost identical to structures in "C." The complete description of the language can be found at the OMG website.
4.1.2 XML Language
Connext provides DTD and XSD files that describe the XML format.
The DTD definition of the XML elements can be found in rti_dds_topic_types.dtd under <NDDSHOME>/resource/app/app_support/rtiddsgen/schema.
The XSD definition of the XML elements can be found in rti_dds_topic_types.xsd under <NDDSHOME>/resource/app/app_support/rtiddsgen/schema.
The XML validation performed by rtiddsgen always uses the DTD definition. If the <!DOCTYPE> tag is not present in the XML file, rtiddsgen will look for the DTD document under <NDDSHOME>/resource/app/app_support/rtiddsgen/schema. Otherwise, it will use the location specified in <!DOCTYPE>.
4.1.3 XSD Language
For detailed information about XSD support in Connext, see the "Data Types and DDS Data Samples" chapter in the RTI Connext Core Libraries User's Manual.
4.1.4 Including Files
4.1.4.1 -generateIncludeFiles Command-Line Option
All the input file formats explained above (IDL, XML, XSD) allow the use of includes to promote modularity and reuse of code. Although by default Code Generator doesn't generate code for an included file, you can change this behavior by using the command-line option -generateIncludeFiles. This option directs rtiddsgen to follow all included files recursively and generate code for them.
For example, consider these two files:
// File Bar.idl
struct Bar {...
};
// File Foo.idl
#include "Bar.idl"
struct Foo {Bar b;
};
If you run the following command, for Python:
rtiddsgen -language python Foo.idl -generateIncludeFiles
This example will produce the following files:
- Foo.py
- Bar.py
If you run the following command, for Modern C++:
rtiddsgen -language C++11 Foo.idl -generateIncludeFiles
This example will produce the following files:
- Foo.hpp
- Foo.cxx
- FooPlugin.hpp
- FooPlugin.cxx
- Bar.hpp
- Bar.cxx
- BarPlugin.hpp
- BarPlugin.cxx
4.1.4.2 Python Import Path
In Python, for each IDL, XML, or XSD file included by another IDL, XML, or XSD file, Code Generator generates an import statement preceded by sys.path.append() with the path of the included file. This allows preserving the directory structure of the input files in the Python output.
If you prefer to manage the structure of the Python output in a different way, you can disable this behavior with the -noSysPathGeneration option. For example:
rtiddsgen -language python Foo.idl -noSysPathGeneration
Consider the following IDL:
#include "bar\bar.idl"
struct Foo {Bar myBar;
};
Without the -noSysPathGeneration option, the generated Python code is as follows:
...
sys.path.append(os.path.join(os.path.dirname(__file__), 'bar\'))
from bar import *
sys.path.pop()
@idl.struct
class Foo:
myBar: Bar = field(default_factory = Bar)
With the -noSysPathGeneration option, the generated Python code is as follows:
...
from bar import *
@idl.struct
class Foo:
myBar: Bar = field(default_factory = Bar)
4.1.5 Specifying Multiple Input Files
You can specify multiple IDL, XML, or XSD input files by listing them explicitly on the command line, specifying a directory, or including them in other files with an #include directive.
Code Generator processes multiple files concurrently. To configure the number of input files processed in parallel, use the -j flag described in Chapter 3 Command-Line Arguments for rtiddsgen.
4.1.5.1 Listing Explicit Files
For example, this command will process two input IDL files, hello_world1.idl and hello_world2.idl:
rtiddsgen -language C -create typefiles hello_world1.idl hello_world2.idl
Do not mix files with different extensions. For example, this is NOT supported:
rtiddsgen -language C -create typefiles hello_world1.idl hello_world2.xml
4.1.5.2 Listing Directories
You can also pass one or more directories as input. To use directories as inputs, use one of the following command-line options: -inputIDL, -inputXML, or -inputXSD. (Do not use more than one of these options in the same command.) Code Generator will scan the folder and generate code for the files with the extension indicated by the input flag. For example:
rtiddsgen -language C -create typefiles -inputIDL folder folder2
You can specify a mix of folders and input files:
rtiddsgen -language C -create typefiles -inputIDL folder file1.idl
4.1.5.3 Enabling Recursion
The command-line option -r will activate a recursive scan of all the input directories. The -r option is only valid when one or more folders are used as inputs. For example:
rtiddsgen -language C -create typefiles -inputIDL folder folder2 -r
In the following example, Code Generator will generate code for all the .idl files in "folder" when you use -r . Without -r, Code Generator will only generate code for idl_root1.idl and idl_root2.idl:
folder
├──folder1
│ └──idl_folder1_1.idl
├──folder2
│ └──idl_folder2_1.idl
├──idl_root1.idl
└──idl_root2.idl
Note that -r and -j work together. If you specify a folder and -r, Code Generator will gather all IDL files in the folder(s) and process them concurrently. To configure the number of input files processed in parallel, use the -j flag described in Chapter 3 Command-Line Arguments for rtiddsgen.
4.2 C++ Example
The following is an input file example that generates the Connext type myDataType:
IDL notation
struct myDataType {long value;
};
XML notation
<?xml version="1.0" encoding="UTF-8"?>
<types xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="rti_dds_topic_types.xsd">
<struct name="myDataType">
<member name="value" type="long"/>
</struct>
</types>
XSD notation
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:dds="http://www.omg.org/dds"
xmlns:tns="http://www.omg.org/IDL-Mapped/" targetNamespace="http://www.omg.org/IDL-
Mapped/">
<xsd:import namespace="http://www.omg.org/dds" schemaLocation="rti_dds_topic_types_common.xsd"/>
<xsd:complexType name= "myDataType">
<xsd:sequence>
<xsd:element name="value" minOccurs="1" maxOccurs="1" type="xsd:int"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
4.3 Advanced Example
After you get familiar with the simple Hello World example, you can create a more advanced example by specifying the 'advanced' template option, for traditional C++, modern C++, C#, Python, and Java languages. The advanced template is already included with Code Generator (see -exampleTemplate and -showTemplates). For example, on macOS:
rtiddsgen -language c++11 -example x64Darwin17clang9.0 -exampleTemplate advanced hello_world.idl
There are a couple of main differences between the default and advanced examples that Code Generator generates, described below: is_default_qos and Listeners.
Note: The advanced generated example is not supported on Android™ or INTEGRITY® platforms.
4.3.1 is_default_qos (true vs. false)
Connext loads QoS profiles from a file named USER_QOS_PROFILES.xml in your current working directory. (Connext may also look for this file in other locations; see How to Load XML-Specified QoS Settings, in the RTI Connext Core Libraries User's Manual). The simple example sets is_default_qos=true in the USER_QOS_PROFILES.xml file. It creates the DDS entities without specifying a profile, so it uses the default from USER_QOS_PROFILES.xml.
The advanced example also loads QoS from the USER_QOS_PROFILES.xml file. However, the advanced example omits the is_default_qos setting, which means that Connext assumes the default setting of false. Therefore, there is no default QoS provided by the XML. The example explicitly specifies which QoS profile to use from the XML file when creating DDS entities.
Setting is_default_qos=true is a convenient way to get you started quickly, but in production applications you should explicitly specify which QoS profile to use, instead of relying on a default. See Basic QoS, in the RTI Connext Getting Started Guide.
Note: The RTI Connext Getting Started Guide example is more complex than the advanced example generated by Code Generator; however, it does not use Listeners like the Code Generator example does.
4.3.2 Listeners vs. WaitSets
Both the default and advanced examples use WaitSets to block a thread until data is available. This is the safest way to get data, because it does not affect any middleware threads (it is blocking the application’s main thread until data is available). In the advanced C# example, TakeAsync() is used instead of a WaitSet, which allows iterating over an asynchronous stream of data samples as they are received.
In addition, the advanced example installs Listeners on both the DataReader and DataWriter with callbacks that you can implement to accomplish a desired behavior. These Listener callbacks are triggered for various events, such as discovering a matched DataWriter or DataReader. Listener callbacks are called back from a middleware thread, which you should not block. There are benefits to using Listeners for non-data callbacks, because you will not miss events. However, if you block or do slow processing in a Listener, it can cause undesired behavior such as data loss. See Listeners, in the RTI Connext Core Libraries User's Manual.
4.4 Minimal Example
You can create simpler example publisher and subscriber applications in Modern C++ and Python using a 'minimal' template option (see -exampleTemplate and -showTemplates). For example, on macOS:
rtiddsgen -language python -example universal -exampleTemplate minimal MyTypes.idl
Or:
rtiddsgen -language c++11 -example <platform> -exampleTemplate minimal MyTypes.idl
The minimal template focuses only on publication and subscription, removing all other unrelated complexity (command-line argument parsing, capturing signal for graceful shutdown, creating of unnecessary entities, and so on).
The minimal option simplifies the subscriber side even further, making it easier to subscribe to additional topics by using two features that handle asynchronous behavior automatically: SampleProcessor (C++11) and @subscribe decorator (Python). The subscriber application in Python uses the @app.subscribe() decorator to subscribe to a Topic by directly associating a function with the Topic to receive updates. In Modern C++, the example subscriber uses the SampleProcessor. In both cases, the application no longer needs to handle a WaitSet and can subscribe to multiple Topics very easily.
4.5 Using Generated Types Without Connext (Standalone)
You can use the generated type-specific source and header files without linking the Connext libraries or even including the Connext header files. That is, the generated files for your data types can be used standalone.
The directory <NDDSHOME>resource/app/app_support/rtiddsgen/standalone/include contains the helper files required to work in standalone mode:
- include: header and templates files for C/C++
- src: source files for C/C++
- class: Java jar file
4.5.1 Using Standalone Types in C
The generated files that can be used standalone are:
- <idl file name>.c : Types source file
- <idl file name>.h : Types header file
You cannot use the type plug-in (<idl file>Plugin.c <idl file>Plugin.h) or the type support (<idl file>Support.c <idl file>Support.h) code standalone.
To use the rtiddsgen-generated types in a standalone manner:
- Include the directory <NDDSHOME>resource/app/app_support/rtiddsgen/standalone/include in the list of directories to be searched for header files.
- Add the source files ndds_standalone_type.c and <idl file name>.c to your project.
- Include the file <idl file name>.h in the source files that will use the generated types in a standalone way.
- Compile the project using the two following preprocessor definitions:
- NDDS_STANDALONE_TYPE
- The definition for your platform: RTI_VXWORKS, RTI_QNX, RTI_WIN32, RTI_INTY, RTI_LYNX or RTI_UNIX
4.5.2 Using Standalone Types in C++98
The generated files that can be used standalone are:
- <idl file name>.cxx : Types source file
- <idl file name>.h : Types header file
You cannot use the type plugin (<idl file>Plugin.cxx <idl file>Plugin.h) or the type support (<idl file>Support.cxx <idl file>Support.h) code standalone.
To use the generated types in a standalone manner:
- Include the directory <NDDSHOME>resource/app/app_support/rtiddsgen/standalone/include in the list of directories to be searched for header files.
- Add the source files ndds_standalone_type.cxx and <idl file name>.cxx to your project.
- Include the file <idl file name>.h in the source files that will use the generated types in a standalone way.
- Compile the project using the two following preprocessor definitions:
- NDDS_STANDALONE_TYPE
- The definition for your platform: RTI_VXWORKS, RTI_QNX, RTI_WIN32, RTI_INTY, RTI_LYNX or RTI_UNIX
4.5.3 Using Standalone Types in C++11
The generated files that can be used standalone are:
- <idl file name>.cxx : Types source file
- <idl file name>.hpp : Types header file
You cannot use the type plugin (<idl file>Plugin.cxx <idl file>Plugin.hpp).
To use the generated types in a standalone manner:
- Include the directories <NDDSHOME>resource/app/app_support/rtiddsgen/standalone/include and <NDDSHOME>resource/app/app_support/rtiddsgen/standalone/include/cpp11 in the list of directories to be searched for header files.
- Add the source files ndds_standalone_type.cxx, Exception.cxx and <idl file name>.cxx to your project.
- Include the file <idl file name>.hpp in the source files that will use the generated types in a standalone way.
- Compile the project using the two following preprocessor definitions:
- NDDS_STANDALONE_TYPE
- The definition for your platform: RTI_VXWORKS, RTI_QNX, RTI_WIN32, RTI_INTY, RTI_LYNX or RTI_UNIX
4.5.4 Using Standalone Types in Java
The generated files that can be used standalone are:
- <idl type>.java
- <idl type>Seq.java
You cannot use the type code (<idl file>TypeCode.java), the type support (<idl type>TypeSupport.java), the data reader (<idl file>DataReader.java) or the data writer code (<idl file>DataWriter.java) standalone.
To use the generated types in a standalone manner:
- Include the file ndds_standalone_type.jar in the classpath of your project.
- Compile the project using the standalone types files (<idl type>.java <idl type>Seq.java).