Skip to content

Commit

Permalink
[mc_rtc/Configuration] Add Configuration::find
Browse files Browse the repository at this point in the history
  • Loading branch information
gergondet committed Sep 12, 2023
1 parent 8c0ffc4 commit cebda99
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 21 deletions.
26 changes: 26 additions & 0 deletions include/mc_rtc/Configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<Configuration> 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<typename T>
std::optional<T> 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;

Expand Down
16 changes: 16 additions & 0 deletions src/mc_rtc/Configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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> Configuration::find(const std::string & key) const
{
assert(v.value_);
auto * value = static_cast<internal::RapidJSONValue *>(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<void *>(kvalue), v.doc_});
}
}
return std::nullopt;
}

size_t Configuration::size() const
{
if(v.isArray()) { return v.size(); }
Expand Down
62 changes: 46 additions & 16 deletions tests/testConfiguration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ namespace bfs = boost::filesystem;
#include <fstream>
#include <iostream>

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
{

Expand Down Expand Up @@ -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<mc_rtc::Configuration>{}) == std::nullopt);

BOOST_CHECK(config.find("NONE") == std::nullopt);

/* int tests */
{
/*! Check that we can access a direct int entry */
Expand Down Expand Up @@ -227,6 +237,11 @@ void testConfigurationReading(mc_rtc::Configuration & config, bool fromDisk2, bo

auto k = config("double", std::optional<int>{std::nullopt});
BOOST_CHECK(k == std::nullopt);

auto l = config.find<int>("int");
BOOST_CHECK(l.has_value() && *l == 42);

MC_RTC_CHECK_THROW([[maybe_unused]] auto m = config.find<int>("double"));
}

/* unsigned int tests */
Expand All @@ -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;
Expand All @@ -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<unsigned int>("int");
BOOST_CHECK(h.has_value() && *h == 42);

MC_RTC_CHECK_THROW([[maybe_unused]] auto i = config.find<unsigned int>("double"));
}

/* double tests */
Expand Down Expand Up @@ -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<Eigen::Vector2d>("v6d"));
MC_RTC_diagnostic_pop

Eigen::Vector2d e = Eigen::Vector2d::Zero();
Expand Down Expand Up @@ -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<mc_rbdyn::Gains2d>("v6d"));
MC_RTC_diagnostic_pop

mc_rbdyn::Gains2d e = mc_rbdyn::Gains2d::Zero();
Expand Down Expand Up @@ -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<Eigen::Vector3d>("v6d"));
MC_RTC_diagnostic_pop

Eigen::Vector3d e = Eigen::Vector3d::Zero();
Expand Down Expand Up @@ -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<mc_rbdyn::Gains3d>("v6d"));
MC_RTC_diagnostic_pop

mc_rbdyn::Gains3d e = mc_rbdyn::Gains3d::Zero();
Expand Down Expand Up @@ -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<Eigen::Vector4d>("v6d"));
MC_RTC_diagnostic_pop

Eigen::Vector4d e = Eigen::Vector4d::Zero();
Expand Down Expand Up @@ -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<Eigen::Vector6d>("v3d"));
MC_RTC_diagnostic_pop

Eigen::Vector6d e = Eigen::Vector6d::Zero();
Expand Down Expand Up @@ -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<mc_rbdyn::Gains6d>("v3d"));
MC_RTC_diagnostic_pop

mc_rbdyn::Gains6d e = mc_rbdyn::Gains6d::Zero();
Expand Down Expand Up @@ -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<Eigen::VectorXd>("int"));

Eigen::VectorXd f = Eigen::VectorXd::Zero(0);
f = config("dict")("v6d");
Expand Down Expand Up @@ -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<double, double>)config("quat"), mc_rtc::Configuration::Exception);
BOOST_CHECK_THROW(c = (std::pair<double, double>)config("doubleStringPair"), mc_rtc::Configuration::Exception);
MC_RTC_CHECK_THROW(c = (std::pair<double, double>)config("quat"));
MC_RTC_CHECK_THROW(c = (std::pair<double, double>)config("doubleStringPair"));
}

/* pair<double, string> test */
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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)
Expand Down
16 changes: 11 additions & 5 deletions tests/testConfigurationHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@
#include <mc_rtc/ConfigurationHelpers.h>
#include <boost/test/unit_test.hpp>

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<typename ElemT,
typename WrongElemT,
typename VecT = std::vector<ElemT>,
Expand All @@ -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<ElemT>(vectorConf, "elem") == elemVec);
BOOST_CHECK(fromVectorOrElement<ElemT>(vectorConf, "elemNonExisting", elemVec) == elemVec);
BOOST_REQUIRE_THROW(fromVectorOrElement<ElemT>(vectorConf, "elemNonExisting"), Configuration::Exception);
BOOST_REQUIRE_THROW(fromVectorOrElement<ElemT>(vectorConf, "elemNonConvertible"), Configuration::Exception);
MC_RTC_REQUIRE_THROW(fromVectorOrElement<ElemT>(vectorConf, "elemNonExisting"), Configuration::Exception);
MC_RTC_REQUIRE_THROW(fromVectorOrElement<ElemT>(vectorConf, "elemNonConvertible"), Configuration::Exception);

// Try to load from vector of elements
BOOST_CHECK(fromVectorOrElement<ElemT>(vectorConf, "vector") == vec);
BOOST_CHECK(fromVectorOrElement<ElemT>(vectorConf, "vectorNonExisting", vec) == vec);
BOOST_REQUIRE_THROW(fromVectorOrElement<ElemT>(vectorConf, "vectorNonExisting"), Configuration::Exception);
BOOST_REQUIRE_THROW(fromVectorOrElement<ElemT>(vectorConf, "vectorNonConvertible"), Configuration::Exception);
MC_RTC_REQUIRE_THROW(fromVectorOrElement<ElemT>(vectorConf, "vectorNonExisting"), Configuration::Exception);
MC_RTC_REQUIRE_THROW(fromVectorOrElement<ElemT>(vectorConf, "vectorNonConvertible"), Configuration::Exception);
}

BOOST_AUTO_TEST_CASE(TestIOUtils)
Expand Down

0 comments on commit cebda99

Please sign in to comment.