Add "get" functions to AnyMap
Simplify the common use case of checking for a key and using a default value when it is missing.
This commit is contained in:
parent
530adbb931
commit
ea88d4f9fb
3 changed files with 101 additions and 9 deletions
|
|
@ -11,6 +11,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
namespace boost
|
namespace boost
|
||||||
{
|
{
|
||||||
|
|
@ -68,16 +69,19 @@ public:
|
||||||
|
|
||||||
explicit AnyValue(double value);
|
explicit AnyValue(double value);
|
||||||
AnyValue& operator=(double value);
|
AnyValue& operator=(double value);
|
||||||
double asDouble() const;
|
double& asDouble();
|
||||||
|
const double& asDouble() const;
|
||||||
|
|
||||||
explicit AnyValue(bool value);
|
explicit AnyValue(bool value);
|
||||||
AnyValue& operator=(bool value);
|
AnyValue& operator=(bool value);
|
||||||
bool asBool() const;
|
bool& asBool();
|
||||||
|
const bool& asBool() const;
|
||||||
|
|
||||||
explicit AnyValue(long int value);
|
explicit AnyValue(long int value);
|
||||||
AnyValue& operator=(long int value);
|
AnyValue& operator=(long int value);
|
||||||
AnyValue& operator=(int value);
|
AnyValue& operator=(int value);
|
||||||
long int asInt() const;
|
long int& asInt();
|
||||||
|
const long int& asInt() const;
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
AnyValue& operator=(const std::vector<T>& value);
|
AnyValue& operator=(const std::vector<T>& value);
|
||||||
|
|
@ -216,7 +220,17 @@ public:
|
||||||
|
|
||||||
bool hasKey(const std::string& key) const;
|
bool hasKey(const std::string& key) const;
|
||||||
|
|
||||||
|
bool getBool(const std::string& key, bool default_) const;
|
||||||
|
long int getInt(const std::string& key, long int default_) const;
|
||||||
|
double getDouble(const std::string& key, double default_) const;
|
||||||
|
const std::string& getString(const std::string& key,
|
||||||
|
const std::string& default_) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
template <class T>
|
||||||
|
const T& get(const std::string& key, const T& default_,
|
||||||
|
std::function<const T&(const AnyValue*)> getter) const;
|
||||||
|
|
||||||
std::unordered_map<std::string, AnyValue> m_data;
|
std::unordered_map<std::string, AnyValue> m_data;
|
||||||
friend class AnyValue;
|
friend class AnyValue;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -304,12 +304,18 @@ AnyValue &AnyValue::operator=(double value) {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
double AnyValue::asDouble() const {
|
double& AnyValue::asDouble() {
|
||||||
if (m_value->type() == typeid(long int)) {
|
if (m_value->type() == typeid(long int)) {
|
||||||
return as<long int>();
|
*m_value = static_cast<double>(as<long int>());
|
||||||
} else {
|
|
||||||
return as<double>();
|
|
||||||
}
|
}
|
||||||
|
return as<double>();
|
||||||
|
}
|
||||||
|
|
||||||
|
const double& AnyValue::asDouble() const {
|
||||||
|
if (m_value->type() == typeid(long int)) {
|
||||||
|
*m_value = static_cast<double>(as<long int>());
|
||||||
|
}
|
||||||
|
return as<double>();
|
||||||
}
|
}
|
||||||
|
|
||||||
AnyValue::AnyValue(bool value) : m_value(new boost::any{value}) {}
|
AnyValue::AnyValue(bool value) : m_value(new boost::any{value}) {}
|
||||||
|
|
@ -319,7 +325,11 @@ AnyValue &AnyValue::operator=(bool value) {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AnyValue::asBool() const {
|
bool& AnyValue::asBool() {
|
||||||
|
return as<bool>();
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool& AnyValue::asBool() const {
|
||||||
return as<bool>();
|
return as<bool>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -335,7 +345,11 @@ AnyValue &AnyValue::operator=(int value) {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
long int AnyValue::asInt() const {
|
long int& AnyValue::asInt() {
|
||||||
|
return as<long int>();
|
||||||
|
}
|
||||||
|
|
||||||
|
const long int& AnyValue::asInt() const {
|
||||||
return as<long int>();
|
return as<long int>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -518,6 +532,52 @@ bool AnyMap::hasKey(const std::string& key) const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
const T& AnyMap::get(const std::string& key, const T& default_,
|
||||||
|
std::function<const T&(const AnyValue*)> getter) const
|
||||||
|
{
|
||||||
|
const auto& slash = boost::ifind_first(key, "/");
|
||||||
|
if (!slash) {
|
||||||
|
if (m_data.find(key) != m_data.end()) {
|
||||||
|
return getter(&m_data.at(key));
|
||||||
|
} else {
|
||||||
|
return default_;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::string head(key.begin(), slash.begin());
|
||||||
|
std::string tail(slash.end(), key.end());
|
||||||
|
if (m_data.find(head) == m_data.end() || !m_data.at(head).is<AnyMap>()) {
|
||||||
|
return default_;
|
||||||
|
} else {
|
||||||
|
return m_data.at(head).as<AnyMap>().get(tail, default_, getter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AnyMap::getBool(const std::string& key, bool default_) const
|
||||||
|
{
|
||||||
|
return get<bool>(key, default_,
|
||||||
|
std::mem_fun<const bool&, const AnyValue>(&AnyValue::asBool));
|
||||||
|
}
|
||||||
|
|
||||||
|
double AnyMap::getDouble(const std::string& key, double default_) const
|
||||||
|
{
|
||||||
|
return get<double>(key, default_,
|
||||||
|
std::mem_fun<const double&, const AnyValue>(&AnyValue::asDouble));
|
||||||
|
}
|
||||||
|
|
||||||
|
long int AnyMap::getInt(const std::string& key, long int default_) const
|
||||||
|
{
|
||||||
|
return get<long int>(key, default_,
|
||||||
|
std::mem_fun<const long int&, const AnyValue>(&AnyValue::asInt));
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& AnyMap::getString(const std::string& key,
|
||||||
|
const std::string& default_) const
|
||||||
|
{
|
||||||
|
return get<std::string>(key, default_, &AnyValue::asString);
|
||||||
|
}
|
||||||
|
|
||||||
AnyMap AnyMap::fromYamlString(const std::string& yaml) {
|
AnyMap AnyMap::fromYamlString(const std::string& yaml) {
|
||||||
YAML::Node node = YAML::Load(yaml);
|
YAML::Node node = YAML::Load(yaml);
|
||||||
return node.as<AnyMap>();
|
return node.as<AnyMap>();
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,24 @@ TEST(AnyMap, vector)
|
||||||
EXPECT_EQ(m["nested/item"].asVector<double>().size(), (size_t) 4);
|
EXPECT_EQ(m["nested/item"].asVector<double>().size(), (size_t) 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(AnyMap, getters_with_defaults)
|
||||||
|
{
|
||||||
|
AnyMap m;
|
||||||
|
std::map<std::string, double> zz{{"a", 9.0}, {"b", 13.5}};
|
||||||
|
m["foo"] = zz;
|
||||||
|
m["foo/c"] = 4;
|
||||||
|
m["bar"] = "baz";
|
||||||
|
m["qux"] = false;
|
||||||
|
EXPECT_FALSE(m.getBool("qux", true));
|
||||||
|
EXPECT_TRUE(m.getBool("missing", true));
|
||||||
|
EXPECT_EQ(m.getString("missing", "hi"), "hi");
|
||||||
|
EXPECT_EQ(m.getString("bar", "hi"), "baz");
|
||||||
|
EXPECT_EQ(m.getInt("missing", 3), 3);
|
||||||
|
EXPECT_EQ(m.getInt("foo/c", 3), 4);
|
||||||
|
EXPECT_EQ(m.getDouble("foo/missing", 3), 3);
|
||||||
|
EXPECT_EQ(m.getDouble("foo/b", 3), 13.5);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(AnyMap, conversion_to_double)
|
TEST(AnyMap, conversion_to_double)
|
||||||
{
|
{
|
||||||
AnyMap m = AnyMap::fromYamlString(
|
AnyMap m = AnyMap::fromYamlString(
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue