From 287efc533084e872f665b70feea81808818226c2 Mon Sep 17 00:00:00 2001 From: jparisu Date: Fri, 21 Apr 2023 10:19:14 +0200 Subject: [PATCH 1/6] Add function to check required and unexpected tags inside a Yaml object Signed-off-by: jparisu --- .../include/ddspipe_yaml/YamlReader.hpp | 20 ++++++ ddspipe_yaml/src/cpp/YamlReader_generic.cpp | 66 +++++++++++++++++++ ddspipe_yaml/src/cpp/YamlReader_types.cpp | 14 +++- .../entities/topic/YamlGetEntityTopicTest.cpp | 15 +++++ 4 files changed, 114 insertions(+), 1 deletion(-) diff --git a/ddspipe_yaml/include/ddspipe_yaml/YamlReader.hpp b/ddspipe_yaml/include/ddspipe_yaml/YamlReader.hpp index 0f0954e60..f406b8a6e 100644 --- a/ddspipe_yaml/include/ddspipe_yaml/YamlReader.hpp +++ b/ddspipe_yaml/include/ddspipe_yaml/YamlReader.hpp @@ -252,6 +252,26 @@ class const utils::EnumBuilder& enum_builder); }; +ENUMERATION_BUILDER +( + TagKind, + required, + optional +); + +struct YamlFieldCheck +{ + TagKind kind; + std::string tag; +}; + +DDSPIPE_YAML_DllAPI +bool check_tags( + const std::vector& tags_allowed, + const Yaml& yml, + bool fail_with_extra_tags = true, + bool fail_with_exception = true); + /** * @brief \c YamlReaderVersion to stream serialization */ diff --git a/ddspipe_yaml/src/cpp/YamlReader_generic.cpp b/ddspipe_yaml/src/cpp/YamlReader_generic.cpp index 1e9ae5514..2f5c2c447 100644 --- a/ddspipe_yaml/src/cpp/YamlReader_generic.cpp +++ b/ddspipe_yaml/src/cpp/YamlReader_generic.cpp @@ -215,6 +215,72 @@ std::string YamlReader::get( return get_scalar(yml); } +bool check_tags( + const std::vector& tags_allowed, + const Yaml& yml, + bool fail_with_extra_tags /* = true */, + bool fail_with_exception /* = true */) +{ + if (!yml.IsMap() && !yml.IsNull()) + { + throw eprosima::utils::ConfigurationException(STR_ENTRY + << "Trying to check tags: in a not yaml object map."); + } + + // First, check that every required flag is present + for (const auto& field : tags_allowed) + { + if (field.kind == TagKind::required) + { + if (!YamlReader::is_tag_present(yml, field.tag)) + { + if (fail_with_exception) + { + throw eprosima::utils::ConfigurationException(STR_ENTRY + << "Required tag <" << field.tag << "> not present."); + } + else + { + return false; + } + } + } + } + + // Now that every required field is present, check for extra tags + for (const auto& yaml_field : yml) + { + auto tag = yaml_field.first.as(); + bool should_be_present = false; + + // For each tag, check if it is inside allowed tags + for (const auto& field : tags_allowed) + { + if (field.tag == tag) + { + should_be_present = true; + break; + } + } + + // If present tag is not inside allowed ones, fail + if (!should_be_present) + { + if (fail_with_exception) + { + throw eprosima::utils::ConfigurationException(STR_ENTRY + << "Unexpected tag <" << tag << "> present."); + } + else + { + return false; + } + } + } + + return true; +} + } /* namespace yaml */ } /* namespace ddspipe */ } /* namespace eprosima */ diff --git a/ddspipe_yaml/src/cpp/YamlReader_types.cpp b/ddspipe_yaml/src/cpp/YamlReader_types.cpp index 74283a6bd..cb60b3efc 100644 --- a/ddspipe_yaml/src/cpp/YamlReader_types.cpp +++ b/ddspipe_yaml/src/cpp/YamlReader_types.cpp @@ -264,8 +264,8 @@ DiscoveryServerConnectionAddress _get_discovery_server_connection_address_v1( const YamlReaderVersion version) { // GuidPrefix required - GuidPrefix server_guid = YamlReader::get(yml, version); + GuidPrefix server_guid = YamlReader::get(yml, version); // Addresses required std::set
addresses = YamlReader::get_set
(yml, COLLECTION_ADDRESSES_TAG, version); @@ -395,6 +395,18 @@ void YamlReader::fill( const Yaml& yml, const YamlReaderVersion version) { + static const std::vector fields_allowed = + { + { TagKind::required , TOPIC_NAME_TAG }, + { TagKind::required , TOPIC_TYPE_NAME_TAG }, + { TagKind::optional , TOPIC_QOS_TAG }, + }; + + check_tags( + fields_allowed, + yml + ); + // Name required object.m_topic_name = get(yml, TOPIC_NAME_TAG, version); diff --git a/ddspipe_yaml/test/unittest/entities/topic/YamlGetEntityTopicTest.cpp b/ddspipe_yaml/test/unittest/entities/topic/YamlGetEntityTopicTest.cpp index 164db031b..a1d3466be 100644 --- a/ddspipe_yaml/test/unittest/entities/topic/YamlGetEntityTopicTest.cpp +++ b/ddspipe_yaml/test/unittest/entities/topic/YamlGetEntityTopicTest.cpp @@ -141,6 +141,7 @@ TEST(YamlGetEntityTopicTest, get_real_topic) * - Empty * - Topic without name * - Topic without type + * - Topic with extra field */ TEST(YamlGetEntityTopicTest, get_real_topic_negative) { @@ -177,6 +178,20 @@ TEST(YamlGetEntityTopicTest, get_real_topic_negative) ASSERT_THROW(YamlReader::get(yml, "topic", LATEST), eprosima::utils::ConfigurationException); } + + // Topic without type + { + Yaml yml_topic; + add_field_to_yaml(yml_topic, YamlField(test::TOPIC_TYPE), TOPIC_TYPE_NAME_TAG); + add_field_to_yaml(yml_topic, YamlField(test::TOPIC_NAME), TOPIC_NAME_TAG); + add_field_to_yaml(yml_topic, YamlField("some_value"), "some_tag"); + + Yaml yml; + yml["topic"] = yml_topic; + + ASSERT_THROW(YamlReader::get(yml, "topic", + LATEST), eprosima::utils::ConfigurationException); + } } /** From 91ea357eb3ac3187ac42e6cb997f1dafe9fc8859 Mon Sep 17 00:00:00 2001 From: jparisu Date: Fri, 21 Apr 2023 10:46:32 +0200 Subject: [PATCH 2/6] uncrustify fix Signed-off-by: jparisu --- ddspipe_yaml/include/ddspipe_yaml/YamlReader.hpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ddspipe_yaml/include/ddspipe_yaml/YamlReader.hpp b/ddspipe_yaml/include/ddspipe_yaml/YamlReader.hpp index f406b8a6e..0769b17b6 100644 --- a/ddspipe_yaml/include/ddspipe_yaml/YamlReader.hpp +++ b/ddspipe_yaml/include/ddspipe_yaml/YamlReader.hpp @@ -91,9 +91,7 @@ enum YamlReaderVersion * * Every method is implemented */ -class - DDSPIPE_YAML_DllAPI - YamlReader +class DDSPIPE_YAML_DllAPI YamlReader { public: From 9949d61391184763e03528f82ea126c60b959e9b Mon Sep 17 00:00:00 2001 From: jparisu Date: Fri, 21 Apr 2023 10:51:21 +0200 Subject: [PATCH 3/6] uncrustify Signed-off-by: jparisu --- ddspipe_yaml/src/cpp/YamlReader_generic.cpp | 6 +++--- ddspipe_yaml/src/cpp/YamlReader_types.cpp | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ddspipe_yaml/src/cpp/YamlReader_generic.cpp b/ddspipe_yaml/src/cpp/YamlReader_generic.cpp index 2f5c2c447..934fc747c 100644 --- a/ddspipe_yaml/src/cpp/YamlReader_generic.cpp +++ b/ddspipe_yaml/src/cpp/YamlReader_generic.cpp @@ -224,7 +224,7 @@ bool check_tags( if (!yml.IsMap() && !yml.IsNull()) { throw eprosima::utils::ConfigurationException(STR_ENTRY - << "Trying to check tags: in a not yaml object map."); + << "Trying to check tags: in a not yaml object map."); } // First, check that every required flag is present @@ -237,7 +237,7 @@ bool check_tags( if (fail_with_exception) { throw eprosima::utils::ConfigurationException(STR_ENTRY - << "Required tag <" << field.tag << "> not present."); + << "Required tag <" << field.tag << "> not present."); } else { @@ -269,7 +269,7 @@ bool check_tags( if (fail_with_exception) { throw eprosima::utils::ConfigurationException(STR_ENTRY - << "Unexpected tag <" << tag << "> present."); + << "Unexpected tag <" << tag << "> present."); } else { diff --git a/ddspipe_yaml/src/cpp/YamlReader_types.cpp b/ddspipe_yaml/src/cpp/YamlReader_types.cpp index cb60b3efc..543cd2dad 100644 --- a/ddspipe_yaml/src/cpp/YamlReader_types.cpp +++ b/ddspipe_yaml/src/cpp/YamlReader_types.cpp @@ -397,15 +397,15 @@ void YamlReader::fill( { static const std::vector fields_allowed = { - { TagKind::required , TOPIC_NAME_TAG }, - { TagKind::required , TOPIC_TYPE_NAME_TAG }, - { TagKind::optional , TOPIC_QOS_TAG }, + { TagKind::required, TOPIC_NAME_TAG }, + { TagKind::required, TOPIC_TYPE_NAME_TAG }, + { TagKind::optional, TOPIC_QOS_TAG }, }; check_tags( fields_allowed, yml - ); + ); // Name required object.m_topic_name = get(yml, TOPIC_NAME_TAG, version); From d33db0765c2e959165e3b27f79940437fad1cfc4 Mon Sep 17 00:00:00 2001 From: jparisu Date: Tue, 25 Apr 2023 07:50:18 +0200 Subject: [PATCH 4/6] TMP new yaml reader with get_tags Signed-off-by: jparisu --- .../include/ddspipe_yaml/NewYamlReader.hpp | 311 ++++++++++++++++++ .../include/ddspipe_yaml/YamlReader.hpp | 129 ++++++++ .../src/cpp/NewYamlReader_generic.cpp | 86 +++++ ddspipe_yaml/src/cpp/YamlReader_types.cpp | 54 ++- .../test/unittest/entities/CMakeLists.txt | 1 + .../entities/new_topic/CMakeLists.txt | 43 +++ .../new_topic/NewYamlGetEntityTopicTest.cpp | 57 ++++ 7 files changed, 652 insertions(+), 29 deletions(-) create mode 100644 ddspipe_yaml/include/ddspipe_yaml/NewYamlReader.hpp create mode 100644 ddspipe_yaml/src/cpp/NewYamlReader_generic.cpp create mode 100644 ddspipe_yaml/test/unittest/entities/new_topic/CMakeLists.txt create mode 100644 ddspipe_yaml/test/unittest/entities/new_topic/NewYamlGetEntityTopicTest.cpp diff --git a/ddspipe_yaml/include/ddspipe_yaml/NewYamlReader.hpp b/ddspipe_yaml/include/ddspipe_yaml/NewYamlReader.hpp new file mode 100644 index 000000000..21238e72e --- /dev/null +++ b/ddspipe_yaml/include/ddspipe_yaml/NewYamlReader.hpp @@ -0,0 +1,311 @@ +// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include +#include + +#include +#include + +namespace eprosima { +namespace ddspipe { +namespace yaml { + +/** + * @brief Whether key \c tag is present in \c yml + * + * The key \c tag must be in base level of \c yml , it will no be looked for recursively. + * + * It could only look for keys in yaml map and empty yaml. It will fail in array and scalar cases. + * In this second case, return will always be false. + * + * @param yml base yaml + * @param tag key to look for in \c yml + * @return true id \c tag is a key in \c yml + * @return false otherwise + * + * @throw \c ConfigurationException if \c yml is not a map or empty yaml + */ +bool is_tag_present( + const Yaml& yml, + const TagType& tag); + +/** + * @brief Get the yaml inside key \c tag in \c yml + * + * \c tag must be present in \c yml . It uses \c is_tag_present to check the key is present. + * Use \c is_tag_present before calling this method in order to avoid the exception. + * + * @param yml base yaml + * @param tag key refering the value returned + * @return yaml value inside key \c tag + * + * @throw \c ConfigurationException if \c tag is not present in \c yml + */ +Yaml get_value_in_tag( + const Yaml& yml, + const TagType& tag); + +/** + * @brief Fill an element given by parameter with the values inside \c yml + * + * This method simplifies the process of retrieving an object whose parent has its own \c fill method, + * as the code is reused from one another calling parent \c fill in child. + * It is also very helpful to handle default creation values. Without this, every different default value + * must have its own if-else clause, forcing to create the respective constructor. With this method, + * the default values are initialized with the default constructor, and then are overwritten by the yaml. + * [this problem arises because C++ does not allow different order of parameters in method call] + */ +template +void fill( + T& object, + const Yaml& yml); + +//! Fill an element given by parameter with the values inside \c tag in \c yml +template +void fill( + T& object, + const Yaml& yml, + const TagType& tag); + +ENUMERATION_BUILDER +( + TagKind, + required, + optional +); + +struct IYamlFieldGetter +{ + IYamlFieldGetter( + TagKind kind_arg, + std::string tag_arg) + : kind(kind_arg) + , tag(tag_arg) + { + } + + virtual ~IYamlFieldGetter() = default; + virtual void get(const Yaml& yml) = 0; + + TagKind kind; + std::string tag; +}; + +template +struct YamlFieldGetter : IYamlFieldGetter +{ + YamlFieldGetter( + std::string tag_arg, + TagKind kind_arg, + T& vessel_arg) + : IYamlFieldGetter(kind_arg, tag_arg) + , vessel(vessel_arg) + { + } + + virtual void get( + const Yaml& yml) override + { + fill(vessel, yml, tag); + } + + T& vessel; +}; + +template +struct YamlFieldGetterFuzzy : IYamlFieldGetter +{ + YamlFieldGetterFuzzy( + std::string tag_arg, + TagKind kind_arg, + utils::Fuzzy& vessel_arg) + : IYamlFieldGetter(kind_arg, tag_arg) + , vessel(vessel_arg) + { + } + + virtual void get( + const Yaml& yml) override + { + fill(vessel.get_reference(), yml, tag); + vessel.set_level(); + } + + utils::Fuzzy& vessel; +}; + +template +std::vector yaml_reader_definition( + T& object); + +DDSPIPE_YAML_DllAPI +inline bool get_tags( + const Yaml& yml, + const std::vector& tags_to_get, + bool fail_with_extra_tags = true, + bool fail_with_exception = true) +{ + if (!yml.IsMap() && !yml.IsNull()) + { + throw eprosima::utils::ConfigurationException(STR_ENTRY + << "Trying to get tags: in a not yaml object map."); + } + + // First, check that every required flag is present + for (const auto& field : tags_to_get) + { + bool present = is_tag_present(yml, field->tag); + if (present) + { + field->get(yml); + } + else if (field->kind == TagKind::required) + { + if (fail_with_exception) + { + throw eprosima::utils::ConfigurationException(STR_ENTRY + << "Required tag <" << field->tag << "> not present."); + } + else + { + return false; + } + } + } + + // Now that every required field is present, check for extra tags + for (const auto& yaml_field : yml) + { + auto tag = yaml_field.first.as(); + bool should_be_present = false; + + // For each tag, check if it is inside allowed tags + for (const auto& field : tags_to_get) + { + if (field->tag == tag) + { + should_be_present = true; + break; + } + } + + // If present tag is not inside allowed ones, fail + if (!should_be_present) + { + if (fail_with_exception) + { + throw eprosima::utils::ConfigurationException(STR_ENTRY + << "Unexpected tag <" << tag << "> present."); + } + else + { + return false; + } + } + } + + return true; +} + +template +void fill( + T& object, + const Yaml& yml, + const TagType& tag) +{ + // ATTENTION: This try catch can be avoided, it is only used to add verbose information + try + { + fill(object, get_value_in_tag(yml, tag)); + } + catch (const std::exception& e) + { + throw eprosima::utils::ConfigurationException( + utils::Formatter() << + "Error filling object of type <" << TYPE_NAME(T) << + "> in tag <" << tag << "> :\n " << e.what()); + } +} + +template +void fill( + T& object, + const Yaml& yml) +{ + get_tags( + yml, + yaml_reader_definition(object) + ); +} + +template +T get_scalar( + const Yaml& yml) +{ + if (!yml.IsScalar()) + { + throw eprosima::utils::ConfigurationException( + utils::Formatter() << + "Trying to read a primitive value of type <" << TYPE_NAME(T) << "> from a non scalar yaml."); + } + + try + { + return yml.as(); + } + catch (const std::exception& e) + { + throw eprosima::utils::ConfigurationException( + utils::Formatter() << + "Incorrect format for primitive value, expected <" << TYPE_NAME(T) << ">:\n " << e.what()); + } +} + +template <> +void fill( + std::string& object, + const Yaml& yml) +{ + object = get_scalar(yml); +} + +template <> +std::vector yaml_reader_definition( + core::types::DdsTopic& object) +{ + return + { + new YamlFieldGetter("name", TagKind::required, object.m_topic_name), + new YamlFieldGetter("type", TagKind::required, object.type_name), + new YamlFieldGetter("qos", TagKind::optional, object.topic_qos) + }; +} + +template <> +void fill( + core::types::TopicQoS& object, + const Yaml& yml) +{ +} + +} /* namespace yaml */ +} /* namespace ddspipe */ +} /* namespace eprosima */ + +// Include implementation template file +// #include diff --git a/ddspipe_yaml/include/ddspipe_yaml/YamlReader.hpp b/ddspipe_yaml/include/ddspipe_yaml/YamlReader.hpp index 0769b17b6..a43c7e1fd 100644 --- a/ddspipe_yaml/include/ddspipe_yaml/YamlReader.hpp +++ b/ddspipe_yaml/include/ddspipe_yaml/YamlReader.hpp @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -270,6 +271,134 @@ bool check_tags( bool fail_with_extra_tags = true, bool fail_with_exception = true); +struct IYamlFieldGetter +{ + IYamlFieldGetter( + TagKind kind_arg, + std::string tag_arg) + : kind(kind_arg) + , tag(tag_arg) + { + } + + virtual ~IYamlFieldGetter() = default; + virtual void get(const Yaml& yml) = 0; + + TagKind kind; + std::string tag; +}; + +template +struct YamlFieldGetter : IYamlFieldGetter +{ + YamlFieldGetter( + std::string tag_arg, + TagKind kind_arg, + T& vessel_arg) + : IYamlFieldGetter(kind_arg, tag_arg) + , vessel(vessel_arg) + { + } + + virtual void get( + const Yaml& yml) override + { + vessel = YamlReader::get(yml, tag, YamlReaderVersion::LATEST); + } + + T& vessel; +}; + +template +struct YamlFieldGetterFuzzy : IYamlFieldGetter +{ + YamlFieldGetterFuzzy( + std::string tag_arg, + TagKind kind_arg, + utils::Fuzzy& vessel_arg) + : IYamlFieldGetter(kind_arg, tag_arg) + , vessel(vessel_arg) + { + } + + virtual void get( + const Yaml& yml) override + { + vessel.set_value(YamlReader::get(yml, tag, YamlReaderVersion::LATEST)); + } + + utils::Fuzzy& vessel; +}; + +DDSPIPE_YAML_DllAPI +inline bool get_tags( + const std::vector& tags_to_get, + const Yaml& yml, + bool fail_with_extra_tags = true, + bool fail_with_exception = true) +{ + if (!yml.IsMap() && !yml.IsNull()) + { + throw eprosima::utils::ConfigurationException(STR_ENTRY + << "Trying to get tags: in a not yaml object map."); + } + + // First, check that every required flag is present + for (const auto& field : tags_to_get) + { + bool present = YamlReader::is_tag_present(yml, field->tag); + if (present) + { + field->get(yml); + } + else if (field->kind == TagKind::required) + { + if (fail_with_exception) + { + throw eprosima::utils::ConfigurationException(STR_ENTRY + << "Required tag <" << field->tag << "> not present."); + } + else + { + return false; + } + } + } + + // Now that every required field is present, check for extra tags + for (const auto& yaml_field : yml) + { + auto tag = yaml_field.first.as(); + bool should_be_present = false; + + // For each tag, check if it is inside allowed tags + for (const auto& field : tags_to_get) + { + if (field->tag == tag) + { + should_be_present = true; + break; + } + } + + // If present tag is not inside allowed ones, fail + if (!should_be_present) + { + if (fail_with_exception) + { + throw eprosima::utils::ConfigurationException(STR_ENTRY + << "Unexpected tag <" << tag << "> present."); + } + else + { + return false; + } + } + } + + return true; +} + /** * @brief \c YamlReaderVersion to stream serialization */ diff --git a/ddspipe_yaml/src/cpp/NewYamlReader_generic.cpp b/ddspipe_yaml/src/cpp/NewYamlReader_generic.cpp new file mode 100644 index 000000000..5b9f815e2 --- /dev/null +++ b/ddspipe_yaml/src/cpp/NewYamlReader_generic.cpp @@ -0,0 +1,86 @@ +// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @file YamlReader.cpp + * + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace eprosima { +namespace ddspipe { +namespace yaml { + +using namespace eprosima::ddspipe::core::types; +using namespace eprosima::ddspipe::participants::types; + +/************************ +* GENERIC * +************************/ + +bool is_tag_present( + const Yaml& yml, + const TagType& tag) +{ + if (!yml.IsMap() && !yml.IsNull()) + { + throw eprosima::utils::ConfigurationException( + utils::Formatter() << "Trying to find a tag: <" << tag << "> in a not yaml object map."); + } + + // Explicit conversion to avoid windows format warning + // This method performace is the same as only retrieving bool + return (yml[tag]) ? true : false; +} + +Yaml get_value_in_tag( + const Yaml& yml, + const TagType& tag) +{ + if (is_tag_present(yml, tag)) + { + return yml[tag]; + } + else + { + throw eprosima::utils::ConfigurationException( + utils::Formatter() << "Required tag not found: <" << tag << ">."); + } +} + +} /* namespace yaml */ +} /* namespace ddspipe */ +} /* namespace eprosima */ diff --git a/ddspipe_yaml/src/cpp/YamlReader_types.cpp b/ddspipe_yaml/src/cpp/YamlReader_types.cpp index 543cd2dad..5285ce641 100644 --- a/ddspipe_yaml/src/cpp/YamlReader_types.cpp +++ b/ddspipe_yaml/src/cpp/YamlReader_types.cpp @@ -384,6 +384,17 @@ void YamlReader::fill( } } +template <> +DDSPIPE_YAML_DllAPI +TopicQoS YamlReader::get( + const Yaml& yml, + const YamlReaderVersion version) +{ + TopicQoS qos; + fill(qos, yml, version); + return qos; +} + /************************ * TOPICS * ************************/ @@ -395,29 +406,17 @@ void YamlReader::fill( const Yaml& yml, const YamlReaderVersion version) { - static const std::vector fields_allowed = + static const std::vector fields_allowed = { - { TagKind::required, TOPIC_NAME_TAG }, - { TagKind::required, TOPIC_TYPE_NAME_TAG }, - { TagKind::optional, TOPIC_QOS_TAG }, + new YamlFieldGetter(TOPIC_NAME_TAG, TagKind::required, object.m_topic_name), + new YamlFieldGetter(TOPIC_TYPE_NAME_TAG, TagKind::required, object.type_name), + new YamlFieldGetter(TOPIC_QOS_TAG, TagKind::optional, object.topic_qos) }; - check_tags( + get_tags( fields_allowed, yml - ); - - // Name required - object.m_topic_name = get(yml, TOPIC_NAME_TAG, version); - - // Data Type required - object.type_name = get(yml, TOPIC_TYPE_NAME_TAG, version); - - // Optional QoS - if (is_tag_present(yml, TOPIC_QOS_TAG)) - { - fill(object.topic_qos, get_value_in_tag(yml, TOPIC_QOS_TAG), version); - } + ); } template <> @@ -438,19 +437,16 @@ void YamlReader::fill( const Yaml& yml, const YamlReaderVersion version) { - // Optional name - if (is_tag_present(yml, TOPIC_NAME_TAG)) - { - object.topic_name.set_value(get(yml, TOPIC_NAME_TAG, version)); - } - - // Optional data type - if (is_tag_present(yml, TOPIC_TYPE_NAME_TAG)) + static const std::vector fields_allowed = { - object.type_name.set_value(get(yml, TOPIC_TYPE_NAME_TAG, version)); - } + new YamlFieldGetterFuzzy(TOPIC_NAME_TAG, TagKind::optional, object.topic_name), + new YamlFieldGetterFuzzy(TOPIC_TYPE_NAME_TAG, TagKind::optional, object.type_name) + }; - // TODO: decide whether we want to use QoS as filtering + get_tags( + fields_allowed, + yml + ); } template <> diff --git a/ddspipe_yaml/test/unittest/entities/CMakeLists.txt b/ddspipe_yaml/test/unittest/entities/CMakeLists.txt index 7920290b5..0176f2b68 100644 --- a/ddspipe_yaml/test/unittest/entities/CMakeLists.txt +++ b/ddspipe_yaml/test/unittest/entities/CMakeLists.txt @@ -19,3 +19,4 @@ add_subdirectory(address) add_subdirectory(guid) add_subdirectory(topic) +add_subdirectory(new_topic) diff --git a/ddspipe_yaml/test/unittest/entities/new_topic/CMakeLists.txt b/ddspipe_yaml/test/unittest/entities/new_topic/CMakeLists.txt new file mode 100644 index 000000000..1fe0f96e6 --- /dev/null +++ b/ddspipe_yaml/test/unittest/entities/new_topic/CMakeLists.txt @@ -0,0 +1,43 @@ +# Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +############################### +# Yaml GetEntities Topic Test # +############################### + +set(TEST_NAME NewYamlGetEntityTopicTest) + +set(TEST_SOURCES + NewYamlGetEntityTopicTest.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/NewYamlReader_generic.cpp + ) + +set(TEST_LIST + get_real_topic + ) + +set(TEST_EXTRA_LIBRARIES + yaml-cpp + fastcdr + fastrtps + cpp_utils + ddspipe_core + ddspipe_participants + ) + +add_unittest_executable( + "${TEST_NAME}" + "${TEST_SOURCES}" + "${TEST_LIST}" + "${TEST_EXTRA_LIBRARIES}") diff --git a/ddspipe_yaml/test/unittest/entities/new_topic/NewYamlGetEntityTopicTest.cpp b/ddspipe_yaml/test/unittest/entities/new_topic/NewYamlGetEntityTopicTest.cpp new file mode 100644 index 000000000..20670ea23 --- /dev/null +++ b/ddspipe_yaml/test/unittest/entities/new_topic/NewYamlGetEntityTopicTest.cpp @@ -0,0 +1,57 @@ +// Copyright 2021 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include + +#include +#include +#include + +#include + +using namespace eprosima; +using namespace eprosima::ddspipe::yaml; + +TEST(NewYamlGetEntityTopicTest, get_real_topic) +{ + Yaml yml_topic; + // yml_topic["name"] = "something"; + yml_topic["type"] = "s"; + yml_topic["qos"] = "something"; + // yml_topic["r"] = "something"; + + Yaml yml; + yml["topic"] = yml_topic; + + ddspipe::core::types::DdsTopic topic; + fill(topic, yml, "topic"); + + ddspipe::core::types::DdsTopic real_topic; + real_topic.m_topic_name = "something"; + real_topic.type_name = "s"; + + ASSERT_EQ(topic, real_topic); + +} + +int main( + int argc, + char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} From 1f4a23ff7dca5ef486712df084c2fb8e74dd9ace Mon Sep 17 00:00:00 2001 From: jparisu Date: Thu, 27 Apr 2023 15:17:52 +0200 Subject: [PATCH 5/6] Implementation of new YamlReader and Writer Signed-off-by: jparisu --- .gitignore | 2 - .../ddspipe_core/testing/arbitrary_values.hpp | 34 +- .../src/cpp/testing/arbitrary_values.cpp | 172 ++++++++ .../src/cpp/types/topic/dds/DdsTopic.cpp | 2 +- .../include/ddspipe_yaml/NewYamlReader.hpp | 311 ------------- .../include/ddspipe_yaml/YamlReader.hpp | 415 ------------------ .../{YamlWriter.hpp => core/Yaml.hpp} | 88 ++-- .../YamlFileManager.hpp} | 4 +- .../ddspipe_yaml/field/YamlObjectField.hpp | 70 +++ .../field/impl/YamlObjectField.ipp | 174 ++++++++ .../include/ddspipe_yaml/impl/YamlReader.ipp | 241 ---------- .../ddspipe_yaml/reader/YamlReader.hpp | 71 +++ .../reader/impl/YamlReader_declarations.ipp | 43 ++ .../impl/YamlReader_implementations.ipp | 100 +++++ .../{ => tags}/yaml_configuration_tags.hpp | 0 .../ddspipe_yaml/testing/generate_yaml.hpp | 210 --------- .../ddspipe_yaml/writer/YamlWriter.hpp | 63 +++ .../writer/impl/YamlWriter_declarations.ipp | 96 ++++ .../impl/YamlWriter_implementations.ipp} | 111 ++--- ...generic.cpp => NewYamlReader_generic._cpp} | 2 +- ...er_generic.cpp => YamlReader_generic._cpp} | 2 +- ...pants.cpp => YamlReader_participants._cpp} | 2 +- ...Reader_types.cpp => YamlReader_types._cpp} | 2 +- .../src/cpp/{YamlWriter.cpp => core/Yaml.cpp} | 65 +-- .../src/cpp/{ => core}/YamlManager.cpp | 11 +- .../src/cpp/field/YamlObjectField_generic.cpp | 94 ++++ .../src/cpp/field/YamlObjectField_types.cpp | 70 +++ .../src/cpp/reader/YamlReader_generic.cpp | 74 ++++ .../src/cpp/reader/YamlReader_types.cpp | 97 ++++ .../src/cpp/writer/YamlWriter_generic.cpp | 77 ++++ .../src/cpp/writer/YamlWriter_types.cpp | 61 +++ ddspipe_yaml/test/unittest/CMakeLists.txt | 4 +- .../test/unittest/entities/CMakeLists.txt | 8 +- .../unittest/entities/address/CMakeLists.txt | 12 +- .../{new_topic => generic}/CMakeLists.txt | 24 +- .../generic/GenericReaderWriterTest.cpp | 97 ++++ .../unittest/entities/guid/CMakeLists.txt | 6 +- .../new_topic/NewYamlGetEntityTopicTest.cpp | 57 --- .../unittest/entities/topic/CMakeLists.txt | 6 +- .../test/unittest/yaml_reader/CMakeLists.txt | 6 +- .../test/unittest/yaml_writer/CMakeLists.txt | 8 +- 41 files changed, 1549 insertions(+), 1443 deletions(-) rename ddspipe_yaml/include/ddspipe_yaml/Yaml.hpp => ddspipe_core/include/ddspipe_core/testing/arbitrary_values.hpp (53%) create mode 100644 ddspipe_core/src/cpp/testing/arbitrary_values.cpp delete mode 100644 ddspipe_yaml/include/ddspipe_yaml/NewYamlReader.hpp delete mode 100644 ddspipe_yaml/include/ddspipe_yaml/YamlReader.hpp rename ddspipe_yaml/include/ddspipe_yaml/{YamlWriter.hpp => core/Yaml.hpp} (51%) rename ddspipe_yaml/include/ddspipe_yaml/{YamlManager.hpp => core/YamlFileManager.hpp} (92%) create mode 100644 ddspipe_yaml/include/ddspipe_yaml/field/YamlObjectField.hpp create mode 100644 ddspipe_yaml/include/ddspipe_yaml/field/impl/YamlObjectField.ipp delete mode 100644 ddspipe_yaml/include/ddspipe_yaml/impl/YamlReader.ipp create mode 100644 ddspipe_yaml/include/ddspipe_yaml/reader/YamlReader.hpp create mode 100644 ddspipe_yaml/include/ddspipe_yaml/reader/impl/YamlReader_declarations.ipp create mode 100644 ddspipe_yaml/include/ddspipe_yaml/reader/impl/YamlReader_implementations.ipp rename ddspipe_yaml/include/ddspipe_yaml/{ => tags}/yaml_configuration_tags.hpp (100%) delete mode 100644 ddspipe_yaml/include/ddspipe_yaml/testing/generate_yaml.hpp create mode 100644 ddspipe_yaml/include/ddspipe_yaml/writer/YamlWriter.hpp create mode 100644 ddspipe_yaml/include/ddspipe_yaml/writer/impl/YamlWriter_declarations.ipp rename ddspipe_yaml/include/ddspipe_yaml/{impl/YamlWriter.ipp => writer/impl/YamlWriter_implementations.ipp} (60%) rename ddspipe_yaml/src/cpp/{NewYamlReader_generic.cpp => NewYamlReader_generic._cpp} (98%) rename ddspipe_yaml/src/cpp/{YamlReader_generic.cpp => YamlReader_generic._cpp} (99%) rename ddspipe_yaml/src/cpp/{YamlReader_participants.cpp => YamlReader_participants._cpp} (99%) rename ddspipe_yaml/src/cpp/{YamlReader_types.cpp => YamlReader_types._cpp} (99%) rename ddspipe_yaml/src/cpp/{YamlWriter.cpp => core/Yaml.cpp} (60%) rename ddspipe_yaml/src/cpp/{ => core}/YamlManager.cpp (89%) create mode 100644 ddspipe_yaml/src/cpp/field/YamlObjectField_generic.cpp create mode 100644 ddspipe_yaml/src/cpp/field/YamlObjectField_types.cpp create mode 100644 ddspipe_yaml/src/cpp/reader/YamlReader_generic.cpp create mode 100644 ddspipe_yaml/src/cpp/reader/YamlReader_types.cpp create mode 100644 ddspipe_yaml/src/cpp/writer/YamlWriter_generic.cpp create mode 100644 ddspipe_yaml/src/cpp/writer/YamlWriter_types.cpp rename ddspipe_yaml/test/unittest/entities/{new_topic => generic}/CMakeLists.txt (65%) create mode 100644 ddspipe_yaml/test/unittest/entities/generic/GenericReaderWriterTest.cpp delete mode 100644 ddspipe_yaml/test/unittest/entities/new_topic/NewYamlGetEntityTopicTest.cpp diff --git a/.gitignore b/.gitignore index d666beed9..9e75015b6 100644 --- a/.gitignore +++ b/.gitignore @@ -42,8 +42,6 @@ Session.vim # temporary .netrwhist *~ -# auto-generated tag files -tags build/ install/ diff --git a/ddspipe_yaml/include/ddspipe_yaml/Yaml.hpp b/ddspipe_core/include/ddspipe_core/testing/arbitrary_values.hpp similarity index 53% rename from ddspipe_yaml/include/ddspipe_yaml/Yaml.hpp rename to ddspipe_core/include/ddspipe_core/testing/arbitrary_values.hpp index 19f86756d..6d928ad9f 100644 --- a/ddspipe_yaml/include/ddspipe_yaml/Yaml.hpp +++ b/ddspipe_core/include/ddspipe_core/testing/arbitrary_values.hpp @@ -1,4 +1,4 @@ -// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// Copyright 2021 Proyectos y Sistemas de Mantenimiento SL (eProsima). // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -10,29 +10,25 @@ // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and -// limitations under the License. +// limitations under the License\. #pragma once -#include - -#include - namespace eprosima { +namespace ddspipe { +namespace core { +namespace testing { -/** - * Configuration is in dictionary format - * - * YAML spec: https://yaml.org/spec/1.2.2/ - * - * @note It is not legal to repeat keys in a YAML - * - * @note this object contains a reference to the actual yaml object. Thus passing it by value or by reference - * makes no different. - */ -using Yaml = YAML::Node; +template +T arbitrary( + unsigned int seed = 0); -//! Type of tag in the yaml -using TagType = std::string; +template +T arbitrary( + unsigned int seed, + Args... args); +} /* namespace testing */ +} /* namespace core */ +} /* namespace ddspipe */ } /* namespace eprosima */ diff --git a/ddspipe_core/src/cpp/testing/arbitrary_values.cpp b/ddspipe_core/src/cpp/testing/arbitrary_values.cpp new file mode 100644 index 000000000..e3d5b469d --- /dev/null +++ b/ddspipe_core/src/cpp/testing/arbitrary_values.cpp @@ -0,0 +1,172 @@ +// Copyright 2021 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace eprosima { +namespace ddspipe { +namespace core { +namespace testing { + +using namespace eprosima::ddspipe::core::types; + +//////////////////////////////////////////////// +// DEFAULT ARBITRARY +//////////////////////////////////////////////// + +template <> +TopicQoS arbitrary( + unsigned int seed /* = 0 */) +{ + TopicQoS qos; + + if (seed % 2) + { + qos.reliability_qos = ReliabilityKind::BEST_EFFORT; + } + else + { + qos.reliability_qos = ReliabilityKind::RELIABLE; + } + + seed /= 2; + + switch ((seed / 2) % 4) + { + case 0: + qos.durability_qos = DurabilityKind::VOLATILE; + break; + + case 1: + qos.durability_qos = DurabilityKind::TRANSIENT_LOCAL; + break; + + case 2: + qos.durability_qos = DurabilityKind::TRANSIENT; + break; + + case 3: + qos.durability_qos = DurabilityKind::PERSISTENT; + break; + + default: + break; + } + + seed /= 4; + + if (seed % 2) + { + qos.ownership_qos = OwnershipQosPolicyKind::SHARED_OWNERSHIP_QOS; + } + else + { + qos.ownership_qos = OwnershipQosPolicyKind::EXCLUSIVE_OWNERSHIP_QOS; + } + + seed /= 2; + + qos.use_partitions = ((seed % 2) == 0); + + seed /= 2; + + qos.keyed = ((seed % 2) == 0); + + seed /= 2; + + qos.downsampling = (seed % 2) + 1; + + seed /= 2; + + qos.max_reception_rate = seed % 2; + + seed /= 2; + + qos.history_depth = seed; + + return qos; +} + +template <> +DdsTopic arbitrary( + unsigned int seed /* = 0 */) +{ + DdsTopic topic; + topic.m_topic_name = "TopicName_" + std::to_string(seed); + topic.type_name = "TopicType_" + std::to_string(seed); + topic.topic_qos = arbitrary(seed); + return topic; +} + +template <> +WildcardDdsFilterTopic arbitrary( + unsigned int seed /* = 0 */) +{ + WildcardDdsFilterTopic topic; + + if (seed % 2) + { + topic.topic_name.set_value("TopicName_" + std::to_string(seed)); + } + + seed /= 2; + + if (seed % 2) + { + topic.type_name.set_value("TopicName_" + std::to_string(seed)); + } + + return topic; +} + +//////////////////////////////////////////////// +// SPECIFIC ARBITRARY +//////////////////////////////////////////////// + +template <> +TopicQoS arbitrary( + unsigned int seed, + bool limit_durability) +{ + TopicQoS qos = arbitrary(seed); + + // Reliability + seed /= 2; + + if (seed % 2) + { + qos.durability_qos = DurabilityKind::VOLATILE; + } + else + { + qos.durability_qos = DurabilityKind::TRANSIENT_LOCAL; + } + + return qos; +} + +} /* namespace testing */ +} /* namespace core */ +} /* namespace ddspipe */ +} /* namespace eprosima */ diff --git a/ddspipe_core/src/cpp/types/topic/dds/DdsTopic.cpp b/ddspipe_core/src/cpp/types/topic/dds/DdsTopic.cpp index aa8bbc084..e91f60c62 100644 --- a/ddspipe_core/src/cpp/types/topic/dds/DdsTopic.cpp +++ b/ddspipe_core/src/cpp/types/topic/dds/DdsTopic.cpp @@ -37,7 +37,7 @@ std::ostream& operator <<( std::ostream& os, const DdsTopic& t) { - os << "DdsTopic{" << t.topic_name() << ";" << t.type_name << t.topic_qos << "}"; + os << "DdsTopic{" << t.topic_name() << ";" << t.type_name << ";" << t.topic_qos << "}"; return os; } diff --git a/ddspipe_yaml/include/ddspipe_yaml/NewYamlReader.hpp b/ddspipe_yaml/include/ddspipe_yaml/NewYamlReader.hpp deleted file mode 100644 index 21238e72e..000000000 --- a/ddspipe_yaml/include/ddspipe_yaml/NewYamlReader.hpp +++ /dev/null @@ -1,311 +0,0 @@ -// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include -#include -#include - -#include -#include - -namespace eprosima { -namespace ddspipe { -namespace yaml { - -/** - * @brief Whether key \c tag is present in \c yml - * - * The key \c tag must be in base level of \c yml , it will no be looked for recursively. - * - * It could only look for keys in yaml map and empty yaml. It will fail in array and scalar cases. - * In this second case, return will always be false. - * - * @param yml base yaml - * @param tag key to look for in \c yml - * @return true id \c tag is a key in \c yml - * @return false otherwise - * - * @throw \c ConfigurationException if \c yml is not a map or empty yaml - */ -bool is_tag_present( - const Yaml& yml, - const TagType& tag); - -/** - * @brief Get the yaml inside key \c tag in \c yml - * - * \c tag must be present in \c yml . It uses \c is_tag_present to check the key is present. - * Use \c is_tag_present before calling this method in order to avoid the exception. - * - * @param yml base yaml - * @param tag key refering the value returned - * @return yaml value inside key \c tag - * - * @throw \c ConfigurationException if \c tag is not present in \c yml - */ -Yaml get_value_in_tag( - const Yaml& yml, - const TagType& tag); - -/** - * @brief Fill an element given by parameter with the values inside \c yml - * - * This method simplifies the process of retrieving an object whose parent has its own \c fill method, - * as the code is reused from one another calling parent \c fill in child. - * It is also very helpful to handle default creation values. Without this, every different default value - * must have its own if-else clause, forcing to create the respective constructor. With this method, - * the default values are initialized with the default constructor, and then are overwritten by the yaml. - * [this problem arises because C++ does not allow different order of parameters in method call] - */ -template -void fill( - T& object, - const Yaml& yml); - -//! Fill an element given by parameter with the values inside \c tag in \c yml -template -void fill( - T& object, - const Yaml& yml, - const TagType& tag); - -ENUMERATION_BUILDER -( - TagKind, - required, - optional -); - -struct IYamlFieldGetter -{ - IYamlFieldGetter( - TagKind kind_arg, - std::string tag_arg) - : kind(kind_arg) - , tag(tag_arg) - { - } - - virtual ~IYamlFieldGetter() = default; - virtual void get(const Yaml& yml) = 0; - - TagKind kind; - std::string tag; -}; - -template -struct YamlFieldGetter : IYamlFieldGetter -{ - YamlFieldGetter( - std::string tag_arg, - TagKind kind_arg, - T& vessel_arg) - : IYamlFieldGetter(kind_arg, tag_arg) - , vessel(vessel_arg) - { - } - - virtual void get( - const Yaml& yml) override - { - fill(vessel, yml, tag); - } - - T& vessel; -}; - -template -struct YamlFieldGetterFuzzy : IYamlFieldGetter -{ - YamlFieldGetterFuzzy( - std::string tag_arg, - TagKind kind_arg, - utils::Fuzzy& vessel_arg) - : IYamlFieldGetter(kind_arg, tag_arg) - , vessel(vessel_arg) - { - } - - virtual void get( - const Yaml& yml) override - { - fill(vessel.get_reference(), yml, tag); - vessel.set_level(); - } - - utils::Fuzzy& vessel; -}; - -template -std::vector yaml_reader_definition( - T& object); - -DDSPIPE_YAML_DllAPI -inline bool get_tags( - const Yaml& yml, - const std::vector& tags_to_get, - bool fail_with_extra_tags = true, - bool fail_with_exception = true) -{ - if (!yml.IsMap() && !yml.IsNull()) - { - throw eprosima::utils::ConfigurationException(STR_ENTRY - << "Trying to get tags: in a not yaml object map."); - } - - // First, check that every required flag is present - for (const auto& field : tags_to_get) - { - bool present = is_tag_present(yml, field->tag); - if (present) - { - field->get(yml); - } - else if (field->kind == TagKind::required) - { - if (fail_with_exception) - { - throw eprosima::utils::ConfigurationException(STR_ENTRY - << "Required tag <" << field->tag << "> not present."); - } - else - { - return false; - } - } - } - - // Now that every required field is present, check for extra tags - for (const auto& yaml_field : yml) - { - auto tag = yaml_field.first.as(); - bool should_be_present = false; - - // For each tag, check if it is inside allowed tags - for (const auto& field : tags_to_get) - { - if (field->tag == tag) - { - should_be_present = true; - break; - } - } - - // If present tag is not inside allowed ones, fail - if (!should_be_present) - { - if (fail_with_exception) - { - throw eprosima::utils::ConfigurationException(STR_ENTRY - << "Unexpected tag <" << tag << "> present."); - } - else - { - return false; - } - } - } - - return true; -} - -template -void fill( - T& object, - const Yaml& yml, - const TagType& tag) -{ - // ATTENTION: This try catch can be avoided, it is only used to add verbose information - try - { - fill(object, get_value_in_tag(yml, tag)); - } - catch (const std::exception& e) - { - throw eprosima::utils::ConfigurationException( - utils::Formatter() << - "Error filling object of type <" << TYPE_NAME(T) << - "> in tag <" << tag << "> :\n " << e.what()); - } -} - -template -void fill( - T& object, - const Yaml& yml) -{ - get_tags( - yml, - yaml_reader_definition(object) - ); -} - -template -T get_scalar( - const Yaml& yml) -{ - if (!yml.IsScalar()) - { - throw eprosima::utils::ConfigurationException( - utils::Formatter() << - "Trying to read a primitive value of type <" << TYPE_NAME(T) << "> from a non scalar yaml."); - } - - try - { - return yml.as(); - } - catch (const std::exception& e) - { - throw eprosima::utils::ConfigurationException( - utils::Formatter() << - "Incorrect format for primitive value, expected <" << TYPE_NAME(T) << ">:\n " << e.what()); - } -} - -template <> -void fill( - std::string& object, - const Yaml& yml) -{ - object = get_scalar(yml); -} - -template <> -std::vector yaml_reader_definition( - core::types::DdsTopic& object) -{ - return - { - new YamlFieldGetter("name", TagKind::required, object.m_topic_name), - new YamlFieldGetter("type", TagKind::required, object.type_name), - new YamlFieldGetter("qos", TagKind::optional, object.topic_qos) - }; -} - -template <> -void fill( - core::types::TopicQoS& object, - const Yaml& yml) -{ -} - -} /* namespace yaml */ -} /* namespace ddspipe */ -} /* namespace eprosima */ - -// Include implementation template file -// #include diff --git a/ddspipe_yaml/include/ddspipe_yaml/YamlReader.hpp b/ddspipe_yaml/include/ddspipe_yaml/YamlReader.hpp deleted file mode 100644 index a43c7e1fd..000000000 --- a/ddspipe_yaml/include/ddspipe_yaml/YamlReader.hpp +++ /dev/null @@ -1,415 +0,0 @@ -// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include -#include -#include - -#include -#include - -namespace eprosima { -namespace ddspipe { -namespace yaml { - -/** - * @brief Yaml Configuration Version - * - * With each version, the yaml format could change in different aspects - * - new fields - * - eliminate fields - * - change key words / tags - * - change requirement of field - * - etc. - * - * Each version is stored in a value of this enumeration, and each \c get method (without tag) is speciallized - * for each different versions. - * - * There is a version call \c LATEST that refers to the lates valid Yaml Configuration Versions. - * Most elemnts will not change from a version change to other. Thus, all of them must be implemented as - * the \c LATEST version, and then if a specific object changes with a specific version, implement that version - * (not latest) separately. - * This way, any new version uses \c LATEST as default implementation, and old versions that diverged from latest - * have their own implementation. As old versions will not change, those methods are definitive, and do not - * need further maintenance. - */ -enum YamlReaderVersion -{ - /** - * @brief First version. - * - * @version 0.1.0 - * @version 0.2.0 - */ - V_1_0, - - /** - * @brief Version 2.0 - * - * @version 0.3.0 - * - * - Adds builtin-topics tag. - * - Adds participants list. - * - Changes the parent of guid for DS to a new tag discovery-server-guid. - * - Adds domain tag in Address to remplace ip when DNS. - */ - V_2_0, - - /** - * @brief Latest version. - * - * @version 0.4.0 - * - * - Change wan to initial peers participant - * - Add Specs - */ - V_3_0, - - /** - * @brief Main version. - * - * This is the version used when the method is not specialized regarding the version, - * or the latest version when it does. - */ - LATEST, -}; - -/** - * @brief Class that encapsulates methods to get values from Yaml Node. - * - * Every method is implemented - */ -class DDSPIPE_YAML_DllAPI YamlReader -{ -public: - - /** - * @brief Whether key \c tag is present in \c yml - * - * The key \c tag must be in base level of \c yml , it will no be looked for recursively. - * - * It could only look for keys in yaml map and empty yaml. It will fail in array and scalar cases. - * In this second case, return will always be false. - * - * @param yml base yaml - * @param tag key to look for in \c yml - * @return true id \c tag is a key in \c yml - * @return false otherwise - * - * @throw \c ConfigurationException if \c yml is not a map or empty yaml - */ - static bool is_tag_present( - const Yaml& yml, - const TagType& tag); - - /** - * @brief Get the yaml inside key \c tag in \c yml - * - * \c tag must be present in \c yml . It uses \c is_tag_present to check the key is present. - * Use \c is_tag_present before calling this method in order to avoid the exception. - * - * @param yml base yaml - * @param tag key refering the value returned - * @return yaml value inside key \c tag - * - * @throw \c ConfigurationException if \c tag is not present in \c yml - */ - static Yaml get_value_in_tag( - const Yaml& yml, - const TagType& tag); - - //! TODO comment - template - static T get( - const Yaml& yml, - const YamlReaderVersion version); - - //! Get element inside \c tag - template - static T get( - const Yaml& yml, - const TagType& tag, - const YamlReaderVersion version); - - /** - * @brief Fill an element given by parameter with the values inside \c yml - * - * This method simplifies the process of retrieving an object whose parent has its own \c fill method, - * as the code is reused from one another calling parent \c fill in child. - * It is also very helpful to handle default creation values. Without this, every different default value - * must have its own if-else clause, forcing to create the respective constructor. With this method, - * the default values are initialized with the default constructor, and then are overwritten by the yaml. - * [this problem arises because C++ does not allow different order of parameters in method call] - */ - template - static void fill( - T& object, - const Yaml& yml, - const YamlReaderVersion version); - - //! Fill an element given by parameter with the values inside \c tag in \c yml - template - static void fill( - T& object, - const Yaml& yml, - const TagType& tag, - const YamlReaderVersion version); - - //! TODO comment - template - static std::list get_list( - const Yaml& yml, - const YamlReaderVersion version); - - //! Get list inside \c tag - template - static std::list get_list( - const Yaml& yml, - const TagType& tag, - const YamlReaderVersion version); - - /** - * @brief Get set inside \c tag - * - * It get a list of elements T with \c get_list and then convert it to set. - * - * @tparam T type of each object of the set - * @param yml base yaml - * @param tag key to yaml containing set - * @param version configuration version - * @return set of elements - */ - template - static std::set get_set( - const Yaml& yml, - const TagType& tag, - const YamlReaderVersion version); - - //! TODO comment - template - static T get_scalar( - const Yaml& yml); - - //! Get scalar value inside \c tag - template - static T get_scalar( - const Yaml& yml, - const TagType& tag); - - //! Get positive integer value inside \c tag - static unsigned int get_positive_int( - const Yaml& yml, - const TagType& tag); - - //! Get positive float value inside \c tag - static float get_positive_float( - const Yaml& yml, - const TagType& tag); - - //! Get positive double value inside \c tag - static double get_positive_double( - const Yaml& yml, - const TagType& tag); - - //! TODO comment - template - static T get_enumeration( - const Yaml& yml, - const std::map& enum_values); - - //! Get enumeration value inside \c tag - template - static T get_enumeration( - const Yaml& yml, - const TagType& tag, - const std::map& enum_values); - - //! TODO comment - template - static T get_enumeration_from_builder( - const Yaml& yml, - const utils::EnumBuilder& enum_builder); - - //! Get enumeration value inside \c tag - template - static T get_enumeration_from_builder( - const Yaml& yml, - const TagType& tag, - const utils::EnumBuilder& enum_builder); -}; - -ENUMERATION_BUILDER -( - TagKind, - required, - optional -); - -struct YamlFieldCheck -{ - TagKind kind; - std::string tag; -}; - -DDSPIPE_YAML_DllAPI -bool check_tags( - const std::vector& tags_allowed, - const Yaml& yml, - bool fail_with_extra_tags = true, - bool fail_with_exception = true); - -struct IYamlFieldGetter -{ - IYamlFieldGetter( - TagKind kind_arg, - std::string tag_arg) - : kind(kind_arg) - , tag(tag_arg) - { - } - - virtual ~IYamlFieldGetter() = default; - virtual void get(const Yaml& yml) = 0; - - TagKind kind; - std::string tag; -}; - -template -struct YamlFieldGetter : IYamlFieldGetter -{ - YamlFieldGetter( - std::string tag_arg, - TagKind kind_arg, - T& vessel_arg) - : IYamlFieldGetter(kind_arg, tag_arg) - , vessel(vessel_arg) - { - } - - virtual void get( - const Yaml& yml) override - { - vessel = YamlReader::get(yml, tag, YamlReaderVersion::LATEST); - } - - T& vessel; -}; - -template -struct YamlFieldGetterFuzzy : IYamlFieldGetter -{ - YamlFieldGetterFuzzy( - std::string tag_arg, - TagKind kind_arg, - utils::Fuzzy& vessel_arg) - : IYamlFieldGetter(kind_arg, tag_arg) - , vessel(vessel_arg) - { - } - - virtual void get( - const Yaml& yml) override - { - vessel.set_value(YamlReader::get(yml, tag, YamlReaderVersion::LATEST)); - } - - utils::Fuzzy& vessel; -}; - -DDSPIPE_YAML_DllAPI -inline bool get_tags( - const std::vector& tags_to_get, - const Yaml& yml, - bool fail_with_extra_tags = true, - bool fail_with_exception = true) -{ - if (!yml.IsMap() && !yml.IsNull()) - { - throw eprosima::utils::ConfigurationException(STR_ENTRY - << "Trying to get tags: in a not yaml object map."); - } - - // First, check that every required flag is present - for (const auto& field : tags_to_get) - { - bool present = YamlReader::is_tag_present(yml, field->tag); - if (present) - { - field->get(yml); - } - else if (field->kind == TagKind::required) - { - if (fail_with_exception) - { - throw eprosima::utils::ConfigurationException(STR_ENTRY - << "Required tag <" << field->tag << "> not present."); - } - else - { - return false; - } - } - } - - // Now that every required field is present, check for extra tags - for (const auto& yaml_field : yml) - { - auto tag = yaml_field.first.as(); - bool should_be_present = false; - - // For each tag, check if it is inside allowed tags - for (const auto& field : tags_to_get) - { - if (field->tag == tag) - { - should_be_present = true; - break; - } - } - - // If present tag is not inside allowed ones, fail - if (!should_be_present) - { - if (fail_with_exception) - { - throw eprosima::utils::ConfigurationException(STR_ENTRY - << "Unexpected tag <" << tag << "> present."); - } - else - { - return false; - } - } - } - - return true; -} - -/** - * @brief \c YamlReaderVersion to stream serialization - */ -DDSPIPE_YAML_DllAPI -std::ostream& operator <<( - std::ostream& os, - const YamlReaderVersion& version); - -} /* namespace yaml */ -} /* namespace ddspipe */ -} /* namespace eprosima */ - -// Include implementation template file -#include diff --git a/ddspipe_yaml/include/ddspipe_yaml/YamlWriter.hpp b/ddspipe_yaml/include/ddspipe_yaml/core/Yaml.hpp similarity index 51% rename from ddspipe_yaml/include/ddspipe_yaml/YamlWriter.hpp rename to ddspipe_yaml/include/ddspipe_yaml/core/Yaml.hpp index b88d37b0f..5b93ca25a 100644 --- a/ddspipe_yaml/include/ddspipe_yaml/YamlWriter.hpp +++ b/ddspipe_yaml/include/ddspipe_yaml/core/Yaml.hpp @@ -1,4 +1,4 @@ -// Copyright 2023 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -14,13 +14,68 @@ #pragma once +#include + +#include + #include -#include namespace eprosima { namespace ddspipe { namespace yaml { +/** + * Configuration is in dictionary format + * + * YAML spec: https://yaml.org/spec/1.2.2/ + * + * @note It is not legal to repeat keys in a YAML + * + * @note this object contains a reference to the actual yaml object. Thus passing it by value or by reference + * makes no different. + */ +using Yaml = YAML::Node; + +//! Type of tag in the yaml +using TagType = std::string; + +/** + * @brief Whether key \c tag is present in \c yml + * + * The key \c tag must be in base level of \c yml , it will no be looked for recursively. + * + * It could only look for keys in yaml map and empty yaml. It will fail in array and scalar cases. + * In this second case, return will always be false. + * + * @param yml base yaml + * @param tag key to look for in \c yml + * @return true id \c tag is a key in \c yml + * @return false otherwise + * + * @throw \c ConfigurationException if \c yml is not a map or empty yaml + */ +DDSPIPE_YAML_DllAPI +bool is_tag_present( + const Yaml& yml, + const TagType& tag); + +/** + * @brief Get the yaml inside key \c tag in \c yml + * + * \c tag must be present in \c yml . It uses \c is_tag_present to check the key is present. + * Use \c is_tag_present before calling this method in order to avoid the exception. + * + * @param yml base yaml + * @param tag key refering the value returned + * @return yaml value inside key \c tag + * + * @throw \c ConfigurationException if \c tag is not present in \c yml + */ +DDSPIPE_YAML_DllAPI +Yaml get_value_in_tag( + const Yaml& yml, + const TagType& tag); + /** * @brief Add a new yaml under tag \c tag . * @@ -46,35 +101,6 @@ Yaml add_tag( bool initialize = false, bool overwrite = true); -/** - * @brief Set a new value in \c yml . - * - * This method is thought to be specialized for each different type coding the serialization method to a yaml. - * - * @param yml base yaml where to write the value - * @param value value to write - * - * @tparam T type of the value to set in the yaml. - * - * @note some specializations are already implemented: - * - native types (int, string, boolean) - * - collections (vector, set, map) - */ -template -void set( - Yaml& yml, - const T& value); - -//! Set the \c value in a new yaml in \c yml under \c tag . -template -void set( - Yaml& yml, - const TagType& tag, - const T& value); - } /* namespace yaml */ } /* namespace ddspipe */ } /* namespace eprosima */ - -// Include implementation template file -#include diff --git a/ddspipe_yaml/include/ddspipe_yaml/YamlManager.hpp b/ddspipe_yaml/include/ddspipe_yaml/core/YamlFileManager.hpp similarity index 92% rename from ddspipe_yaml/include/ddspipe_yaml/YamlManager.hpp rename to ddspipe_yaml/include/ddspipe_yaml/core/YamlFileManager.hpp index a74f0a205..7e55d80f4 100644 --- a/ddspipe_yaml/include/ddspipe_yaml/YamlManager.hpp +++ b/ddspipe_yaml/include/ddspipe_yaml/core/YamlFileManager.hpp @@ -15,7 +15,7 @@ #pragma once #include -#include +#include namespace eprosima { namespace ddspipe { @@ -24,7 +24,7 @@ namespace yaml { /** * Class that manages generic methods related with yaml load and yaml validation. */ -class DDSPIPE_YAML_DllAPI YamlManager +class DDSPIPE_YAML_DllAPI YamlFileManager { public: diff --git a/ddspipe_yaml/include/ddspipe_yaml/field/YamlObjectField.hpp b/ddspipe_yaml/include/ddspipe_yaml/field/YamlObjectField.hpp new file mode 100644 index 000000000..4f6b1facf --- /dev/null +++ b/ddspipe_yaml/include/ddspipe_yaml/field/YamlObjectField.hpp @@ -0,0 +1,70 @@ +// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// !! WARNING !! +// NO RESPONSIBILITY SHALL BE TAKEN FOR ANY ISSUES CAUSED BY THE MISUSE OF THIS FILE +// This file is meant for developers only, and must not be used from any non expert. +// Only classes and methods in this file should be used, and not the ones in .ipp, +// and only inside read and write YAML methods + +#pragma once + +#include + +#include +#include + +#include +#include + +namespace eprosima { +namespace ddspipe { +namespace yaml { + +ENUMERATION_BUILDER +( + OptionalKind, + required, + optional, + advisable +); + +// Forward declaration +class IYamlObjectField; + +template +utils::Heritable create_object_field( + const std::string& tag, + const OptionalKind& kind, + const T& vessel); + +template +std::vector> object_fields(const T& object); + +template +void read_from_fields( + const Yaml& yml, + T& object); + +template +void write_from_fields( + Yaml& yml, + const T& object); + +} /* namespace yaml */ +} /* namespace ddspipe */ +} /* namespace eprosima */ + +// Include implementation template file +#include diff --git a/ddspipe_yaml/include/ddspipe_yaml/field/impl/YamlObjectField.ipp b/ddspipe_yaml/include/ddspipe_yaml/field/impl/YamlObjectField.ipp new file mode 100644 index 000000000..925435318 --- /dev/null +++ b/ddspipe_yaml/include/ddspipe_yaml/field/impl/YamlObjectField.ipp @@ -0,0 +1,174 @@ +// Copyright 2023 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include + +#include +#include + +namespace eprosima { +namespace ddspipe { +namespace yaml { + +//////////////////////////////////////////////// +// DEFINITIONS +//////////////////////////////////////////////// +// These definitions are hidden from user and thus they are in .ipp + +class IYamlObjectField +{ +public: + virtual ~IYamlObjectField() = default; + virtual void read_field(const Yaml& yml) = 0; + virtual void write_field(Yaml& yml) const = 0; + virtual std::string tag() const noexcept = 0; +}; + +template +class YamlObjectField : public IYamlObjectField +{ +public: + + YamlObjectField( + const std::string& tag, + const OptionalKind& kind, + T& vessel); + + YamlObjectField( + const std::string& tag, + const OptionalKind& kind, + const T& vessel); + + virtual void read_field( + const Yaml& yml) override; + + virtual void write_field( + Yaml& yml) const override; + + virtual std::string tag() const noexcept override; + +protected: + + std::string tag_; + OptionalKind kind_; + T& vessel_; +}; + +DDSPIPE_YAML_DllAPI +void read_fields( + const Yaml& yml, + const std::vector>& fields, + bool fail_with_extra_tags = false, + bool show_warning_with_extra_tags = true); + +DDSPIPE_YAML_DllAPI +void write_fields( + Yaml& yml, + const std::vector>& fields); + +//////////////////////////////////////////////// +// IMPLEMENTATIONS +//////////////////////////////////////////////// + +template +YamlObjectField::YamlObjectField( + const std::string& tag, + const OptionalKind& kind, + const T& vessel) + : tag_(tag) + , kind_(kind) + , vessel_(const_cast(vessel)) +{ + // Do nothing + + // WARNING: This ctor does something that SHOULD NOT BE DONE + // It cast a const reference to non const. This is done because we assume this will be used wisely + // and non const variables will be tried to be written. + // However it suppose a risk that may change in the future if we find a way to solve the following problem: + // There is NO WAY to write function object_fields commonly for const and non const variables. + // So instead of forcing developer to write it twice for each type, it is just written once and here cast is made. +} + +template +void YamlObjectField::read_field( + const Yaml& yml) +{ + bool present = is_tag_present(yml, this->tag_); + if (present) + { + read(yml, this->tag_, vessel_); + } + else if (this->kind_ == OptionalKind::required) + { + throw eprosima::utils::ConfigurationException(STR_ENTRY + << "Required tag <" << this->tag_ + << "> not present."); + } + else if (this->kind_ == OptionalKind::advisable) + { + logWarning(DDSPIPE_YAML, "Tag <" << this->tag_ << "> is advisable but not present in YAML."); + } +} + +template +void YamlObjectField::write_field( + Yaml& yml) const +{ + write(yml, tag_, vessel_); +} + +template +std::string YamlObjectField::tag() const noexcept +{ + return tag_; +} + +template +utils::Heritable create_object_field( + const std::string& tag, + const OptionalKind& kind, + const T& vessel) +{ + return utils::Heritable>::make_heritable( + YamlObjectField(tag, kind, vessel)); +} + +template +void read_from_fields( + const Yaml& yml, + T& object) +{ + read_fields( + yml, + object_fields(object) + ); +} + +template +void write_from_fields( + Yaml& yml, + const T& object) +{ + write_fields( + yml, + object_fields(object) + ); +} + +} /* namespace yaml */ +} /* namespace ddspipe */ +} /* namespace eprosima */ diff --git a/ddspipe_yaml/include/ddspipe_yaml/impl/YamlReader.ipp b/ddspipe_yaml/include/ddspipe_yaml/impl/YamlReader.ipp deleted file mode 100644 index 7b79d1e29..000000000 --- a/ddspipe_yaml/include/ddspipe_yaml/impl/YamlReader.ipp +++ /dev/null @@ -1,241 +0,0 @@ -// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include -#include -#include -#include - -namespace eprosima { -namespace ddspipe { -namespace yaml { - -template -T YamlReader::get( - const Yaml& yml, - const TagType& tag, - const YamlReaderVersion version) -{ - // ATTENTION: This try catch can be avoided, it is only used to add verbose information - try - { - return get(get_value_in_tag(yml, tag), version); - } - catch (const std::exception& e) - { - throw eprosima::utils::ConfigurationException( - utils::Formatter() << - "Error getting required value of type <" << TYPE_NAME(T) << - "> in tag <" << tag << "> :\n " << e.what()); - } -} - -template -void YamlReader::fill( - T& object, - const Yaml& yml, - const TagType& tag, - const YamlReaderVersion version) -{ - // ATTENTION: This try catch can be avoided, it is only used to add verbose information - try - { - return fill(get_value_in_tag(yml, tag), object, version); - } - catch (const std::exception& e) - { - throw eprosima::utils::ConfigurationException( - utils::Formatter() << - "Error filling object of type <" << TYPE_NAME(T) << - "> in tag <" << tag << "> :\n " << e.what()); - } -} - -template -T YamlReader::get_scalar( - const Yaml& yml, - const TagType& tag) -{ - try - { - return get_scalar(get_value_in_tag(yml, tag)); - } - catch (const std::exception& e) - { - throw eprosima::utils::ConfigurationException( - utils::Formatter() << "Error reading yaml scalar under tag <" << tag << "> :\n " << e.what()); - } -} - -template -T YamlReader::get_scalar( - const Yaml& yml) -{ - if (!yml.IsScalar()) - { - throw eprosima::utils::ConfigurationException( - utils::Formatter() << - "Trying to read a primitive value of type <" << TYPE_NAME(T) << "> from a non scalar yaml."); - } - - try - { - return yml.as(); - } - catch (const std::exception& e) - { - throw eprosima::utils::ConfigurationException( - utils::Formatter() << - "Incorrect format for primitive value, expected <" << TYPE_NAME(T) << ">:\n " << e.what()); - } -} - -template -std::list YamlReader::get_list( - const Yaml& yml, - const TagType& tag, - const YamlReaderVersion version) -{ - try - { - return get_list(get_value_in_tag(yml, tag), version); - } - catch (const std::exception& e) - { - throw eprosima::utils::ConfigurationException( - utils::Formatter() << "Error reading yaml list under tag <" << tag << "> :\n " << e.what()); - } -} - -template -std::list YamlReader::get_list( - const Yaml& yml, - const YamlReaderVersion version) -{ - if (!yml.IsSequence()) - { - throw eprosima::utils::ConfigurationException( - utils::Formatter() << "Incorrect format, yaml Sequence expected."); - } - - std::list result; - - try - { - for (Yaml element : yml) - { - result.push_back(get(element, version)); - } - } - catch (const std::exception& e) - { - throw eprosima::utils::ConfigurationException( - utils::Formatter() << "Error reading yaml sequence of type <" << TYPE_NAME(T) << "> :\n " << - e.what()); - } - - return result; -} - -template -std::set YamlReader::get_set( - const Yaml& yml, - const TagType& tag, - const YamlReaderVersion version) -{ - std::list elements_list = get_list(yml, tag, version); - return std::set(elements_list.begin(), elements_list.end()); -} - -template -T YamlReader::get_enumeration( - const Yaml& yml, - const TagType& tag, - const std::map& enum_values) -{ - try - { - return get_enumeration(get_value_in_tag(yml, tag), enum_values); - } - catch (const std::exception& e) - { - throw eprosima::utils::ConfigurationException( - utils::Formatter() << "Error reading enumeration value under tag <" << tag << "> :\n " << e.what()); - } -} - -template -T YamlReader::get_enumeration( - const Yaml& yml, - const std::map& enum_values) -{ - TagType value = get_scalar(yml); - - // Find value - auto it = enum_values.find(value); - - if (it == enum_values.end()) - { - throw eprosima::utils::ConfigurationException( - utils::Formatter() << "Value <" << value << "> is not valid in enumeration <" << TYPE_NAME(T) << "."); - } - else - { - return it->second; - } -} - -template -T YamlReader::get_enumeration_from_builder( - const Yaml& yml, - const utils::EnumBuilder& enum_builder) -{ - std::string str_value = get_scalar(yml); - T enum_value; - bool found = enum_builder.string_to_enumeration(str_value, enum_value); - - if (found) - { - return enum_value; - } - else - { - throw eprosima::utils::ConfigurationException(utils::Formatter() - << "Value <" << str_value << "> cannot be parsed as enum <" << TYPE_NAME(T) << ">."); - } -} - -template -T YamlReader::get_enumeration_from_builder( - const Yaml& yml, - const TagType& tag, - const utils::EnumBuilder& enum_builder) -{ - try - { - return get_enumeration_from_builder(get_value_in_tag(yml, tag), enum_builder); - } - catch (const std::exception& e) - { - throw eprosima::utils::ConfigurationException(utils::Formatter() - << "Error reading enumeration from builder under tag <" - << tag << "> :\n " << e.what()); - } -} - -} /* namespace yaml */ -} /* namespace ddspipe */ -} /* namespace eprosima */ diff --git a/ddspipe_yaml/include/ddspipe_yaml/reader/YamlReader.hpp b/ddspipe_yaml/include/ddspipe_yaml/reader/YamlReader.hpp new file mode 100644 index 000000000..57df0de6e --- /dev/null +++ b/ddspipe_yaml/include/ddspipe_yaml/reader/YamlReader.hpp @@ -0,0 +1,71 @@ +// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include +#include + +#include +#include + +namespace eprosima { +namespace ddspipe { +namespace yaml { + +/** + * @brief Fill an element given by parameter with the values inside \c yml + * + * This method simplifies the process of retrieving an object whose parent has its own \c read method, + * as the code is reused from one another calling parent \c read in child. + * It is also very helpful to handle default creation values. Without this, every different default value + * must have its own if-else clause, forcing to create the respective constructor. With this method, + * the default values are initialized with the default constructor, and then are overwritten by the yaml. + * [this problem arises because C++ does not allow different order of parameters in method call] + */ +template +void read( + const Yaml& yml, + T& object); + +//! Fill an element given by parameter with the values inside \c tag in \c yml +template +void read( + const Yaml& yml, + const TagType& tag, + T& object); + +template +T read( + const Yaml& yml); + +template +T read( + const Yaml& yml, + const TagType& tag); + +//! Fill \c object with a Yaml structure +template +void operator <<( + T& object, + const Yaml& yml); + +} /* namespace yaml */ +} /* namespace ddspipe */ +} /* namespace eprosima */ + +// Include implementation template file +#include +#include diff --git a/ddspipe_yaml/include/ddspipe_yaml/reader/impl/YamlReader_declarations.ipp b/ddspipe_yaml/include/ddspipe_yaml/reader/impl/YamlReader_declarations.ipp new file mode 100644 index 000000000..a2d28eb2c --- /dev/null +++ b/ddspipe_yaml/include/ddspipe_yaml/reader/impl/YamlReader_declarations.ipp @@ -0,0 +1,43 @@ +// Copyright 2023 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include + +namespace eprosima { +namespace ddspipe { +namespace yaml { + +//////////////////////////////////////////////// +// DEFINITION OF FUNCTIONS FOR TEMPLATED TYPES +//////////////////////////////////////////////// + +template +void read_fuzzy( + const Yaml& yml, + utils::Fuzzy& fuzzy); + +//////////////////////////////////////////////// +// SPECIALIZATION DECLARATIONS FOR TEMPLATED TYPES +//////////////////////////////////////////////// + +template +void read( + const Yaml& yml, + utils::Fuzzy& fuzzy); + +} /* namespace yaml */ +} /* namespace ddspipe */ +} /* namespace eprosima */ diff --git a/ddspipe_yaml/include/ddspipe_yaml/reader/impl/YamlReader_implementations.ipp b/ddspipe_yaml/include/ddspipe_yaml/reader/impl/YamlReader_implementations.ipp new file mode 100644 index 000000000..3318bd408 --- /dev/null +++ b/ddspipe_yaml/include/ddspipe_yaml/reader/impl/YamlReader_implementations.ipp @@ -0,0 +1,100 @@ +// Copyright 2023 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +namespace eprosima { +namespace ddspipe { +namespace yaml { + +//////////////////////////////////////////////// +// DEFINITION OF FUNCTIONS FOR TEMPLATED TYPES +//////////////////////////////////////////////// + +template +void read_fuzzy( + const Yaml& yml, + utils::Fuzzy& fuzzy) +{ + read(yml, fuzzy.get_reference()); + fuzzy.set_level(); +} + +//////////////////////////////////////////////// +// SPECIALIZATION DECLARATIONS FOR TEMPLATED TYPES +//////////////////////////////////////////////// + +template +void read( + const Yaml& yml, + utils::Fuzzy& fuzzy) +{ + read_fuzzy(yml, fuzzy); +} + +//////////////////////////////////////////////// +// SPECIALIZATIONS OF SPECIFIC FUNCTIONS +//////////////////////////////////////////////// + +template +void read( + const Yaml& yml, + const TagType& tag, + T& object) +{ + // ATTENTION: This try catch can be avoided, it is only used to add verbose information + try + { + return read(get_value_in_tag(yml, tag), object); + } + catch (const std::exception& e) + { + throw eprosima::utils::ConfigurationException( + utils::Formatter() << + "Error reading object of type <" << TYPE_NAME(T) << + "> in tag <" << tag << "> :\n " << e.what()); + } +} + + +template +T read( + const Yaml& yml) +{ + T object; + read(yml, object); + return object; +} + +template +T read( + const Yaml& yml, + const TagType& tag) +{ + T object; + read(yml, tag, object); + return object; +} + +template +void operator <<( + T& object, + const Yaml& yml) +{ + read(yml, object); +} + +} /* namespace yaml */ +} /* namespace ddspipe */ +} /* namespace eprosima */ diff --git a/ddspipe_yaml/include/ddspipe_yaml/yaml_configuration_tags.hpp b/ddspipe_yaml/include/ddspipe_yaml/tags/yaml_configuration_tags.hpp similarity index 100% rename from ddspipe_yaml/include/ddspipe_yaml/yaml_configuration_tags.hpp rename to ddspipe_yaml/include/ddspipe_yaml/tags/yaml_configuration_tags.hpp diff --git a/ddspipe_yaml/include/ddspipe_yaml/testing/generate_yaml.hpp b/ddspipe_yaml/include/ddspipe_yaml/testing/generate_yaml.hpp deleted file mode 100644 index fb68838d8..000000000 --- a/ddspipe_yaml/include/ddspipe_yaml/testing/generate_yaml.hpp +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -namespace eprosima { -namespace ddspipe { -namespace yaml { -namespace testing { - -template -struct YamlField -{ - YamlField() - : present(false) - { - } - - YamlField( - const T& arg_value, - bool arg_present = true) - : value(arg_value) - , present(arg_present) - { - } - - T value; - bool present; -}; - -template -void add_field_to_yaml( - Yaml& yml, - const YamlField& field, - const std::string& tag) -{ - if (field.present) - { - yml[tag] = field.value; - } -} - -void guid_prefix_to_yaml( - Yaml& yml, - const core::types::GuidPrefix& guid_prefix) -{ - std::stringstream ss; - ss << guid_prefix; - - add_field_to_yaml( - yml, - YamlField(ss.str()), - DISCOVERY_SERVER_GUID_TAG); -} - -void discovery_server_guid_prefix_to_yaml( - Yaml& yml, - const core::types::GuidPrefix& guid_prefix) -{ - Yaml yml_guid; - guid_prefix_to_yaml(yml_guid, guid_prefix); - - yml[DISCOVERY_SERVER_GUID_PREFIX_TAG] = yml_guid; -} - -void address_to_yaml( - Yaml& yml, - const participants::types::Address& address) -{ - add_field_to_yaml( - yml, - YamlField(address.ip()), - ADDRESS_IP_TAG); - - add_field_to_yaml( - yml, - YamlField(address.port()), - ADDRESS_PORT_TAG); - - if (address.transport_protocol() == participants::types::TransportProtocol::udp) - { - add_field_to_yaml( - yml, - YamlField(ADDRESS_TRANSPORT_UDP_TAG), - ADDRESS_TRANSPORT_TAG); - } - else if (address.transport_protocol() == participants::types::TransportProtocol::tcp) - { - add_field_to_yaml( - yml, - YamlField(ADDRESS_TRANSPORT_TCP_TAG), - ADDRESS_TRANSPORT_TAG); - } - - if (address.ip_version() == participants::types::IpVersion::v4) - { - add_field_to_yaml( - yml, - YamlField(ADDRESS_IP_VERSION_V4_TAG), - ADDRESS_IP_VERSION_TAG); - } - else if (address.ip_version() == participants::types::IpVersion::v6) - { - add_field_to_yaml( - yml, - YamlField(ADDRESS_IP_VERSION_V6_TAG), - ADDRESS_IP_VERSION_TAG); - } -} - -void participantid_to_yaml( - Yaml& yml, - const core::types::ParticipantId& id) -{ - add_field_to_yaml( - yml, - YamlField(id), - PARTICIPANT_NAME_TAG); -} - -void domain_to_yaml( - Yaml& yml, - const core::types::DomainId& domain) -{ - add_field_to_yaml( - yml, - YamlField(domain.domain_id), - DOMAIN_ID_TAG); -} - -void repeater_to_yaml( - Yaml& yml, - const bool& repeater) -{ - add_field_to_yaml( - yml, - YamlField(repeater), - IS_REPEATER_TAG); -} - -// Create a yaml QoS object only with reliability -void qos_to_yaml( - Yaml& yml, - const core::types::TopicQoS& qos) -{ - // TODO: extend this for all qos - add_field_to_yaml(yml, YamlField(qos.is_reliable()), QOS_RELIABLE_TAG); -} - -// Create a yaml Topic object with name, type and key tags -void filter_topic_to_yaml( - Yaml& yml, - const core::types::WildcardDdsFilterTopic& topic) -{ - if (topic.topic_name.is_set()) - { - add_field_to_yaml(yml, YamlField(topic.topic_name.get_reference()), TOPIC_NAME_TAG); - } - - if (topic.type_name.is_set()) - { - add_field_to_yaml(yml, YamlField(topic.type_name.get_reference()), TOPIC_TYPE_NAME_TAG); - } -} - -// Create a yaml DdsTopic object with name, type, key and reliable tags -void real_topic_to_yaml( - Yaml& yml, - const core::types::DdsTopic& topic) -{ - add_field_to_yaml(yml, YamlField(topic.m_topic_name), TOPIC_NAME_TAG); - add_field_to_yaml(yml, YamlField(topic.type_name), TOPIC_TYPE_NAME_TAG); - - // QoS - Yaml yml_qos; - qos_to_yaml(yml_qos, topic.topic_qos); - add_field_to_yaml(yml, YamlField(yml_qos), TOPIC_QOS_TAG); -} - -} /* namespace testing */ -} /* namespace yaml */ -} /* namespace ddspipe */ -} /* namespace eprosima */ diff --git a/ddspipe_yaml/include/ddspipe_yaml/writer/YamlWriter.hpp b/ddspipe_yaml/include/ddspipe_yaml/writer/YamlWriter.hpp new file mode 100644 index 000000000..cfe460ef2 --- /dev/null +++ b/ddspipe_yaml/include/ddspipe_yaml/writer/YamlWriter.hpp @@ -0,0 +1,63 @@ +// Copyright 2023 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include + +namespace eprosima { +namespace ddspipe { +namespace yaml { + +/** + * @brief Set a new object in \c yml . + * + * This method is thought to be specialized for each different type coding the serialization method to a yaml. + * + * @param yml base yaml where to write the object + * @param object object to write + * + * @tparam T type of the object to set in the yaml. + * + * @note some specializations are already implemented: + * - native types (int, string, boolean) + * - collections (vector, set, map) + */ +template +void write( + Yaml& yml, + const T& object); + +//! Set the \c object in a new yaml in \c yml under \c tag . +template +void write( + Yaml& yml, + const TagType& tag, + const T& object); + + +//! Fill Yaml structure with \c object +template +void operator <<( + Yaml& yml, + const T& object); + +} /* namespace yaml */ +} /* namespace ddspipe */ +} /* namespace eprosima */ + +// Include implementation template file +#include +#include diff --git a/ddspipe_yaml/include/ddspipe_yaml/writer/impl/YamlWriter_declarations.ipp b/ddspipe_yaml/include/ddspipe_yaml/writer/impl/YamlWriter_declarations.ipp new file mode 100644 index 000000000..20c4211b2 --- /dev/null +++ b/ddspipe_yaml/include/ddspipe_yaml/writer/impl/YamlWriter_declarations.ipp @@ -0,0 +1,96 @@ +// Copyright 2023 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include +#include +#include + +#include + +namespace eprosima { +namespace ddspipe { +namespace yaml { + +//////////////////////////////////////////////// +// DEFINITION OF FUNCTIONS FOR TEMPLATED TYPES +//////////////////////////////////////////////// + +template +void write_fuzzy( + Yaml& yml, + const utils::Fuzzy& fuzzy); + +template +void write_collection( + Yaml& yml, + const std::vector& collection); + +template +void write_map( + Yaml& yml, + const std::map& collection); + +//////////////////////////////////////////////// +// DEFINITION OF FUNCTIONS FOR TRIVIAL TYPES +//////////////////////////////////////////////// +// These are needed for Dll export + +template <> +DDSPIPE_YAML_DllAPI +void write( + Yaml& yml, + const std::string& value); + +template <> +DDSPIPE_YAML_DllAPI +void write( + Yaml& yml, + const bool& value); + +template <> +DDSPIPE_YAML_DllAPI +void write( + Yaml& yml, + const int& value); + +//////////////////////////////////////////////// +// SPECIALIZATION DECLARATIONS FOR TEMPLATED TYPES +//////////////////////////////////////////////// + +template +void write( + Yaml& yml, + const utils::Fuzzy& collection); + +template +void write( + Yaml& yml, + const std::vector& collection); + +template +void write( + Yaml& yml, + const std::set& collection); + +template +void write( + Yaml& yml, + const std::map& collection); + +} /* namespace yaml */ +} /* namespace ddspipe */ +} /* namespace eprosima */ diff --git a/ddspipe_yaml/include/ddspipe_yaml/impl/YamlWriter.ipp b/ddspipe_yaml/include/ddspipe_yaml/writer/impl/YamlWriter_implementations.ipp similarity index 60% rename from ddspipe_yaml/include/ddspipe_yaml/impl/YamlWriter.ipp rename to ddspipe_yaml/include/ddspipe_yaml/writer/impl/YamlWriter_implementations.ipp index 0845b7c09..67d74e338 100644 --- a/ddspipe_yaml/include/ddspipe_yaml/impl/YamlWriter.ipp +++ b/ddspipe_yaml/include/ddspipe_yaml/writer/impl/YamlWriter_implementations.ipp @@ -14,131 +14,104 @@ #pragma once -#include -#include -#include -#include - #include -#include -#include - namespace eprosima { namespace ddspipe { namespace yaml { -template -void set( - Yaml& yml, - const std::vector& collection); - -template -void set( - Yaml& yml, - const std::set& collection); - -template -void set( - Yaml& yml, - const std::map& collection); - -template -void set_collection( - Yaml& yml, - const std::vector& collection); - -template -void set_map( - Yaml& yml, - const std::map& collection); - //////////////////////////////////////////////// -// SPECIALIZATIONS FOR TRIVIAL TYPES +// SPECIALIZATIONS FOR TEMPLATED TYPES //////////////////////////////////////////////// -template <> -DDSPIPE_YAML_DllAPI -void set( - Yaml& yml, - const std::string& value); - -template <> -DDSPIPE_YAML_DllAPI -void set( - Yaml& yml, - const bool& value); - -template <> -DDSPIPE_YAML_DllAPI -void set( +template +void write( Yaml& yml, - const int& value); - -//////////////////////////////////////////////// -// SPECIALIZATIONS FOR OTHER TYPES -//////////////////////////////////////////////// + const utils::Fuzzy& fuzzy) +{ + write_fuzzy(yml, fuzzy); +} template -void set( +void write( Yaml& yml, const std::vector& collection) { - set_collection(yml, collection); + write_collection(yml, collection); } template -void set( +void write( Yaml& yml, const std::set& collection) { - set_collection(yml, std::vector(collection.begin(), collection.end())); + write_collection(yml, std::vector(collection.begin(), collection.end())); } template -void set( +void write( Yaml& yml, const std::map& collection) { - set_map(yml, collection); + write_map(yml, collection); } -//////////////////////////////////// -// SPECIALIZATIONS NEEDED -//////////////////////////////////// +//////////////////////////////////////////////// +// SPECIALIZATIONS OF SPECIFIC FUNCTIONS +//////////////////////////////////////////////// template -void set( +void write( Yaml& yml, const TagType& tag, const T& value) { auto yml_under_tag = add_tag(yml, tag); - set(yml_under_tag, value); + write(yml_under_tag, value); +} + +template +void operator <<( + Yaml& yml, + const T& object) +{ + write(yml, object); +} + +//////////////////////////////////////////////// +// SPECIALIZATIONS OF SPECIFIC TYPES FUNCTIONS +//////////////////////////////////////////////// + +template +void write_fuzzy( + Yaml& yml, + const utils::Fuzzy& fuzzy) +{ + write(yml, fuzzy.get_reference()); } template -void set_collection( +void write_collection( Yaml& yml, const std::vector& collection) { for (const auto& v : collection) { Yaml yml_value; - set(yml_value, v); + write(yml_value, v); yml.push_back(yml_value); } } template -void set_map( +void write_map( Yaml& yml, const std::map& collection) { for (const auto& v : collection) { Yaml yml_in_new_tag = yml[utils::generic_to_string(v.first)]; - set(yml_in_new_tag, v.second); + write(yml_in_new_tag, v.second); } } diff --git a/ddspipe_yaml/src/cpp/NewYamlReader_generic.cpp b/ddspipe_yaml/src/cpp/NewYamlReader_generic._cpp similarity index 98% rename from ddspipe_yaml/src/cpp/NewYamlReader_generic.cpp rename to ddspipe_yaml/src/cpp/NewYamlReader_generic._cpp index 5b9f815e2..55d90d866 100644 --- a/ddspipe_yaml/src/cpp/NewYamlReader_generic.cpp +++ b/ddspipe_yaml/src/cpp/NewYamlReader_generic._cpp @@ -36,7 +36,7 @@ #include #include -#include +#include #include #include diff --git a/ddspipe_yaml/src/cpp/YamlReader_generic.cpp b/ddspipe_yaml/src/cpp/YamlReader_generic._cpp similarity index 99% rename from ddspipe_yaml/src/cpp/YamlReader_generic.cpp rename to ddspipe_yaml/src/cpp/YamlReader_generic._cpp index 934fc747c..ec68944ce 100644 --- a/ddspipe_yaml/src/cpp/YamlReader_generic.cpp +++ b/ddspipe_yaml/src/cpp/YamlReader_generic._cpp @@ -36,7 +36,7 @@ #include #include -#include +#include #include #include diff --git a/ddspipe_yaml/src/cpp/YamlReader_participants.cpp b/ddspipe_yaml/src/cpp/YamlReader_participants._cpp similarity index 99% rename from ddspipe_yaml/src/cpp/YamlReader_participants.cpp rename to ddspipe_yaml/src/cpp/YamlReader_participants._cpp index c9188e829..7abf4e5e5 100644 --- a/ddspipe_yaml/src/cpp/YamlReader_participants.cpp +++ b/ddspipe_yaml/src/cpp/YamlReader_participants._cpp @@ -34,7 +34,7 @@ #include #include -#include +#include #include #include diff --git a/ddspipe_yaml/src/cpp/YamlReader_types.cpp b/ddspipe_yaml/src/cpp/YamlReader_types._cpp similarity index 99% rename from ddspipe_yaml/src/cpp/YamlReader_types.cpp rename to ddspipe_yaml/src/cpp/YamlReader_types._cpp index 5285ce641..bbfa4ee9d 100644 --- a/ddspipe_yaml/src/cpp/YamlReader_types.cpp +++ b/ddspipe_yaml/src/cpp/YamlReader_types._cpp @@ -37,7 +37,7 @@ #include #include -#include +#include #include #include diff --git a/ddspipe_yaml/src/cpp/YamlWriter.cpp b/ddspipe_yaml/src/cpp/core/Yaml.cpp similarity index 60% rename from ddspipe_yaml/src/cpp/YamlWriter.cpp rename to ddspipe_yaml/src/cpp/core/Yaml.cpp index bd7d7b643..2696067fd 100644 --- a/ddspipe_yaml/src/cpp/YamlWriter.cpp +++ b/ddspipe_yaml/src/cpp/core/Yaml.cpp @@ -1,4 +1,4 @@ -// Copyright 2023 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,16 +12,47 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include + +#include #include -#include -#include -#include +#include namespace eprosima { namespace ddspipe { namespace yaml { +bool is_tag_present( + const Yaml& yml, + const TagType& tag) +{ + if (!yml.IsMap() && !yml.IsNull()) + { + throw eprosima::utils::PreconditionNotMet( + utils::Formatter() << "Trying to find a tag: <" << tag << "> in a not yaml object map."); + } + + // Explicit conversion to avoid windows format warning + // This method performace is the same as only retrieving bool + return (yml[tag]) ? true : false; +} + +Yaml get_value_in_tag( + const Yaml& yml, + const TagType& tag) +{ + if (is_tag_present(yml, tag)) + { + return yml[tag]; + } + else + { + throw eprosima::utils::PreconditionNotMet( + utils::Formatter() << "Required tag not found: <" << tag << ">."); + } +} + Yaml add_tag( Yaml& yml, const TagType& tag, @@ -29,7 +60,7 @@ Yaml add_tag( bool overwrite /* = true */) { // If node must be initialized and could not overwrite, and node already exist, throw failure. - if (initialize && !overwrite && YamlReader::is_tag_present(yml, tag)) + if (initialize && !overwrite && is_tag_present(yml, tag)) { throw utils::PreconditionNotMet( STR_ENTRY @@ -50,30 +81,6 @@ Yaml add_tag( return yml[tag]; } -template <> -void set( - Yaml& yml, - const std::string& value) -{ - yml = value; -} - -template <> -void set( - Yaml& yml, - const bool& value) -{ - yml = value; -} - -template <> -void set( - Yaml& yml, - const int& value) -{ - yml = value; -} - } /* namespace yaml */ } /* namespace ddspipe */ } /* namespace eprosima */ diff --git a/ddspipe_yaml/src/cpp/YamlManager.cpp b/ddspipe_yaml/src/cpp/core/YamlManager.cpp similarity index 89% rename from ddspipe_yaml/src/cpp/YamlManager.cpp rename to ddspipe_yaml/src/cpp/core/YamlManager.cpp index 9c310c8b2..ac9ace8f6 100644 --- a/ddspipe_yaml/src/cpp/YamlManager.cpp +++ b/ddspipe_yaml/src/cpp/core/YamlManager.cpp @@ -12,21 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -/** - * @file YamlManager.cpp - * - */ - #include -#include -#include +#include +#include namespace eprosima { namespace ddspipe { namespace yaml { -Yaml YamlManager::load_file( +Yaml YamlFileManager::load_file( const std::string& file_path) { try diff --git a/ddspipe_yaml/src/cpp/field/YamlObjectField_generic.cpp b/ddspipe_yaml/src/cpp/field/YamlObjectField_generic.cpp new file mode 100644 index 000000000..409bf507a --- /dev/null +++ b/ddspipe_yaml/src/cpp/field/YamlObjectField_generic.cpp @@ -0,0 +1,94 @@ +// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include +#include + +namespace eprosima { +namespace ddspipe { +namespace yaml { + +void read_fields( + const Yaml& yml, + const std::vector>& fields, + bool fail_with_extra_tags /* = false */, + bool show_warning_with_extra_tags /* = true */) +{ + // Check that yml to find is actually a map or it is empty + if (!yml.IsMap() && !yml.IsNull()) + { + throw eprosima::utils::ConfigurationException(STR_ENTRY + << "Trying to get tags: in a not yaml object map."); + } + + // First, check that every required flag is present + for (const auto& field : fields) + { + field->read_field(yml); + } + + // If it should not show warnings, neither fail, do not check extra args + if (!fail_with_extra_tags && !show_warning_with_extra_tags) + { + return; + } + + // Now that every required field is present, check for extra tags + for (const auto& yaml_field : yml) + { + auto tag = yaml_field.first.as(); + bool should_be_present = false; + + // For each tag, check if it is inside allowed tags + for (const auto& field : fields) + { + if (field->tag() == tag) + { + should_be_present = true; + break; + } + } + + // If present tag is not inside allowed ones, fail + if (!should_be_present) + { + if (fail_with_extra_tags) + { + throw eprosima::utils::ConfigurationException(STR_ENTRY + << "Unexpected tag <" << tag << "> present."); + } + else if(show_warning_with_extra_tags) + { + logWarning(DDSPIPE_YAML, "Unexpected tag <" << tag << "> present."); + } + } + } +} + +void write_fields( + Yaml& yml, + const std::vector>& fields) +{ + for (const auto& field : fields) + { + field->write_field(yml); + } +} + +} /* namespace yaml */ +} /* namespace ddspipe */ +} /* namespace eprosima */ diff --git a/ddspipe_yaml/src/cpp/field/YamlObjectField_types.cpp b/ddspipe_yaml/src/cpp/field/YamlObjectField_types.cpp new file mode 100644 index 000000000..281989637 --- /dev/null +++ b/ddspipe_yaml/src/cpp/field/YamlObjectField_types.cpp @@ -0,0 +1,70 @@ +// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include +#include + +#include +#include +#include + +namespace eprosima { +namespace ddspipe { +namespace yaml { + +template <> +std::vector> object_fields( + const core::types::TopicQoS& object) +{ + // WARNING these fields are not complete + // reliability_qos , durability_qos and ownership_qos are parsed separately + return + { + create_object_field(QOS_HISTORY_DEPTH_TAG, OptionalKind::advisable, object.history_depth), + create_object_field(QOS_PARTITION_TAG, OptionalKind::optional, object.use_partitions), + create_object_field(QOS_DOWNSAMPLING_TAG, OptionalKind::optional, object.downsampling), + create_object_field(QOS_KEYED_TAG, OptionalKind::optional, object.keyed), + create_object_field(QOS_MAX_RECEPTION_RATE_TAG, OptionalKind::optional, object.max_reception_rate), + }; +} + +template <> +std::vector> object_fields( + const core::types::DdsTopic& object) +{ + return + { + create_object_field(TOPIC_NAME_TAG, OptionalKind::required, object.m_topic_name), + create_object_field(TOPIC_TYPE_NAME_TAG, OptionalKind::required, object.type_name), + create_object_field(TOPIC_QOS_TAG, OptionalKind::optional, object.topic_qos) + }; +} + +template <> +std::vector> object_fields( + const core::types::WildcardDdsFilterTopic& object) +{ + return + { + // create_object_field(TOPIC_NAME_TAG, OptionalKind::optional, object.topic_name), + // create_object_field(TOPIC_TYPE_NAME_TAG, OptionalKind::optional, object.type_name), + }; +} + +} /* namespace yaml */ +} /* namespace ddspipe */ +} /* namespace eprosima */ diff --git a/ddspipe_yaml/src/cpp/reader/YamlReader_generic.cpp b/ddspipe_yaml/src/cpp/reader/YamlReader_generic.cpp new file mode 100644 index 000000000..a5de8b319 --- /dev/null +++ b/ddspipe_yaml/src/cpp/reader/YamlReader_generic.cpp @@ -0,0 +1,74 @@ +// Copyright 2023 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +namespace eprosima { +namespace ddspipe { +namespace yaml { + +//////////////////////////////////////////////// +// IMPLEMENTATION OF TRIVIAL TYPES +//////////////////////////////////////////////// + +template <> +void read( + const Yaml& yml, + std::string& object) +{ + object = yml.as(); +} + +template <> +void read( + const Yaml& yml, + int& object) +{ + object = yml.as(); +} + +template <> +void read( + const Yaml& yml, + unsigned int& object) +{ + int x = yml.as(); + if (x < 0) + { + throw eprosima::utils::ConfigurationException( + utils::Formatter() << "Expected to read a positive integer."); + } + object = x; +} + +template <> +void read( + const Yaml& yml, + float& object) +{ + object = yml.as(); +} + +template <> +void read( + const Yaml& yml, + bool& object) +{ + object = yml.as(); +} + +} /* namespace yaml */ +} /* namespace ddspipe */ +} /* namespace eprosima */ diff --git a/ddspipe_yaml/src/cpp/reader/YamlReader_types.cpp b/ddspipe_yaml/src/cpp/reader/YamlReader_types.cpp new file mode 100644 index 000000000..a41b289f4 --- /dev/null +++ b/ddspipe_yaml/src/cpp/reader/YamlReader_types.cpp @@ -0,0 +1,97 @@ +// Copyright 2023 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include +#include +#include +#include + +namespace eprosima { +namespace ddspipe { +namespace yaml { + +//////////////////////////////////////////////// +// IMPLEMENTATION OF TYPES +//////////////////////////////////////////////// + +template <> +void read( + const Yaml& yml, + core::types::TopicQoS& object) +{ + read_from_fields(yml, object); + + // Reliability optional + if (is_tag_present(yml, QOS_RELIABLE_TAG)) + { + if (read(yml, QOS_RELIABLE_TAG)) + { + object.reliability_qos = eprosima::ddspipe::core::types::ReliabilityKind::RELIABLE; + } + else + { + object.reliability_qos = eprosima::ddspipe::core::types::ReliabilityKind::BEST_EFFORT; + } + } + + // Durability optional + if (is_tag_present(yml, QOS_TRANSIENT_TAG)) + { + if (read(yml, QOS_TRANSIENT_TAG)) + { + object.durability_qos = eprosima::ddspipe::core::types::DurabilityKind::TRANSIENT_LOCAL; + } + else + { + object.durability_qos = eprosima::ddspipe::core::types::DurabilityKind::VOLATILE; + } + } + + // Ownership optional + if (is_tag_present(yml, QOS_OWNERSHIP_TAG)) + { + if (read(yml, QOS_OWNERSHIP_TAG)) + { + object.ownership_qos = eprosima::ddspipe::core::types::OwnershipQosPolicyKind::EXCLUSIVE_OWNERSHIP_QOS; + } + else + { + object.ownership_qos = eprosima::ddspipe::core::types::OwnershipQosPolicyKind::SHARED_OWNERSHIP_QOS; + } + } +} + +template <> +void read( + const Yaml& yml, + core::types::DdsTopic& object) +{ + read_from_fields(yml, object); +} + +template <> +void read( + const Yaml& yml, + core::types::WildcardDdsFilterTopic& object) +{ + read_from_fields(yml, object); +} + +} /* namespace yaml */ +} /* namespace ddspipe */ +} /* namespace eprosima */ diff --git a/ddspipe_yaml/src/cpp/writer/YamlWriter_generic.cpp b/ddspipe_yaml/src/cpp/writer/YamlWriter_generic.cpp new file mode 100644 index 000000000..b2063c247 --- /dev/null +++ b/ddspipe_yaml/src/cpp/writer/YamlWriter_generic.cpp @@ -0,0 +1,77 @@ +// Copyright 2023 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include +#include +#include + +namespace eprosima { +namespace ddspipe { +namespace yaml { + +//////////////////////////////////////////////// +// IMPLEMENTATION OF TRIVIAL TYPES +//////////////////////////////////////////////// + +template <> +void write( + Yaml& yml, + const std::string& value) +{ + yml = value; +} + +template <> +void write( + Yaml& yml, + const int& value) +{ + yml = value; +} + +template <> +void write( + Yaml& yml, + const unsigned int& value) +{ + int x = value; + if (x < 0) + { + throw eprosima::utils::ConfigurationException( + utils::Formatter() << "Expected to write a positive integer."); + } + yml = value; +} + +template <> +void write( + Yaml& yml, + const bool& value) +{ + yml = value; +} + +template <> +void write( + Yaml& yml, + const float& value) +{ + yml = value; +} + +} /* namespace yaml */ +} /* namespace ddspipe */ +} /* namespace eprosima */ diff --git a/ddspipe_yaml/src/cpp/writer/YamlWriter_types.cpp b/ddspipe_yaml/src/cpp/writer/YamlWriter_types.cpp new file mode 100644 index 000000000..cea1cf000 --- /dev/null +++ b/ddspipe_yaml/src/cpp/writer/YamlWriter_types.cpp @@ -0,0 +1,61 @@ +// Copyright 2023 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include +#include +#include +#include + +namespace eprosima { +namespace ddspipe { +namespace yaml { + +//////////////////////////////////////////////// +// IMPLEMENTATION OF TYPES +//////////////////////////////////////////////// + +template <> +void write( + Yaml& yml, + const core::types::TopicQoS& object) +{ + write_from_fields(yml, object); + + write(yml, QOS_RELIABLE_TAG, object.is_reliable()); + write(yml, QOS_TRANSIENT_TAG, object.is_transient_local()); + write(yml, QOS_OWNERSHIP_TAG, object.has_ownership()); +} + +template <> +void write( + Yaml& yml, + const core::types::DdsTopic& object) +{ + write_from_fields(yml, object); +} + +template <> +void write( + Yaml& yml, + const core::types::WildcardDdsFilterTopic& object) +{ + write_from_fields(yml, object); +} + +} /* namespace yaml */ +} /* namespace ddspipe */ +} /* namespace eprosima */ diff --git a/ddspipe_yaml/test/unittest/CMakeLists.txt b/ddspipe_yaml/test/unittest/CMakeLists.txt index 5204db13f..4551fd7c5 100644 --- a/ddspipe_yaml/test/unittest/CMakeLists.txt +++ b/ddspipe_yaml/test/unittest/CMakeLists.txt @@ -17,5 +17,5 @@ ############## add_subdirectory(entities) -add_subdirectory(yaml_reader) -add_subdirectory(yaml_writer) +# add_subdirectory(yaml_reader) +# add_subdirectory(yaml_writer) diff --git a/ddspipe_yaml/test/unittest/entities/CMakeLists.txt b/ddspipe_yaml/test/unittest/entities/CMakeLists.txt index 0176f2b68..38af61f78 100644 --- a/ddspipe_yaml/test/unittest/entities/CMakeLists.txt +++ b/ddspipe_yaml/test/unittest/entities/CMakeLists.txt @@ -16,7 +16,7 @@ # Yaml GetEntities Test # ######################### -add_subdirectory(address) -add_subdirectory(guid) -add_subdirectory(topic) -add_subdirectory(new_topic) +# add_subdirectory(address) +# add_subdirectory(guid) +# add_subdirectory(topic) +add_subdirectory(generic) diff --git a/ddspipe_yaml/test/unittest/entities/address/CMakeLists.txt b/ddspipe_yaml/test/unittest/entities/address/CMakeLists.txt index 143b72bb1..a15e367ea 100644 --- a/ddspipe_yaml/test/unittest/entities/address/CMakeLists.txt +++ b/ddspipe_yaml/test/unittest/entities/address/CMakeLists.txt @@ -19,9 +19,9 @@ set(TEST_NAME YamlGetEntityAddressTest) set(TEST_SOURCES - ${PROJECT_SOURCE_DIR}/src/cpp/YamlReader_generic.cpp - ${PROJECT_SOURCE_DIR}/src/cpp/YamlReader_participants.cpp - ${PROJECT_SOURCE_DIR}/src/cpp/YamlReader_types.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/reader/YamlReader_generic.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/reader/YamlReader_participants.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/reader/YamlReader_types.cpp YamlGetEntityAddressTest.cpp ) @@ -58,9 +58,9 @@ add_unittest_executable( set(TEST_NAME YamlGetEntityDiscoveryServerAddressTest) set(TEST_SOURCES - ${PROJECT_SOURCE_DIR}/src/cpp/YamlReader_generic.cpp - ${PROJECT_SOURCE_DIR}/src/cpp/YamlReader_participants.cpp - ${PROJECT_SOURCE_DIR}/src/cpp/YamlReader_types.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/reader/YamlReader_generic.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/reader/YamlReader_participants.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/reader/YamlReader_types.cpp YamlGetEntityDiscoveryServerAddressTest.cpp ) diff --git a/ddspipe_yaml/test/unittest/entities/new_topic/CMakeLists.txt b/ddspipe_yaml/test/unittest/entities/generic/CMakeLists.txt similarity index 65% rename from ddspipe_yaml/test/unittest/entities/new_topic/CMakeLists.txt rename to ddspipe_yaml/test/unittest/entities/generic/CMakeLists.txt index 1fe0f96e6..aa7bd574d 100644 --- a/ddspipe_yaml/test/unittest/entities/new_topic/CMakeLists.txt +++ b/ddspipe_yaml/test/unittest/entities/generic/CMakeLists.txt @@ -16,28 +16,14 @@ # Yaml GetEntities Topic Test # ############################### -set(TEST_NAME NewYamlGetEntityTopicTest) +set(TEST_NAME ParametrizedGenericReaderWriterTest) set(TEST_SOURCES - NewYamlGetEntityTopicTest.cpp - ${PROJECT_SOURCE_DIR}/src/cpp/NewYamlReader_generic.cpp + GenericReaderWriterTest.cpp ) -set(TEST_LIST - get_real_topic - ) - -set(TEST_EXTRA_LIBRARIES - yaml-cpp - fastcdr - fastrtps - cpp_utils - ddspipe_core - ddspipe_participants - ) - -add_unittest_executable( +add_blackbox_executable( "${TEST_NAME}" "${TEST_SOURCES}" - "${TEST_LIST}" - "${TEST_EXTRA_LIBRARIES}") + "" + "") diff --git a/ddspipe_yaml/test/unittest/entities/generic/GenericReaderWriterTest.cpp b/ddspipe_yaml/test/unittest/entities/generic/GenericReaderWriterTest.cpp new file mode 100644 index 000000000..00b2c2895 --- /dev/null +++ b/ddspipe_yaml/test/unittest/entities/generic/GenericReaderWriterTest.cpp @@ -0,0 +1,97 @@ +// Copyright 2021 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include +#include +#include + +#include + +#include +#include + +using namespace eprosima; + +namespace test { + +constexpr const unsigned int N_TEST_CASES = 1000; + +template +T arbitrary( + unsigned int seed) +{ + return ddspipe::core::testing::arbitrary(seed); +} + +template <> +ddspipe::core::types::TopicQoS arbitrary( + unsigned int seed) +{ + return ddspipe::core::testing::arbitrary(seed, false); +} + +} // namespace test + +// Empty class to parametrized tests +template +struct GenericReaderWriterTest : public ::testing::Test +{}; +// Needed gtest macro +TYPED_TEST_SUITE_P(GenericReaderWriterTest); + +TYPED_TEST_P(GenericReaderWriterTest, generic_from_and_to) +{ + for (unsigned int i = 0 ; i < test::N_TEST_CASES ; ++i) + { + TypeParam write_object = test::arbitrary(i); + + ddspipe::yaml::Yaml yml; + ddspipe::yaml::write(yml, write_object); + + TypeParam read_object; + ddspipe::yaml::read(yml, read_object); + + ASSERT_EQ(write_object, read_object) << "Failure in value " << i; + } +} + +// Register test class and test cases +REGISTER_TYPED_TEST_SUITE_P( + GenericReaderWriterTest, + generic_from_and_to +); + +// Set types used in parametrization +typedef ::testing::Types< + ddspipe::core::types::DdsTopic, + ddspipe::core::types::TopicQoS + // ddspipe::core::types::WildcardDdsFilterTopic + > CaseTypes; + +// Generate each test case for each type case +INSTANTIATE_TYPED_TEST_SUITE_P( + ParametrizedGenericReaderWriterTest, + GenericReaderWriterTest, + CaseTypes); + +int main( + int argc, + char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/ddspipe_yaml/test/unittest/entities/guid/CMakeLists.txt b/ddspipe_yaml/test/unittest/entities/guid/CMakeLists.txt index e67669ec3..42c3d5d16 100644 --- a/ddspipe_yaml/test/unittest/entities/guid/CMakeLists.txt +++ b/ddspipe_yaml/test/unittest/entities/guid/CMakeLists.txt @@ -20,9 +20,9 @@ set(TEST_NAME YamlGetEntityGuidPrefixTest) set(TEST_SOURCES YamlGetEntityGuidPrefixTest.cpp - ${PROJECT_SOURCE_DIR}/src/cpp/YamlReader_generic.cpp - ${PROJECT_SOURCE_DIR}/src/cpp/YamlReader_participants.cpp - ${PROJECT_SOURCE_DIR}/src/cpp/YamlReader_types.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/reader/YamlReader_generic.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/reader/YamlReader_participants.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/reader/YamlReader_types.cpp ${PROJECT_SOURCE_DIR}/src/cpp/YamlManager.cpp ) diff --git a/ddspipe_yaml/test/unittest/entities/new_topic/NewYamlGetEntityTopicTest.cpp b/ddspipe_yaml/test/unittest/entities/new_topic/NewYamlGetEntityTopicTest.cpp deleted file mode 100644 index 20670ea23..000000000 --- a/ddspipe_yaml/test/unittest/entities/new_topic/NewYamlGetEntityTopicTest.cpp +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2021 Proyectos y Sistemas de Mantenimiento SL (eProsima). -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include - -#include - -#include -#include -#include - -#include - -using namespace eprosima; -using namespace eprosima::ddspipe::yaml; - -TEST(NewYamlGetEntityTopicTest, get_real_topic) -{ - Yaml yml_topic; - // yml_topic["name"] = "something"; - yml_topic["type"] = "s"; - yml_topic["qos"] = "something"; - // yml_topic["r"] = "something"; - - Yaml yml; - yml["topic"] = yml_topic; - - ddspipe::core::types::DdsTopic topic; - fill(topic, yml, "topic"); - - ddspipe::core::types::DdsTopic real_topic; - real_topic.m_topic_name = "something"; - real_topic.type_name = "s"; - - ASSERT_EQ(topic, real_topic); - -} - -int main( - int argc, - char** argv) -{ - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/ddspipe_yaml/test/unittest/entities/topic/CMakeLists.txt b/ddspipe_yaml/test/unittest/entities/topic/CMakeLists.txt index cf536cc3d..a74d08dd3 100644 --- a/ddspipe_yaml/test/unittest/entities/topic/CMakeLists.txt +++ b/ddspipe_yaml/test/unittest/entities/topic/CMakeLists.txt @@ -20,9 +20,9 @@ set(TEST_NAME YamlGetEntityTopicTest) set(TEST_SOURCES YamlGetEntityTopicTest.cpp - ${PROJECT_SOURCE_DIR}/src/cpp/YamlReader_generic.cpp - ${PROJECT_SOURCE_DIR}/src/cpp/YamlReader_participants.cpp - ${PROJECT_SOURCE_DIR}/src/cpp/YamlReader_types.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/reader/YamlReader_generic.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/reader/YamlReader_participants.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/reader/YamlReader_types.cpp ${PROJECT_SOURCE_DIR}/src/cpp/YamlManager.cpp ) diff --git a/ddspipe_yaml/test/unittest/yaml_reader/CMakeLists.txt b/ddspipe_yaml/test/unittest/yaml_reader/CMakeLists.txt index b6bc6909d..7980f50a1 100644 --- a/ddspipe_yaml/test/unittest/yaml_reader/CMakeLists.txt +++ b/ddspipe_yaml/test/unittest/yaml_reader/CMakeLists.txt @@ -19,9 +19,9 @@ set(TEST_NAME YamlReaderScalarTest) set(TEST_SOURCES - ${PROJECT_SOURCE_DIR}/src/cpp/YamlReader_generic.cpp - ${PROJECT_SOURCE_DIR}/src/cpp/YamlReader_participants.cpp - ${PROJECT_SOURCE_DIR}/src/cpp/YamlReader_types.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/reader/YamlReader_generic.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/reader/YamlReader_participants.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/reader/YamlReader_types.cpp YamlReaderScalarTest.cpp ) diff --git a/ddspipe_yaml/test/unittest/yaml_writer/CMakeLists.txt b/ddspipe_yaml/test/unittest/yaml_writer/CMakeLists.txt index 700c43966..096a25c49 100644 --- a/ddspipe_yaml/test/unittest/yaml_writer/CMakeLists.txt +++ b/ddspipe_yaml/test/unittest/yaml_writer/CMakeLists.txt @@ -19,8 +19,8 @@ set(TEST_NAME YamlWriterTest) set(TEST_SOURCES - ${PROJECT_SOURCE_DIR}/src/cpp/YamlReader_generic.cpp - ${PROJECT_SOURCE_DIR}/src/cpp/YamlWriter.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/reader/YamlReader_generic.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/writer/YamlWriter.cpp YamlWriterTest.cpp ) @@ -60,8 +60,8 @@ add_unittest_executable( set(TEST_NAME YamlWriter_collections_test) set(TEST_SOURCES - ${PROJECT_SOURCE_DIR}/src/cpp/YamlReader_generic.cpp - ${PROJECT_SOURCE_DIR}/src/cpp/YamlWriter.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/reader/YamlReader_generic.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/writer/YamlWriter.cpp YamlWriter_collections_test.cpp ) From ac9126709807571e5185647c605a3bcba40f1427 Mon Sep 17 00:00:00 2001 From: jparisu Date: Thu, 27 Apr 2023 17:23:13 +0200 Subject: [PATCH 6/6] Add more data types Signed-off-by: jparisu --- .../src/cpp/testing/arbitrary_values.cpp | 64 +++--- .../types/address/Address.hpp | 18 +- .../DiscoveryServerConnectionAddress.hpp | 27 +-- .../rtps/DiscoveryServerParticipant.cpp | 8 +- .../src/cpp/testing/arbitrary_values.cpp | 59 +++++ .../src/cpp/testing/random_values.cpp | 10 +- .../DiscoveryServerConnectionAddress.cpp | 36 +--- .../ddspipe_yaml/field/YamlObjectField.hpp | 3 +- .../field/impl/YamlObjectField.ipp | 11 +- .../reader/impl/YamlReader_declarations.ipp | 39 ++++ .../impl/YamlReader_implementations.ipp | 87 +++++++- .../writer/impl/YamlWriter_declarations.ipp | 41 ++-- .../impl/YamlWriter_implementations.ipp | 78 +++++-- .../src/cpp/field/YamlObjectField_types.cpp | 17 +- .../src/cpp/reader/YamlReader_generic.cpp | 14 ++ .../src/cpp/reader/YamlReader_types.cpp | 204 +++++++++++++++++- .../src/cpp/writer/YamlWriter_generic.cpp | 14 ++ .../src/cpp/writer/YamlWriter_types.cpp | 83 ++++++- .../generic/GenericReaderWriterTest.cpp | 9 +- 19 files changed, 673 insertions(+), 149 deletions(-) create mode 100644 ddspipe_participants/src/cpp/testing/arbitrary_values.cpp diff --git a/ddspipe_core/src/cpp/testing/arbitrary_values.cpp b/ddspipe_core/src/cpp/testing/arbitrary_values.cpp index e3d5b469d..0e58dc121 100644 --- a/ddspipe_core/src/cpp/testing/arbitrary_values.cpp +++ b/ddspipe_core/src/cpp/testing/arbitrary_values.cpp @@ -39,15 +39,15 @@ template <> TopicQoS arbitrary( unsigned int seed /* = 0 */) { - TopicQoS qos; + TopicQoS object; if (seed % 2) { - qos.reliability_qos = ReliabilityKind::BEST_EFFORT; + object.reliability_qos = ReliabilityKind::BEST_EFFORT; } else { - qos.reliability_qos = ReliabilityKind::RELIABLE; + object.reliability_qos = ReliabilityKind::RELIABLE; } seed /= 2; @@ -55,19 +55,19 @@ TopicQoS arbitrary( switch ((seed / 2) % 4) { case 0: - qos.durability_qos = DurabilityKind::VOLATILE; + object.durability_qos = DurabilityKind::VOLATILE; break; case 1: - qos.durability_qos = DurabilityKind::TRANSIENT_LOCAL; + object.durability_qos = DurabilityKind::TRANSIENT_LOCAL; break; case 2: - qos.durability_qos = DurabilityKind::TRANSIENT; + object.durability_qos = DurabilityKind::TRANSIENT; break; case 3: - qos.durability_qos = DurabilityKind::PERSISTENT; + object.durability_qos = DurabilityKind::PERSISTENT; break; default: @@ -78,66 +78,74 @@ TopicQoS arbitrary( if (seed % 2) { - qos.ownership_qos = OwnershipQosPolicyKind::SHARED_OWNERSHIP_QOS; + object.ownership_qos = OwnershipQosPolicyKind::SHARED_OWNERSHIP_QOS; } else { - qos.ownership_qos = OwnershipQosPolicyKind::EXCLUSIVE_OWNERSHIP_QOS; + object.ownership_qos = OwnershipQosPolicyKind::EXCLUSIVE_OWNERSHIP_QOS; } seed /= 2; - qos.use_partitions = ((seed % 2) == 0); + object.use_partitions = ((seed % 2) == 0); seed /= 2; - qos.keyed = ((seed % 2) == 0); + object.keyed = ((seed % 2) == 0); seed /= 2; - qos.downsampling = (seed % 2) + 1; + object.downsampling = (seed % 2) + 1; seed /= 2; - qos.max_reception_rate = seed % 2; + object.max_reception_rate = seed % 2; seed /= 2; - qos.history_depth = seed; + object.history_depth = seed + 1; - return qos; + return object; } template <> DdsTopic arbitrary( unsigned int seed /* = 0 */) { - DdsTopic topic; - topic.m_topic_name = "TopicName_" + std::to_string(seed); - topic.type_name = "TopicType_" + std::to_string(seed); - topic.topic_qos = arbitrary(seed); - return topic; + DdsTopic object; + object.m_topic_name = "TopicName_" + std::to_string(seed); + object.type_name = "TopicType_" + std::to_string(seed); + object.topic_qos = arbitrary(seed); + return object; } template <> WildcardDdsFilterTopic arbitrary( unsigned int seed /* = 0 */) { - WildcardDdsFilterTopic topic; + WildcardDdsFilterTopic object; if (seed % 2) { - topic.topic_name.set_value("TopicName_" + std::to_string(seed)); + object.topic_name.set_value("TopicName_" + std::to_string(seed)); } seed /= 2; if (seed % 2) { - topic.type_name.set_value("TopicName_" + std::to_string(seed)); + object.type_name.set_value("TopicName_" + std::to_string(seed)); } - return topic; + return object; +} + +template <> +GuidPrefix arbitrary( + unsigned int seed /* = 0 */) +{ + // TODO do it more generic and arbitrary + return GuidPrefix(static_cast(seed)); } //////////////////////////////////////////////// @@ -149,21 +157,21 @@ TopicQoS arbitrary( unsigned int seed, bool limit_durability) { - TopicQoS qos = arbitrary(seed); + TopicQoS object = arbitrary(seed); // Reliability seed /= 2; if (seed % 2) { - qos.durability_qos = DurabilityKind::VOLATILE; + object.durability_qos = DurabilityKind::VOLATILE; } else { - qos.durability_qos = DurabilityKind::TRANSIENT_LOCAL; + object.durability_qos = DurabilityKind::TRANSIENT_LOCAL; } - return qos; + return object; } } /* namespace testing */ diff --git a/ddspipe_participants/include/ddspipe_participants/types/address/Address.hpp b/ddspipe_participants/include/ddspipe_participants/types/address/Address.hpp index 00fd09a82..889bae24e 100644 --- a/ddspipe_participants/include/ddspipe_participants/types/address/Address.hpp +++ b/ddspipe_participants/include/ddspipe_participants/types/address/Address.hpp @@ -18,6 +18,8 @@ #include +#include + #include namespace eprosima { @@ -35,18 +37,18 @@ using DomainType = std::string; using PortType = uint16_t; //! Different versions allowed for IP -enum class IpVersion : int -{ - v4 = 4, - v6 = 6, -}; +ENUMERATION_BUILDER( + IpVersion, + v4, + v6 +); //! Different Transport Protocols allowed -enum class TransportProtocol -{ +ENUMERATION_BUILDER( + TransportProtocol, udp, tcp -}; +); /** * @brief Address that works as a collection of data defining a network address. diff --git a/ddspipe_participants/include/ddspipe_participants/types/address/DiscoveryServerConnectionAddress.hpp b/ddspipe_participants/include/ddspipe_participants/types/address/DiscoveryServerConnectionAddress.hpp index b497ef041..536cfb9e8 100644 --- a/ddspipe_participants/include/ddspipe_participants/types/address/DiscoveryServerConnectionAddress.hpp +++ b/ddspipe_participants/include/ddspipe_participants/types/address/DiscoveryServerConnectionAddress.hpp @@ -33,29 +33,10 @@ namespace types { * This class has several address associated with one \c GuidPrefix in order to connect with * a remote Discovery Server. */ -class DiscoveryServerConnectionAddress +struct DiscoveryServerConnectionAddress { public: - /** - * @brief Construct a new \c DiscoveryServerConnectionAddress object with all the parameters - * - * @param discovery_server_guid_ : Guid Prefix of the remote Discovery Server - * @param addresses_ collection of addresses - */ - DDSPIPE_PARTICIPANTS_DllAPI - DiscoveryServerConnectionAddress( - core::types::GuidPrefix discovery_server_guid, - std::set
addresses); - - //! Discovery Server \c GuidPrefix Port getter - DDSPIPE_PARTICIPANTS_DllAPI - core::types::GuidPrefix discovery_server_guid_prefix() const noexcept; - - //! Addresses getter - DDSPIPE_PARTICIPANTS_DllAPI - std::set
addresses() const noexcept; - /** * @brief Whether the address is correct * @@ -75,13 +56,11 @@ class DiscoveryServerConnectionAddress bool operator ==( const DiscoveryServerConnectionAddress& other) const noexcept; -protected: - //! Internal Discovery Server Guid Prefix object - core::types::GuidPrefix discovery_server_guid_prefix_; + core::types::GuidPrefix discovery_server_guid_prefix; //! Internal Addresses object - std::set
addresses_; + std::set
addresses; }; //! \c DiscoveryServerConnectionAddress to stream serializator diff --git a/ddspipe_participants/src/cpp/participant/rtps/DiscoveryServerParticipant.cpp b/ddspipe_participants/src/cpp/participant/rtps/DiscoveryServerParticipant.cpp index 2892886de..76b7d3117 100644 --- a/ddspipe_participants/src/cpp/participant/rtps/DiscoveryServerParticipant.cpp +++ b/ddspipe_participants/src/cpp/participant/rtps/DiscoveryServerParticipant.cpp @@ -200,22 +200,22 @@ DiscoveryServerParticipant::reckon_participant_attributes_( // Invalid connection address, continue with next one logWarning(DDSPIPE_DISCOVERYSERVER_PARTICIPANT, "Discard connection address with remote server: " << - connection_address.discovery_server_guid_prefix() << + connection_address.discovery_server_guid_prefix << " in Participant " << configuration->id << " initialization."); continue; } // Set Server GUID - core::types::GuidPrefix server_prefix = connection_address.discovery_server_guid_prefix(); + core::types::GuidPrefix server_prefix = connection_address.discovery_server_guid_prefix; - for (types::Address address : connection_address.addresses()) + for (types::Address address : connection_address.addresses) { if (!address.is_valid()) { // Invalid ip address, continue with next one logWarning(DDSPIPE_DISCOVERYSERVER_PARTICIPANT, "Discard connection address with remote server: " << - connection_address.discovery_server_guid_prefix() << + connection_address.discovery_server_guid_prefix << " due to invalid ip address " << address.ip() << " in Participant " << configuration->id << " initialization."); continue; diff --git a/ddspipe_participants/src/cpp/testing/arbitrary_values.cpp b/ddspipe_participants/src/cpp/testing/arbitrary_values.cpp new file mode 100644 index 000000000..3f55eccb3 --- /dev/null +++ b/ddspipe_participants/src/cpp/testing/arbitrary_values.cpp @@ -0,0 +1,59 @@ +// Copyright 2021 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include +#include + +namespace eprosima { +namespace ddspipe { +namespace core { +namespace testing { + +using namespace eprosima::ddspipe::participants::types; + +template <> +Address arbitrary( + unsigned int seed /* = 0 */) +{ + // TODO do it more generic and arbitrary + return Address("127.0.0.1", 10000 + seed, 10000 + seed, TransportProtocol::udp); +} + +template <> +DiscoveryServerConnectionAddress arbitrary( + unsigned int seed /* = 0 */) +{ + DiscoveryServerConnectionAddress object; + + object.discovery_server_guid_prefix = arbitrary(seed); + + unsigned int size = seed % 10; + + // TODO do it more generic and arbitrary + for (unsigned int i = 0; i < size; ++i) + { + object.addresses.insert( + arbitrary
((seed + i))); + } + + return object; +} + +} /* namespace testing */ +} /* namespace core */ +} /* namespace ddspipe */ +} /* namespace eprosima */ diff --git a/ddspipe_participants/src/cpp/testing/random_values.cpp b/ddspipe_participants/src/cpp/testing/random_values.cpp index 2f535513a..f6b15a82c 100644 --- a/ddspipe_participants/src/cpp/testing/random_values.cpp +++ b/ddspipe_participants/src/cpp/testing/random_values.cpp @@ -41,10 +41,12 @@ DiscoveryServerConnectionAddress random_connection_address( random_address((seed + i))); } - return DiscoveryServerConnectionAddress( - core::testing::random_guid_prefix(seed, ros), - addresses - ); + DiscoveryServerConnectionAddress obj; + + obj.discovery_server_guid_prefix = core::testing::random_guid_prefix(seed, ros), + obj.addresses = addresses; + + return obj; } } /* namespace testing */ diff --git a/ddspipe_participants/src/cpp/types/address/DiscoveryServerConnectionAddress.cpp b/ddspipe_participants/src/cpp/types/address/DiscoveryServerConnectionAddress.cpp index d23de30cd..740661206 100644 --- a/ddspipe_participants/src/cpp/types/address/DiscoveryServerConnectionAddress.cpp +++ b/ddspipe_participants/src/cpp/types/address/DiscoveryServerConnectionAddress.cpp @@ -19,32 +19,14 @@ namespace ddspipe { namespace participants { namespace types { -DiscoveryServerConnectionAddress::DiscoveryServerConnectionAddress( - core::types::GuidPrefix discovery_server_guid, - std::set
addresses) - : discovery_server_guid_prefix_(discovery_server_guid) - , addresses_(addresses) -{ -} - -core::types::GuidPrefix DiscoveryServerConnectionAddress::discovery_server_guid_prefix() const noexcept -{ - return discovery_server_guid_prefix_; -} - -std::set
DiscoveryServerConnectionAddress::addresses() const noexcept -{ - return addresses_; -} - bool DiscoveryServerConnectionAddress::is_valid() const noexcept { - if (!discovery_server_guid_prefix_.is_valid()) + if (!discovery_server_guid_prefix.is_valid()) { return false; } - for (auto address : addresses_) + for (auto address : addresses) { if (address.is_valid()) { @@ -58,31 +40,31 @@ bool DiscoveryServerConnectionAddress::is_valid() const noexcept bool DiscoveryServerConnectionAddress::operator <( const DiscoveryServerConnectionAddress& other) const noexcept { - if (this->discovery_server_guid_prefix() == other.discovery_server_guid_prefix()) + if (this->discovery_server_guid_prefix == other.discovery_server_guid_prefix) { // Same Guid - return this->addresses() < other.addresses(); + return this->addresses < other.addresses; } else { // Different guid - return this->discovery_server_guid_prefix() < other.discovery_server_guid_prefix(); + return this->discovery_server_guid_prefix < other.discovery_server_guid_prefix; } } bool DiscoveryServerConnectionAddress::operator ==( const DiscoveryServerConnectionAddress& other) const noexcept { - return (this->discovery_server_guid_prefix() == other.discovery_server_guid_prefix()) && - (this->addresses() == other.addresses()); + return (this->discovery_server_guid_prefix == other.discovery_server_guid_prefix) && + (this->addresses == other.addresses); } std::ostream& operator <<( std::ostream& output, const DiscoveryServerConnectionAddress& address) { - output << "{{" << address.discovery_server_guid_prefix() << "}["; - for (auto a : address.addresses()) + output << "{{" << address.discovery_server_guid_prefix << "}["; + for (auto a : address.addresses) { output << a << ","; } diff --git a/ddspipe_yaml/include/ddspipe_yaml/field/YamlObjectField.hpp b/ddspipe_yaml/include/ddspipe_yaml/field/YamlObjectField.hpp index 4f6b1facf..bd87fd101 100644 --- a/ddspipe_yaml/include/ddspipe_yaml/field/YamlObjectField.hpp +++ b/ddspipe_yaml/include/ddspipe_yaml/field/YamlObjectField.hpp @@ -37,7 +37,8 @@ ENUMERATION_BUILDER OptionalKind, required, optional, - advisable + advisable, + extra ); // Forward declaration diff --git a/ddspipe_yaml/include/ddspipe_yaml/field/impl/YamlObjectField.ipp b/ddspipe_yaml/include/ddspipe_yaml/field/impl/YamlObjectField.ipp index 925435318..ca4e8c970 100644 --- a/ddspipe_yaml/include/ddspipe_yaml/field/impl/YamlObjectField.ipp +++ b/ddspipe_yaml/include/ddspipe_yaml/field/impl/YamlObjectField.ipp @@ -110,16 +110,19 @@ void YamlObjectField::read_field( bool present = is_tag_present(yml, this->tag_); if (present) { - read(yml, this->tag_, vessel_); + // If value in tag present, read it + read(yml, this->tag_, vessel_); } else if (this->kind_ == OptionalKind::required) { + // If field was required and it is not present, fail throw eprosima::utils::ConfigurationException(STR_ENTRY << "Required tag <" << this->tag_ << "> not present."); } else if (this->kind_ == OptionalKind::advisable) { + // If field was advisable and it is not present, show warning logWarning(DDSPIPE_YAML, "Tag <" << this->tag_ << "> is advisable but not present in YAML."); } } @@ -128,7 +131,11 @@ template void YamlObjectField::write_field( Yaml& yml) const { - write(yml, tag_, vessel_); + // Write every field except extra ones + if (kind_ != OptionalKind::extra) + { + write(yml, tag_, vessel_); + } } template diff --git a/ddspipe_yaml/include/ddspipe_yaml/reader/impl/YamlReader_declarations.ipp b/ddspipe_yaml/include/ddspipe_yaml/reader/impl/YamlReader_declarations.ipp index a2d28eb2c..d254d5355 100644 --- a/ddspipe_yaml/include/ddspipe_yaml/reader/impl/YamlReader_declarations.ipp +++ b/ddspipe_yaml/include/ddspipe_yaml/reader/impl/YamlReader_declarations.ipp @@ -14,12 +14,26 @@ #pragma once +#include +#include +#include +#include + #include namespace eprosima { namespace ddspipe { namespace yaml { +//////////////////////////////////////////////// +// DEFINITION OF SPECIFIC TYPES +//////////////////////////////////////////////// + +template +T read_enumeration( + const Yaml& yml, + const std::map& enum_values); + //////////////////////////////////////////////// // DEFINITION OF FUNCTIONS FOR TEMPLATED TYPES //////////////////////////////////////////////// @@ -29,6 +43,16 @@ void read_fuzzy( const Yaml& yml, utils::Fuzzy& fuzzy); +template +void read_collection( + const Yaml& yml, + std::vector& collection); + +template +void read_map( + const Yaml& yml, + std::map& collection); + //////////////////////////////////////////////// // SPECIALIZATION DECLARATIONS FOR TEMPLATED TYPES //////////////////////////////////////////////// @@ -38,6 +62,21 @@ void read( const Yaml& yml, utils::Fuzzy& fuzzy); +template +void read( + const Yaml& yml, + std::vector& collection); + +template +void read( + const Yaml& yml, + std::set& collection); + +template +void read( + const Yaml& yml, + std::map& collection); + } /* namespace yaml */ } /* namespace ddspipe */ } /* namespace eprosima */ diff --git a/ddspipe_yaml/include/ddspipe_yaml/reader/impl/YamlReader_implementations.ipp b/ddspipe_yaml/include/ddspipe_yaml/reader/impl/YamlReader_implementations.ipp index 3318bd408..21a97bd86 100644 --- a/ddspipe_yaml/include/ddspipe_yaml/reader/impl/YamlReader_implementations.ipp +++ b/ddspipe_yaml/include/ddspipe_yaml/reader/impl/YamlReader_implementations.ipp @@ -14,10 +14,41 @@ #pragma once +#include +#include +#include +#include + namespace eprosima { namespace ddspipe { namespace yaml { +//////////////////////////////////////////////// +// DEFINITION OF SPECIFIC TYPES +//////////////////////////////////////////////// + +template +T read_enumeration( + const Yaml& yml, + const std::map& enum_values) +{ + TagType value; + read(yml, value); + + // Find value + auto it = enum_values.find(value); + + if (it == enum_values.end()) + { + throw eprosima::utils::ConfigurationException( + STR_ENTRY << "Value <" << value << "> is not valid in enumeration <" << TYPE_NAME(T) << "."); + } + else + { + return it->second; + } +} + //////////////////////////////////////////////// // DEFINITION OF FUNCTIONS FOR TEMPLATED TYPES //////////////////////////////////////////////// @@ -31,6 +62,32 @@ void read_fuzzy( fuzzy.set_level(); } +template +void read_collection( + const Yaml& yml, + std::vector& collection) +{ + if (!yml.IsSequence()) + { + throw eprosima::utils::ConfigurationException( + STR_ENTRY << "Incorrect format, yaml Sequence expected."); + } + + try + { + for (Yaml yml_element : yml) + { + collection.push_back(read(yml_element)); + } + } + catch (const std::exception& e) + { + throw eprosima::utils::ConfigurationException( + STR_ENTRY << "Error reading yaml sequence of type <" << TYPE_NAME(T) << "> :\n " << + e.what()); + } +} + //////////////////////////////////////////////// // SPECIALIZATION DECLARATIONS FOR TEMPLATED TYPES //////////////////////////////////////////////// @@ -43,6 +100,32 @@ void read( read_fuzzy(yml, fuzzy); } +template +void read( + const Yaml& yml, + std::vector& collection) +{ + read_collection(yml, collection); +} + +template +void read( + const Yaml& yml, + std::set& collection) +{ + std::vector collection_vector; + read_collection(yml, collection_vector); + collection = std::set(collection_vector.begin(), collection_vector.end()); +} + +template +void read( + const Yaml& yml, + std::map& collection) +{ + read_map(yml, collection); +} + //////////////////////////////////////////////// // SPECIALIZATIONS OF SPECIFIC FUNCTIONS //////////////////////////////////////////////// @@ -56,12 +139,12 @@ void read( // ATTENTION: This try catch can be avoided, it is only used to add verbose information try { - return read(get_value_in_tag(yml, tag), object); + return read(get_value_in_tag(yml, tag), object); } catch (const std::exception& e) { throw eprosima::utils::ConfigurationException( - utils::Formatter() << + STR_ENTRY << "Error reading object of type <" << TYPE_NAME(T) << "> in tag <" << tag << "> :\n " << e.what()); } diff --git a/ddspipe_yaml/include/ddspipe_yaml/writer/impl/YamlWriter_declarations.ipp b/ddspipe_yaml/include/ddspipe_yaml/writer/impl/YamlWriter_declarations.ipp index 20c4211b2..ec99107cf 100644 --- a/ddspipe_yaml/include/ddspipe_yaml/writer/impl/YamlWriter_declarations.ipp +++ b/ddspipe_yaml/include/ddspipe_yaml/writer/impl/YamlWriter_declarations.ipp @@ -25,6 +25,16 @@ namespace eprosima { namespace ddspipe { namespace yaml { +//////////////////////////////////////////////// +// DEFINITION OF SPECIFIC TYPES +//////////////////////////////////////////////// + +template +void write_enumeration( + Yaml& yml, + const T& object, + const std::map& enum_values); + //////////////////////////////////////////////// // DEFINITION OF FUNCTIONS FOR TEMPLATED TYPES //////////////////////////////////////////////// @@ -32,17 +42,17 @@ namespace yaml { template void write_fuzzy( Yaml& yml, - const utils::Fuzzy& fuzzy); + const utils::Fuzzy& object); template -void write_collection( +void write_vector( Yaml& yml, - const std::vector& collection); + const std::vector& object); template void write_map( Yaml& yml, - const std::map& collection); + const std::map& object); //////////////////////////////////////////////// // DEFINITION OF FUNCTIONS FOR TRIVIAL TYPES @@ -50,22 +60,19 @@ void write_map( // These are needed for Dll export template <> -DDSPIPE_YAML_DllAPI void write( Yaml& yml, - const std::string& value); + const std::string& object); template <> -DDSPIPE_YAML_DllAPI void write( Yaml& yml, - const bool& value); + const bool& object); template <> -DDSPIPE_YAML_DllAPI void write( Yaml& yml, - const int& value); + const int& object); //////////////////////////////////////////////// // SPECIALIZATION DECLARATIONS FOR TEMPLATED TYPES @@ -74,22 +81,28 @@ void write( template void write( Yaml& yml, - const utils::Fuzzy& collection); + const TagType& tag, + const utils::Fuzzy& object); + +template +void write( + Yaml& yml, + const utils::Fuzzy& object); template void write( Yaml& yml, - const std::vector& collection); + const std::vector& object); template void write( Yaml& yml, - const std::set& collection); + const std::set& object); template void write( Yaml& yml, - const std::map& collection); + const std::map& object); } /* namespace yaml */ } /* namespace ddspipe */ diff --git a/ddspipe_yaml/include/ddspipe_yaml/writer/impl/YamlWriter_implementations.ipp b/ddspipe_yaml/include/ddspipe_yaml/writer/impl/YamlWriter_implementations.ipp index 67d74e338..7e5d99f2a 100644 --- a/ddspipe_yaml/include/ddspipe_yaml/writer/impl/YamlWriter_implementations.ipp +++ b/ddspipe_yaml/include/ddspipe_yaml/writer/impl/YamlWriter_implementations.ipp @@ -14,12 +14,38 @@ #pragma once +#include #include namespace eprosima { namespace ddspipe { namespace yaml { +//////////////////////////////////////////////// +// DEFINITION OF SPECIFIC TYPES +//////////////////////////////////////////////// + +template +void write_enumeration( + Yaml& yml, + const T& value, + const std::map& enum_values) +{ + // TODO check if value exists + for (const auto& it : enum_values) + { + if (value == it.second) + { + write(yml, it.first); + } + return; + } + + // If it finished without finding value, error + throw utils::InconsistencyException( + STR_ENTRY << "Value <" << value << "> of enumeration type <" << TYPE_NAME(T) << " has no parse value."); +} + //////////////////////////////////////////////// // SPECIALIZATIONS FOR TEMPLATED TYPES //////////////////////////////////////////////// @@ -27,33 +53,45 @@ namespace yaml { template void write( Yaml& yml, - const utils::Fuzzy& fuzzy) + const TagType& tag, + const utils::Fuzzy& object) +{ + if (object.is_set()) + { + write(yml, tag, object.get_reference()); + } +} + +template +void write( + Yaml& yml, + const utils::Fuzzy& object) { - write_fuzzy(yml, fuzzy); + write_fuzzy(yml, object); } template void write( Yaml& yml, - const std::vector& collection) + const std::vector& object) { - write_collection(yml, collection); + write_vector(yml, object); } template void write( Yaml& yml, - const std::set& collection) + const std::set& object) { - write_collection(yml, std::vector(collection.begin(), collection.end())); + write_vector(yml, std::vector(object.begin(), object.end())); } template void write( Yaml& yml, - const std::map& collection) + const std::map& object) { - write_map(yml, collection); + write_map(yml, object); } //////////////////////////////////////////////// @@ -64,10 +102,10 @@ template void write( Yaml& yml, const TagType& tag, - const T& value) + const T& object) { auto yml_under_tag = add_tag(yml, tag); - write(yml_under_tag, value); + write(yml_under_tag, object); } template @@ -75,7 +113,7 @@ void operator <<( Yaml& yml, const T& object) { - write(yml, object); + write(yml, object); } //////////////////////////////////////////////// @@ -85,20 +123,20 @@ void operator <<( template void write_fuzzy( Yaml& yml, - const utils::Fuzzy& fuzzy) + const utils::Fuzzy& object) { - write(yml, fuzzy.get_reference()); + write(yml, object.get_reference()); } template -void write_collection( +void write_vector( Yaml& yml, - const std::vector& collection) + const std::vector& object) { - for (const auto& v : collection) + for (const auto& v : object) { Yaml yml_value; - write(yml_value, v); + write(yml_value, v); yml.push_back(yml_value); } } @@ -106,12 +144,12 @@ void write_collection( template void write_map( Yaml& yml, - const std::map& collection) + const std::map& object) { - for (const auto& v : collection) + for (const auto& v : object) { Yaml yml_in_new_tag = yml[utils::generic_to_string(v.first)]; - write(yml_in_new_tag, v.second); + write(yml_in_new_tag, v.second); } } diff --git a/ddspipe_yaml/src/cpp/field/YamlObjectField_types.cpp b/ddspipe_yaml/src/cpp/field/YamlObjectField_types.cpp index 281989637..476c0455d 100644 --- a/ddspipe_yaml/src/cpp/field/YamlObjectField_types.cpp +++ b/ddspipe_yaml/src/cpp/field/YamlObjectField_types.cpp @@ -18,6 +18,8 @@ #include #include +#include + #include #include #include @@ -60,8 +62,19 @@ std::vector> object_fields( { return { - // create_object_field(TOPIC_NAME_TAG, OptionalKind::optional, object.topic_name), - // create_object_field(TOPIC_TYPE_NAME_TAG, OptionalKind::optional, object.type_name), + create_object_field(TOPIC_NAME_TAG, OptionalKind::optional, object.topic_name), + create_object_field(TOPIC_TYPE_NAME_TAG, OptionalKind::optional, object.type_name), + }; +} + +template <> +std::vector> object_fields( + const participants::types::DiscoveryServerConnectionAddress& object) +{ + return + { + create_object_field(DISCOVERY_SERVER_GUID_PREFIX_TAG, OptionalKind::required, object.discovery_server_guid_prefix), + create_object_field(COLLECTION_ADDRESSES_TAG, OptionalKind::advisable, object.addresses), }; } diff --git a/ddspipe_yaml/src/cpp/reader/YamlReader_generic.cpp b/ddspipe_yaml/src/cpp/reader/YamlReader_generic.cpp index a5de8b319..e2d8422f8 100644 --- a/ddspipe_yaml/src/cpp/reader/YamlReader_generic.cpp +++ b/ddspipe_yaml/src/cpp/reader/YamlReader_generic.cpp @@ -53,6 +53,20 @@ void read( object = x; } +template <> +void read( + const Yaml& yml, + unsigned short& object) +{ + short x = yml.as(); + if (x < 0) + { + throw eprosima::utils::ConfigurationException( + utils::Formatter() << "Expected to read a positive integer."); + } + object = x; +} + template <> void read( const Yaml& yml, diff --git a/ddspipe_yaml/src/cpp/reader/YamlReader_types.cpp b/ddspipe_yaml/src/cpp/reader/YamlReader_types.cpp index a41b289f4..72dbf314e 100644 --- a/ddspipe_yaml/src/cpp/reader/YamlReader_types.cpp +++ b/ddspipe_yaml/src/cpp/reader/YamlReader_types.cpp @@ -12,10 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include +#include #include +#include #include +#include +#include + #include #include #include @@ -26,7 +30,66 @@ namespace ddspipe { namespace yaml { //////////////////////////////////////////////// -// IMPLEMENTATION OF TYPES +// IMPLEMENTATION OF ENUMERATIONS +//////////////////////////////////////////////// + +template <> +void read( + const Yaml& yml, + participants::types::TransportProtocol& object) +{ + // TODO get a way of not duplicating this map in write and read + read_enumeration( + yml, + { + {ADDRESS_TRANSPORT_TCP_TAG, participants::types::TransportProtocol::tcp}, + {ADDRESS_TRANSPORT_UDP_TAG, participants::types::TransportProtocol::udp}, + }); +} + +template <> +void read( + const Yaml& yml, + participants::types::IpVersion& object) +{ + read_enumeration( + yml, + { + {ADDRESS_IP_VERSION_V4_TAG, participants::types::IpVersion::v4}, + {ADDRESS_IP_VERSION_V6_TAG, participants::types::IpVersion::v6}, + }); +} + +//////////////////////////////////////////////// +// IMPLEMENTATION OF TYPES BY DEFAULT +//////////////////////////////////////////////// + +template <> +void read( + const Yaml& yml, + core::types::DdsTopic& object) +{ + read_from_fields(yml, object); +} + +template <> +void read( + const Yaml& yml, + core::types::WildcardDdsFilterTopic& object) +{ + read_from_fields(yml, object); +} + +template <> +void read( + const Yaml& yml, + participants::types::DiscoveryServerConnectionAddress& object) +{ + read_from_fields(yml, object); +} + +//////////////////////////////////////////////// +// IMPLEMENTATION OF SPECIFIC TYPES //////////////////////////////////////////////// template <> @@ -79,17 +142,146 @@ void read( template <> void read( const Yaml& yml, - core::types::DdsTopic& object) + core::types::GuidPrefix& object) { - read_from_fields(yml, object); + // If guid exists, use it. Non mandatory. + if (is_tag_present(yml, DISCOVERY_SERVER_GUID_TAG)) + { + std::string guid = read(yml, DISCOVERY_SERVER_GUID_TAG); + object = core::types::GuidPrefix(guid); + return; + } + + // ROS DS is optional. + bool ros_id; + bool ros_id_set = is_tag_present(yml, DISCOVERY_SERVER_ID_ROS_TAG); + if (ros_id_set) + { + ros_id = read(yml, DISCOVERY_SERVER_ID_ROS_TAG); + } + + // Id is mandatory if guid is not present + uint32_t id = read(yml, DISCOVERY_SERVER_ID_TAG); + + // Create GuidPrefix + if (ros_id_set) + { + object = core::types::GuidPrefix(ros_id, id); + } + else + { + object = core::types::GuidPrefix(id); + } } template <> void read( const Yaml& yml, - core::types::WildcardDdsFilterTopic& object) + participants::types::Address& object) { - read_from_fields(yml, object); + // Optional get IP version + participants::types::IpVersion ip_version; + bool ip_version_set = is_tag_present(yml, ADDRESS_IP_VERSION_TAG); + if (ip_version_set) + { + // Get IP Version from enumeration + ip_version = read(yml, ADDRESS_IP_VERSION_TAG); + } + + // Optional get IP + participants::types::IpType ip; + bool ip_set = is_tag_present(yml, ADDRESS_IP_TAG); + if (ip_set) + { + ip = read(yml, ADDRESS_IP_TAG); + } + + // Optional get Domain tag for DNS + std::string domain_name; + bool domain_name_set = is_tag_present(yml, ADDRESS_DNS_TAG); + if (domain_name_set) + { + domain_name = read(yml, ADDRESS_DNS_TAG); + } + + // If IP and domain_name set, warning that domain_name will not be used + // If only domain_name set, get DNS response + // If neither set, get default + if (ip_set && domain_name_set) + { + logWarning(ddspipe_YAML, + "Tag <" << ADDRESS_DNS_TAG << "> will not be used as <" << ADDRESS_IP_TAG << "> is set."); + domain_name_set = false; + } + else if (!ip_set && !domain_name_set) + { + throw eprosima::utils::ConfigurationException(utils::Formatter() << + "Address requires to specify <" << ADDRESS_IP_TAG << "> or <" << ADDRESS_DNS_TAG << ">."); + } + + // Optional get port + participants::types::PortType port; + bool port_set = is_tag_present(yml, ADDRESS_PORT_TAG); + if (port_set) + { + port = read(yml, ADDRESS_PORT_TAG); + } + else + { + port = participants::types::Address::default_port(); + } + + // WARNING: This adds logic to the parse of the entity, + // This may not be the best place to do so. In the future move this logic to the Address class. + + // Optional get external port + // If it is not set, same as internal port is used + participants::types::PortType external_port; + bool external_port_set = is_tag_present(yml, ADDRESS_EXTERNAL_PORT_TAG); + if (external_port_set) + { + external_port = read(yml, ADDRESS_EXTERNAL_PORT_TAG); + } + else + { + external_port = port; + } + + // Optional get Transport protocol + participants::types::TransportProtocol tp; + bool tp_set = is_tag_present(yml, ADDRESS_TRANSPORT_TAG); + if (tp_set) + { + tp = read(yml, ADDRESS_TRANSPORT_TAG); + } + else + { + tp = participants::types::Address::default_transport_protocol(); + } + + // Construct Address object + if (domain_name_set) + { + if (ip_version_set) + { + object = participants::types::Address(port, external_port, ip_version, domain_name, tp); + } + else + { + object = participants::types::Address(port, external_port, domain_name, tp); + } + } + else + { + if (ip_version_set) + { + object = participants::types::Address(ip, port, external_port, ip_version, tp); + } + else + { + object = participants::types::Address(ip, port, external_port, tp); + } + } } } /* namespace yaml */ diff --git a/ddspipe_yaml/src/cpp/writer/YamlWriter_generic.cpp b/ddspipe_yaml/src/cpp/writer/YamlWriter_generic.cpp index b2063c247..8071202ad 100644 --- a/ddspipe_yaml/src/cpp/writer/YamlWriter_generic.cpp +++ b/ddspipe_yaml/src/cpp/writer/YamlWriter_generic.cpp @@ -56,6 +56,20 @@ void write( yml = value; } +template <> +void write( + Yaml& yml, + const unsigned short& value) +{ + int x = value; + if (x < 0) + { + throw eprosima::utils::ConfigurationException( + utils::Formatter() << "Expected to write a positive short."); + } + yml = value; +} + template <> void write( Yaml& yml, diff --git a/ddspipe_yaml/src/cpp/writer/YamlWriter_types.cpp b/ddspipe_yaml/src/cpp/writer/YamlWriter_types.cpp index cea1cf000..33565529e 100644 --- a/ddspipe_yaml/src/cpp/writer/YamlWriter_types.cpp +++ b/ddspipe_yaml/src/cpp/writer/YamlWriter_types.cpp @@ -12,9 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include #include #include +#include +#include + #include #include #include @@ -25,7 +29,68 @@ namespace ddspipe { namespace yaml { //////////////////////////////////////////////// -// IMPLEMENTATION OF TYPES +// IMPLEMENTATION OF ENUMERATIONS +//////////////////////////////////////////////// + +template <> +void write( + Yaml& yml, + const participants::types::TransportProtocol& object) +{ + // TODO get a way of not duplicating this map in write and write + write_enumeration( + yml, + object, + { + {ADDRESS_TRANSPORT_TCP_TAG, participants::types::TransportProtocol::tcp}, + {ADDRESS_TRANSPORT_UDP_TAG, participants::types::TransportProtocol::udp}, + }); +} + +template <> +void write( + Yaml& yml, + const participants::types::IpVersion& object) +{ + write_enumeration( + yml, + object, + { + {ADDRESS_IP_VERSION_V4_TAG, participants::types::IpVersion::v4}, + {ADDRESS_IP_VERSION_V6_TAG, participants::types::IpVersion::v6}, + }); +} + +//////////////////////////////////////////////// +// IMPLEMENTATION OF TYPES BY DEFAULT +//////////////////////////////////////////////// + +template <> +void write( + Yaml& yml, + const core::types::DdsTopic& object) +{ + write_from_fields(yml, object); +} + +template <> +void write( + Yaml& yml, + const core::types::WildcardDdsFilterTopic& object) +{ + write_from_fields(yml, object); +} + +template <> +void write( + Yaml& yml, + const participants::types::DiscoveryServerConnectionAddress& object) +{ + write_from_fields(yml, object); +} + +//////////////////////////////////////////////// +// IMPLEMENTATION OF SPECIFIC TYPES //////////////////////////////////////////////// template <> @@ -43,17 +108,25 @@ void write( template <> void write( Yaml& yml, - const core::types::DdsTopic& object) + const core::types::GuidPrefix& object) { - write_from_fields(yml, object); + write(yml, DISCOVERY_SERVER_GUID_TAG, utils::generic_to_string(object)); } template <> void write( Yaml& yml, - const core::types::WildcardDdsFilterTopic& object) + const participants::types::Address& object) { - write_from_fields(yml, object); + write(yml, ADDRESS_IP_TAG, object.ip()); + write(yml, ADDRESS_PORT_TAG, object.port()); + write(yml, ADDRESS_IP_VERSION_TAG, object.ip_version()); + write(yml, ADDRESS_TRANSPORT_TAG, object.transport_protocol()); + + if (object.port() != object.external_port()) + { + write(yml, ADDRESS_PORT_TAG, object.external_port()); + } } } /* namespace yaml */ diff --git a/ddspipe_yaml/test/unittest/entities/generic/GenericReaderWriterTest.cpp b/ddspipe_yaml/test/unittest/entities/generic/GenericReaderWriterTest.cpp index 00b2c2895..f212d3a1d 100644 --- a/ddspipe_yaml/test/unittest/entities/generic/GenericReaderWriterTest.cpp +++ b/ddspipe_yaml/test/unittest/entities/generic/GenericReaderWriterTest.cpp @@ -17,9 +17,11 @@ #include #include +#include #include #include +#include #include #include @@ -78,8 +80,11 @@ REGISTER_TYPED_TEST_SUITE_P( // Set types used in parametrization typedef ::testing::Types< ddspipe::core::types::DdsTopic, - ddspipe::core::types::TopicQoS - // ddspipe::core::types::WildcardDdsFilterTopic + ddspipe::core::types::TopicQoS, + ddspipe::core::types::WildcardDdsFilterTopic, + ddspipe::core::types::GuidPrefix, + ddspipe::participants::types::Address, + ddspipe::participants::types::DiscoveryServerConnectionAddress > CaseTypes; // Generate each test case for each type case