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:
Ray Speth 2018-02-05 18:50:20 -05:00
parent 530adbb931
commit ea88d4f9fb
3 changed files with 101 additions and 9 deletions

View file

@ -11,6 +11,7 @@
#include <vector>
#include <memory>
#include <unordered_map>
#include <functional>
namespace boost
{
@ -68,16 +69,19 @@ public:
explicit AnyValue(double value);
AnyValue& operator=(double value);
double asDouble() const;
double& asDouble();
const double& asDouble() const;
explicit AnyValue(bool value);
AnyValue& operator=(bool value);
bool asBool() const;
bool& asBool();
const bool& asBool() const;
explicit AnyValue(long int value);
AnyValue& operator=(long int value);
AnyValue& operator=(int value);
long int asInt() const;
long int& asInt();
const long int& asInt() const;
template<class T>
AnyValue& operator=(const std::vector<T>& value);
@ -216,7 +220,17 @@ public:
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:
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;
friend class AnyValue;
};

View file

@ -304,12 +304,18 @@ AnyValue &AnyValue::operator=(double value) {
return *this;
}
double AnyValue::asDouble() const {
double& AnyValue::asDouble() {
if (m_value->type() == typeid(long int)) {
return as<long int>();
} else {
return as<double>();
*m_value = static_cast<double>(as<long int>());
}
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}) {}
@ -319,7 +325,11 @@ AnyValue &AnyValue::operator=(bool value) {
return *this;
}
bool AnyValue::asBool() const {
bool& AnyValue::asBool() {
return as<bool>();
}
const bool& AnyValue::asBool() const {
return as<bool>();
}
@ -335,7 +345,11 @@ AnyValue &AnyValue::operator=(int value) {
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>();
}
@ -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) {
YAML::Node node = YAML::Load(yaml);
return node.as<AnyMap>();

View file

@ -89,6 +89,24 @@ TEST(AnyMap, vector)
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)
{
AnyMap m = AnyMap::fromYamlString(