Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding dynamic data enum example #640

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/connext_dds/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ if(NOT DEFINED CONNEXTDDS_CONNEXT_DDS_EXAMPLES)
"compression"
"detect_samples_dropped"
"dynamic_data_access_union_discriminator"
"dynamic_data_get_enum_value"
"dynamic_data_nested_structs"
"dynamic_data_request_reply"
"dynamic_data_sequences"
Expand Down
4 changes: 4 additions & 0 deletions examples/connext_dds/dynamic_data_get_enum_value/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# This project has a specific makefile / solution
!*.sln
!*.vcxproj
!makefile*
26 changes: 26 additions & 0 deletions examples/connext_dds/dynamic_data_get_enum_value/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Dynamic Data API: access to complex member example

## Concept

*Dynamic Data API* allows to create topic samples in a programmatically manner
without defining an IDL in compile time.

This example shows how to access enum ordinal values by name from a
dynamic data object.

## Example description

In this example, we show three different scenarios to get the ordinal value of
an enum member by using `DynamicData` and `DynamicType` elements.

The first scenario shows how to get the enum member ordinal value from a
`DynamicType`. In this case, you have to specify the enum field name and
the enum element string. In this case, there is a translation from the
`DynamicType` into a `StructType` and then the element into an `EnumType`.

In the second scenario, a for loop iterates through all elements and check
which one is an `ENUMERATION_TYPE`. Then, if this enum is the type that we are
looking for, then it prints the specified values.

In the third scenario, the `EnumType` is gotten from the a DynamicData by using
the `member_type` API.
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#
# (c) 2023 Copyright, Real-Time Innovations, Inc. All rights reserved.
#
# RTI grants Licensee a license to use, modify, compile, and create derivative
# works of the Software. Licensee has the right to distribute object form
# only for use with RTI products. The Software is provided "as is", with no
# warranty of any type, including any warranty for fitness for any purpose.
# RTI is under no obligation to maintain or support the Software. RTI shall
# not be liable for any incidental or consequential damages arising out of the
# use or inability to use the software.
#
cmake_minimum_required(VERSION 3.11)
project(rtiexamples-dynamic-data-access-enum-discriminator)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

list(APPEND CMAKE_MODULE_PATH
"${CMAKE_CURRENT_SOURCE_DIR}/../../../../resources/cmake/Modules"
)
include(ConnextDdsConfigureCmakeUtils)
connextdds_configure_cmake_utils()

# Find the ConnextDDS libraries. This will look for core and API libraries
find_package(RTIConnextDDS
"7.0.0"
REQUIRED
COMPONENTS
core
)

add_executable(dynamic_data_enum_example_cxx2
"${CMAKE_CURRENT_SOURCE_DIR}/dynamic_data_enum_example.cxx"
)

target_link_libraries(dynamic_data_enum_example_cxx2
PRIVATE
RTIConnextDDS::cpp2_api
)

set_target_properties(dynamic_data_enum_example_cxx2
PROPERTIES
OUTPUT_NAME "dynamic_data_enum_example")

if(CMAKE_SYSTEM_NAME MATCHES "Linux" AND CMAKE_CXX_COMPILER_ID MATCHES "GNU")
set_target_properties(dynamic_data_enum_example_cxx2
PROPERTIES
LINK_FLAGS -Wl,--no-as-needed)
endif()
112 changes: 112 additions & 0 deletions examples/connext_dds/dynamic_data_get_enum_value/c++11/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# Example code: Get Enum Ordinals in Dynamic Data

## Building the Example :wrench:

To build this example, first run CMake to generate the corresponding build
files. We recommend you use a separate directory to store all the generated
files (e.g., ./build).

```sh
mkdir build
cd build
cmake ..
```

Once you have run CMake, you will find a number of new files in your build
directory (the list of generated files will depend on the specific CMake
Generator). To build the example, run CMake as follows:

```sh
cmake --build .
```

**Note**: if you are using a multi-configuration generator, such as Visual
Studio solutions, you can specify the configuration mode to build as follows:

```sh
cmake --build . --config Release|Debug
```

Alternatively, you can use directly the generated infrastructure (e.g.,
Makefiles or Visual Studio Solutions) to build the example. If you generated
Makefiles in the configuration process, run make to build the example. Likewise,
if you generated a Visual Studio solution, open the solution and follow the
regular build process.

## Running the Example

Run the following command from the example directory to execute the application.

On *UNIX* systems:

```bash
./dynamic_data_enum_example
```

On *Windows* Systems:

```bash
dynamic_data_enum_example
```

## Customizing the Build

### Configuring Build Type and Generator

By default, CMake will generate build files using the most common generator for
your host platform (e.g., Makefiles on Unix-like systems and Visual Studio
Solutions on Windows). You can use the following CMake variables to modify the
default behavior:

- `-DCMAKE_BUILD_TYPE` - specifies the build mode. Valid values are `Release`
and `Debug`. See the [CMake documentation for more details
(Optional)](https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html).

- `-DBUILD_SHARED_LIBS` - specifies the link mode. Valid values are `ON` for
dynamic linking and `OFF` for static linking. See [CMake documentation for
more details
(Optional)](https://cmake.org/cmake/help/latest/variable/BUILD_SHARED_LIBS.html).

- `-G` - CMake generator. The generator is the native build system used to
build the source code. All the valid values are described in the CMake
documentation for [CMake
Generators](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html).

For example, to build an example in Debug/Dynamic mode run CMake as follows:

```sh
cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_SHARED_LIBS=ON .. -G "Visual Studio 15 2017" -A x64
```

### Configuring Connext Installation Path and Architecture

The CMake build infrastructure will try to guess the location of your Connext
installation and the Connext architecture based on the default settings
for your host platform. If you installed Connext in a custom location, you
can use the `CONNEXTDDS_DIR` variable to indicate the path to your RTI Connext
installation folder. For example:

```sh
cmake -DCONNEXTDDS_DIR=/home/rti/rti_connext_dds-x.y.z ..
```

Also, if you installed libraries for multiple target architectures on your system
(i.e., you installed more than one target `.rtipkg` file), you can use the
`CONNEXTDDS_ARCH` variable to indicate the architecture of the specific libraries
you want to link against. For example:

```sh
cmake -DCONNEXTDDS_ARCH=x64Linux3gcc5.4.0 ..
```

### CMake Build Infrastructure

This example does not require the usage of rtiddsgen. Therefore, the
`CMakeLists.txt` script just creates the executable and link it with the Connext
API.

For a more comprehensive example on how to build an RTI Connext application
using CMake, please refer to the
[hello_world](../../../connext_dds/build_systems/cmake/) example, which includes
a comprehensive `CMakeLists.txt` script with all the steps and instructions
described in detail.
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
/*******************************************************************************
(c) 2023 Copyright, Real-Time Innovations, Inc. All rights reserved.
RTI grants Licensee a license to use, modify, compile, and create derivative
works of the Software. Licensee has the right to distribute object form only
for use with RTI products. The Software is provided "as is", with no warranty
of any type, including any warranty for fitness for any purpose. RTI is under
no obligation to maintain or support the Software. RTI shall not be liable for
any incidental or consequential damages arising out of the use or inability to
use the software.
******************************************************************************/

#include <cstdlib>
#include <iostream>

#include <dds/dds.hpp>

using namespace dds::core::xtypes;

EnumType create_enum_type()
{
return EnumType(
"EngineState",
{ EnumMember("off", 0), EnumMember("on", 42) });
}

DynamicType create_struct_dynamic_type()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can be simplified:

{
    return StructType("EnumStruct", { {"engine_state_element", create_enum_type() } });
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
DynamicType create_struct_dynamic_type()
StructType create_struct_dynamic_type()

{
// First create the type code for a struct
StructType my_struct("EnumStruct");
EnumType inner_enum = create_enum_type();

// Member 1 will be an enum EngineState named engine_state_element
my_struct.add_member(Member(
"engine_state_element",
static_cast<const dds::core::xtypes::DynamicType &>(inner_enum)));

return my_struct;
}

int32_t get_enum_ordinal_value_by_name(
DynamicType struct_dynamic_type,
std::string enum_element)
{
// Get value using scenario 1 of `print_enum_ordinal_value_by_name`
const StructType &dynamic_struct =
static_cast<const StructType &>(struct_dynamic_type);
EnumType enum_type = static_cast<const EnumType &>(
dynamic_struct.member("engine_state_element").type());

// Return the specified value
return enum_type.member(enum_element).ordinal();
}

void print_enum_ordinal_values_from_dynamic_type(
DynamicType struct_dynamic_type)
{
// If you need a switch/case statement you can use
// enum_dynamic_type.kind().underlying()
if (struct_dynamic_type.kind() == TypeKind::STRUCTURE_TYPE) {
// Different ways of getting the enum ordinals
// Scenario 1. We already know the member name and it is an enum

// cast the DynamicType into a StructType
const StructType &dynamic_struct =
static_cast<const StructType &>(struct_dynamic_type);

EnumType enum_type = static_cast<const EnumType &>(
dynamic_struct.member("engine_state_element").type());

std::cout << "Scenario 1 - print enum ordinals from DynamicType"
<< std::endl;
std::cout << "On: " << enum_type.member("on").ordinal() << std::endl;
std::cout << "Off: " << enum_type.member("off").ordinal() << std::endl
<< std::endl;


// Scenario 2, iterate through all the elements in the StructType
// and do something for the enums
for (auto member : dynamic_struct.members()) {
// here you may want to use a switch/case
if (member.type().kind() == TypeKind::ENUMERATION_TYPE) {
EnumType engine_state_type =
static_cast<const EnumType &>(member.type());

// This assumes that we know the enum strings
if (engine_state_type.name() == "EngineState") {
std::cout << "Scenario 2 - print enum ordinals from "
"DynamicType iterating members"
<< std::endl;
std::cout << "On: "
<< engine_state_type.member("on").ordinal()
<< std::endl;
std::cout << "Off: "
<< engine_state_type.member("off").ordinal()
<< std::endl
<< std::endl;
}
}
}
}

return;
}

void print_enum_ordinal_values_from_dynamic_data(
DynamicData struct_dynamic_data)
{
// Scenario 3, we can get the member type directly as a DynamicType
// This assumes that we already know the name "EngineState" and that it is
// an Enum.
// This is similar to Scenario 1, but getting directly the EngineState as a
// DynamicType instead of the struct that contains it.
DynamicType enum_member_type =
struct_dynamic_data.member_type("engine_state_element");
EnumType enum_type = static_cast<const EnumType &>(enum_member_type);

std::cout << "Scenario 3 - print enum ordinals from DynamicData"
<< std::endl;
std::cout << "On: " << enum_type.member("on").ordinal() << std::endl;
std::cout << "Off: " << enum_type.member("off").ordinal() << std::endl
<< std::endl;

return;
}

void example()
{
// Create the type of the struct with the enum
DynamicType enum_dynamic_type = create_struct_dynamic_type();

std::cout << "Print IDL corresponding type" << std::endl;
print_idl(enum_dynamic_type);
std::cout << std::endl;

// Create the DynamicData.
DynamicData enum_data(enum_dynamic_type);

// Set the value of the current member
enum_data.value<int32_t>(
"engine_state_element",
get_enum_ordinal_value_by_name(enum_dynamic_type, "on"));

// Print the values of the enum elements from a dynamic data. It uses
// the DynamicType internally.
print_enum_ordinal_values_from_dynamic_type(enum_dynamic_type);
print_enum_ordinal_values_from_dynamic_data(enum_data);

std::cout << "Print enum ordinal value of DynamicData" << std::endl;
std::cout << "Value: " << enum_data.value<int32_t>("engine_state_element")
<< std::endl;

return;
}

int main(int argc, char *argv[])
{
try {
example();
} catch (const std::exception &ex) {
std::cerr << "Caught exception: " << ex.what() << std::endl;
return -1;
}

return 0;
}
Loading