diff --git a/examples/connext_dds/CMakeLists.txt b/examples/connext_dds/CMakeLists.txt index ac82c71f6..3142cb969 100644 --- a/examples/connext_dds/CMakeLists.txt +++ b/examples/connext_dds/CMakeLists.txt @@ -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" diff --git a/examples/connext_dds/dynamic_data_get_enum_value/.gitignore b/examples/connext_dds/dynamic_data_get_enum_value/.gitignore new file mode 100644 index 000000000..fe580da99 --- /dev/null +++ b/examples/connext_dds/dynamic_data_get_enum_value/.gitignore @@ -0,0 +1,4 @@ +# This project has a specific makefile / solution +!*.sln +!*.vcxproj +!makefile* diff --git a/examples/connext_dds/dynamic_data_get_enum_value/README.md b/examples/connext_dds/dynamic_data_get_enum_value/README.md new file mode 100644 index 000000000..d3c3b7178 --- /dev/null +++ b/examples/connext_dds/dynamic_data_get_enum_value/README.md @@ -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. diff --git a/examples/connext_dds/dynamic_data_get_enum_value/c++11/CMakeLists.txt b/examples/connext_dds/dynamic_data_get_enum_value/c++11/CMakeLists.txt new file mode 100644 index 000000000..e5cc4cb10 --- /dev/null +++ b/examples/connext_dds/dynamic_data_get_enum_value/c++11/CMakeLists.txt @@ -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() diff --git a/examples/connext_dds/dynamic_data_get_enum_value/c++11/README.md b/examples/connext_dds/dynamic_data_get_enum_value/c++11/README.md new file mode 100644 index 000000000..08a0eb6a9 --- /dev/null +++ b/examples/connext_dds/dynamic_data_get_enum_value/c++11/README.md @@ -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. diff --git a/examples/connext_dds/dynamic_data_get_enum_value/c++11/dynamic_data_enum_example.cxx b/examples/connext_dds/dynamic_data_get_enum_value/c++11/dynamic_data_enum_example.cxx new file mode 100644 index 000000000..c2f7e62fb --- /dev/null +++ b/examples/connext_dds/dynamic_data_get_enum_value/c++11/dynamic_data_enum_example.cxx @@ -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 +#include + +#include + +using namespace dds::core::xtypes; + +EnumType create_enum_type() +{ + return EnumType( + "EngineState", + { EnumMember("off", 0), EnumMember("on", 42) }); +} + +DynamicType 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(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(struct_dynamic_type); + EnumType enum_type = static_cast( + 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(struct_dynamic_type); + + EnumType enum_type = static_cast( + 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(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(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( + "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("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; +}