From cebda998ef18d85926bc4301f036a74a7122ca0f Mon Sep 17 00:00:00 2001 From: Pierre Gergondet Date: Tue, 29 Aug 2023 15:03:34 +0900 Subject: [PATCH] [mc_rtc/Configuration] Add Configuration::find --- include/mc_rtc/Configuration.h | 26 +++++++++++++ src/mc_rtc/Configuration.cpp | 16 ++++++++ tests/testConfiguration.cpp | 62 ++++++++++++++++++++++-------- tests/testConfigurationHelpers.cpp | 16 +++++--- 4 files changed, 99 insertions(+), 21 deletions(-) diff --git a/include/mc_rtc/Configuration.h b/include/mc_rtc/Configuration.h index 5cbe28531d..3f1a44e643 100644 --- a/include/mc_rtc/Configuration.h +++ b/include/mc_rtc/Configuration.h @@ -813,6 +813,32 @@ struct MC_RTC_UTILS_DLLAPI Configuration */ Configuration operator()(const std::string & key) const; + /*! \brief Returns the Configuration entry if it exists, std::nullopt otherwise + * + * \param Key key used to store the value + * + * \returns Configuration entry if it exists, std::nullopt otherwise + */ + std::optional find(const std::string & key) const; + + /*! \brief Returns the value stored within the configuration if it exists, std::nullopt otherwise + * + * \param key Key used to store the value + * + * \returns Value if key is in the configuration, std::nullopt otherwise + * + * \tparam T Type of the entry retrieved + * + * \throws If the conversion to \tparam T fails + */ + template + std::optional find(const std::string & key) const + { + auto maybe_cfg = find(key); + if(maybe_cfg) { return maybe_cfg->operator T(); } + return std::nullopt; + } + /*! \brief Returns true if the underlying array or underlying object is empty */ bool empty() const; diff --git a/src/mc_rtc/Configuration.cpp b/src/mc_rtc/Configuration.cpp index 113fae8fac..b61bf3ceb9 100644 --- a/src/mc_rtc/Configuration.cpp +++ b/src/mc_rtc/Configuration.cpp @@ -200,6 +200,22 @@ Configuration Configuration::operator()(const std::string & key) const throw Exception("No entry named " + key + " in the configuration", v); } +std::optional Configuration::find(const std::string & key) const +{ + assert(v.value_); + auto * value = static_cast(v.value_); + if(v.isObject()) + { + auto ret = value->FindMember(key); + if(ret != value->MemberEnd()) + { + internal::RapidJSONValue * kvalue = &(ret->value); + return Configuration(Configuration::Json{static_cast(kvalue), v.doc_}); + } + } + return std::nullopt; +} + size_t Configuration::size() const { if(v.isArray()) { return v.size(); } diff --git a/tests/testConfiguration.cpp b/tests/testConfiguration.cpp index 9d0bb0dea3..a13412fdab 100644 --- a/tests/testConfiguration.cpp +++ b/tests/testConfiguration.cpp @@ -16,6 +16,14 @@ namespace bfs = boost::filesystem; #include #include +bool silence_exception(const mc_rtc::Configuration::Exception & e) +{ + e.silence(); + return true; +} +#define MC_RTC_CHECK_THROW(statement) \ + BOOST_CHECK_EXCEPTION(statement, mc_rtc::Configuration::Exception, silence_exception) + namespace Eigen { @@ -176,10 +184,12 @@ std::string sampleConfig2(bool fromDisk, bool json) void testConfigurationReading(mc_rtc::Configuration & config, bool fromDisk2, bool json2) { /*! Check that accessing a non-existing entry throws */ - BOOST_CHECK_THROW(config("NONE"), mc_rtc::Configuration::Exception); + MC_RTC_CHECK_THROW(config("NONE")); BOOST_CHECK(config("NONE", std::optional{}) == std::nullopt); + BOOST_CHECK(config.find("NONE") == std::nullopt); + /* int tests */ { /*! Check that we can access a direct int entry */ @@ -227,6 +237,11 @@ void testConfigurationReading(mc_rtc::Configuration & config, bool fromDisk2, bo auto k = config("double", std::optional{std::nullopt}); BOOST_CHECK(k == std::nullopt); + + auto l = config.find("int"); + BOOST_CHECK(l.has_value() && *l == 42); + + MC_RTC_CHECK_THROW([[maybe_unused]] auto m = config.find("double")); } /* unsigned int tests */ @@ -253,9 +268,9 @@ void testConfigurationReading(mc_rtc::Configuration & config, bool fromDisk2, bo BOOST_CHECK_EQUAL(d, 10); /*! Check int to unsigned int does not happen */ - unsigned int h = 10; - config("sint", h); - BOOST_CHECK_EQUAL(h, 10); + unsigned int e = 10; + config("sint", e); + BOOST_CHECK_EQUAL(e, 10); /*! Access a unsigned int from a dict */ unsigned int f = 0; @@ -266,6 +281,11 @@ void testConfigurationReading(mc_rtc::Configuration & config, bool fromDisk2, bo unsigned int g = 0; config("dict")("int", g); BOOST_CHECK_EQUAL(g, 42); + + auto h = config.find("int"); + BOOST_CHECK(h.has_value() && *h == 42); + + MC_RTC_CHECK_THROW([[maybe_unused]] auto i = config.find("double")); } /* double tests */ @@ -349,7 +369,8 @@ void testConfigurationReading(mc_rtc::Configuration & config, bool fromDisk2, bo MC_RTC_diagnostic_push MC_RTC_diagnostic_ignored(GCC, "-Wunused", ClangOnly, "-Wunknown-warning-option", GCC, "-Wunused-but-set-variable") - BOOST_CHECK_THROW(Eigen::Vector2d d = config("v6d"), mc_rtc::Configuration::Exception); + MC_RTC_CHECK_THROW(Eigen::Vector2d d = config("v6d")); + MC_RTC_CHECK_THROW(auto d = config.find("v6d")); MC_RTC_diagnostic_pop Eigen::Vector2d e = Eigen::Vector2d::Zero(); @@ -380,7 +401,8 @@ void testConfigurationReading(mc_rtc::Configuration & config, bool fromDisk2, bo MC_RTC_diagnostic_push MC_RTC_diagnostic_ignored(GCC, "-Wunused", ClangOnly, "-Wunknown-warning-option", GCC, "-Wunused-but-set-variable") - BOOST_CHECK_THROW(mc_rbdyn::Gains2d d = config("v6d"), mc_rtc::Configuration::Exception); + MC_RTC_CHECK_THROW(mc_rbdyn::Gains2d d = config("v6d")); + MC_RTC_CHECK_THROW(auto d = config.find("v6d")); MC_RTC_diagnostic_pop mc_rbdyn::Gains2d e = mc_rbdyn::Gains2d::Zero(); @@ -424,7 +446,8 @@ void testConfigurationReading(mc_rtc::Configuration & config, bool fromDisk2, bo MC_RTC_diagnostic_push MC_RTC_diagnostic_ignored(GCC, "-Wunused", ClangOnly, "-Wunknown-warning-option", GCC, "-Wunused-but-set-variable") - BOOST_CHECK_THROW(Eigen::Vector3d d = config("v6d"), mc_rtc::Configuration::Exception); + MC_RTC_CHECK_THROW(Eigen::Vector3d d = config("v6d")); + MC_RTC_CHECK_THROW(auto d = config.find("v6d")); MC_RTC_diagnostic_pop Eigen::Vector3d e = Eigen::Vector3d::Zero(); @@ -455,7 +478,8 @@ void testConfigurationReading(mc_rtc::Configuration & config, bool fromDisk2, bo MC_RTC_diagnostic_push MC_RTC_diagnostic_ignored(GCC, "-Wunused", ClangOnly, "-Wunknown-warning-option", GCC, "-Wunused-but-set-variable") - BOOST_CHECK_THROW(mc_rbdyn::Gains3d d = config("v6d"), mc_rtc::Configuration::Exception); + MC_RTC_CHECK_THROW(mc_rbdyn::Gains3d d = config("v6d")); + MC_RTC_CHECK_THROW(auto d = config.find("v6d")); MC_RTC_diagnostic_pop mc_rbdyn::Gains3d e = mc_rbdyn::Gains3d::Zero(); @@ -499,7 +523,8 @@ void testConfigurationReading(mc_rtc::Configuration & config, bool fromDisk2, bo MC_RTC_diagnostic_push MC_RTC_diagnostic_ignored(GCC, "-Wunused", ClangOnly, "-Wunknown-warning-option", GCC, "-Wunused-but-set-variable") - BOOST_CHECK_THROW(Eigen::Vector4d d = config("v6d"), mc_rtc::Configuration::Exception); + MC_RTC_CHECK_THROW(Eigen::Vector4d d = config("v6d")); + MC_RTC_CHECK_THROW(auto d = config.find("v6d")); MC_RTC_diagnostic_pop Eigen::Vector4d e = Eigen::Vector4d::Zero(); @@ -530,7 +555,8 @@ void testConfigurationReading(mc_rtc::Configuration & config, bool fromDisk2, bo MC_RTC_diagnostic_push MC_RTC_diagnostic_ignored(GCC, "-Wunused", ClangOnly, "-Wunknown-warning-option", GCC, "-Wunused-but-set-variable") - BOOST_CHECK_THROW(Eigen::Vector6d d = config("v3d"), mc_rtc::Configuration::Exception); + MC_RTC_CHECK_THROW(Eigen::Vector6d d = config("v3d")); + MC_RTC_CHECK_THROW(auto d = config.find("v3d")); MC_RTC_diagnostic_pop Eigen::Vector6d e = Eigen::Vector6d::Zero(); @@ -561,7 +587,8 @@ void testConfigurationReading(mc_rtc::Configuration & config, bool fromDisk2, bo MC_RTC_diagnostic_push MC_RTC_diagnostic_ignored(GCC, "-Wunused", ClangOnly, "-Wunknown-warning-option", GCC, "-Wunused-but-set-variable") - BOOST_CHECK_THROW(mc_rbdyn::Gains6d d = config("v3d"), mc_rtc::Configuration::Exception); + MC_RTC_CHECK_THROW(mc_rbdyn::Gains6d d = config("v3d")); + MC_RTC_CHECK_THROW(auto d = config.find("v3d")); MC_RTC_diagnostic_pop mc_rbdyn::Gains6d e = mc_rbdyn::Gains6d::Zero(); @@ -611,7 +638,8 @@ void testConfigurationReading(mc_rtc::Configuration & config, bool fromDisk2, bo Eigen::VectorXd d = config("vXd"); BOOST_CHECK_EQUAL(d, ref); - BOOST_CHECK_THROW(Eigen::VectorXd e = config("int"), mc_rtc::Configuration::Exception); + MC_RTC_CHECK_THROW(Eigen::VectorXd e = config("int")); + MC_RTC_CHECK_THROW(auto d = config.find("int")); Eigen::VectorXd f = Eigen::VectorXd::Zero(0); f = config("dict")("v6d"); @@ -755,8 +783,8 @@ void testConfigurationReading(mc_rtc::Configuration & config, bool fromDisk2, bo config("dict")("doubleDoublePair", c); BOOST_CHECK(c == ref); - BOOST_CHECK_THROW(c = (std::pair)config("quat"), mc_rtc::Configuration::Exception); - BOOST_CHECK_THROW(c = (std::pair)config("doubleStringPair"), mc_rtc::Configuration::Exception); + MC_RTC_CHECK_THROW(c = (std::pair)config("quat")); + MC_RTC_CHECK_THROW(c = (std::pair)config("doubleStringPair")); } /* pair test */ @@ -992,8 +1020,10 @@ BOOST_AUTO_TEST_CASE(TestConfigurationWriting) BOOST_CHECK(config_test("a3_v") == ref_a3_v); BOOST_CHECK(config_test("a3_a") == ref_a3_a); BOOST_REQUIRE(config_test.has("dict")); + BOOST_REQUIRE(config_test.find("dict").has_value()); BOOST_CHECK(config_test("dict")("int") == ref_int); BOOST_REQUIRE(config_test.has("dict2")); + BOOST_REQUIRE(config_test.find("dict2").has_value()); BOOST_CHECK(config_test("dict2")("double_v") == ref_double_v); /* Save a part of the configuration */ @@ -1210,10 +1240,10 @@ BOOST_AUTO_TEST_CASE(TestNestedLoading) mc_rtc::Configuration c3; c3.load(c2("nested")("b")); BOOST_REQUIRE(c3 == true); - BOOST_CHECK_THROW(c3.add("nested"), mc_rtc::Configuration::Exception); + MC_RTC_CHECK_THROW(c3.add("nested")); c1.load(c2("nested")("b")); BOOST_REQUIRE(c1 == true); - BOOST_CHECK_THROW(c1.add("nested"), mc_rtc::Configuration::Exception); + MC_RTC_CHECK_THROW(c1.add("nested")); } BOOST_AUTO_TEST_CASE(TestConfigurartionRemove) diff --git a/tests/testConfigurationHelpers.cpp b/tests/testConfigurationHelpers.cpp index fc8998987a..ea5597f485 100644 --- a/tests/testConfigurationHelpers.cpp +++ b/tests/testConfigurationHelpers.cpp @@ -5,6 +5,13 @@ #include #include +bool silence_exception(const mc_rtc::Configuration::Exception & e) +{ + e.silence(); + return true; +} +#define MC_RTC_REQUIRE_THROW(statement, exception) BOOST_REQUIRE_EXCEPTION(statement, exception, silence_exception) + template, @@ -21,21 +28,20 @@ void test_fromVectorOrElement(const ElemT & elem, vectorConf.add("vectorNonConvertible", wrongVec); vectorConf.add("elem", elem); vectorConf.add("elemNonConvertible", wrongElem); - mc_rtc::log::info(vectorConf.dump(true)); VecT elemVec{elem}; // Try to load from element BOOST_CHECK(fromVectorOrElement(vectorConf, "elem") == elemVec); BOOST_CHECK(fromVectorOrElement(vectorConf, "elemNonExisting", elemVec) == elemVec); - BOOST_REQUIRE_THROW(fromVectorOrElement(vectorConf, "elemNonExisting"), Configuration::Exception); - BOOST_REQUIRE_THROW(fromVectorOrElement(vectorConf, "elemNonConvertible"), Configuration::Exception); + MC_RTC_REQUIRE_THROW(fromVectorOrElement(vectorConf, "elemNonExisting"), Configuration::Exception); + MC_RTC_REQUIRE_THROW(fromVectorOrElement(vectorConf, "elemNonConvertible"), Configuration::Exception); // Try to load from vector of elements BOOST_CHECK(fromVectorOrElement(vectorConf, "vector") == vec); BOOST_CHECK(fromVectorOrElement(vectorConf, "vectorNonExisting", vec) == vec); - BOOST_REQUIRE_THROW(fromVectorOrElement(vectorConf, "vectorNonExisting"), Configuration::Exception); - BOOST_REQUIRE_THROW(fromVectorOrElement(vectorConf, "vectorNonConvertible"), Configuration::Exception); + MC_RTC_REQUIRE_THROW(fromVectorOrElement(vectorConf, "vectorNonExisting"), Configuration::Exception); + MC_RTC_REQUIRE_THROW(fromVectorOrElement(vectorConf, "vectorNonConvertible"), Configuration::Exception); } BOOST_AUTO_TEST_CASE(TestIOUtils)