Skip to content

Commit

Permalink
Add introspection to eckit::Configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
wdeconinck committed Jan 9, 2024
1 parent 391e6f4 commit 50c50ce
Show file tree
Hide file tree
Showing 3 changed files with 226 additions and 0 deletions.
102 changes: 102 additions & 0 deletions src/eckit/config/Configuration.cc
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,108 @@ LocalConfiguration Configuration::getSubConfiguration(const std::string& name) c
return result;
}

bool Configuration::isIntegral(const std::string& name) const {
bool found = false;
eckit::Value v = lookUp(name, found);
return found && v.isNumber();
}

bool Configuration::isBoolean(const std::string& name) const {
bool found = false;
eckit::Value v = lookUp(name, found);
return found && v.isBool();
}

bool Configuration::isFloatingPoint(const std::string& name) const {
bool found = false;
eckit::Value v = lookUp(name, found);
return found && v.isDouble();
}

bool Configuration::isString(const std::string& name) const {
bool found = false;
eckit::Value v = lookUp(name, found);
return found && v.isString();
}

bool Configuration::isList(const std::string& name) const {
bool found = false;
eckit::Value v = lookUp(name, found);
return found && v.isList();
}

bool Configuration::isSubConfiguration(const std::string& name) const {
bool found = false;
eckit::Value v = lookUp(name, found);
return found && (v.isMap() || v.isOrderedMap());
}


bool Configuration::isIntegralList(const std::string& name) const {
bool found = false;
eckit::Value v = lookUp(name, found);
if (found && v.isList()) {
if (v.size() == 0) {
return true;
}
auto& firstElement = v[0];
return firstElement.isNumber();
}
return false;
}

bool Configuration::isBooleanList(const std::string& name) const {
bool found = false;
eckit::Value v = lookUp(name, found);
if (found && v.isList()) {
if (v.size() == 0) {
return true;
}
auto& firstElement = v[0];
return firstElement.isBool();
}
return false;
}

bool Configuration::isFloatingPointList(const std::string& name) const {
bool found = false;
eckit::Value v = lookUp(name, found);
if (found && v.isList()) {
if (v.size() == 0) {
return true;
}
auto& firstElement = v[0];
return firstElement.isDouble();
}
return false;
}

bool Configuration::isStringList(const std::string& name) const {
bool found = false;
eckit::Value v = lookUp(name, found);
if (found && v.isList()) {
if (v.size() == 0) {
return true;
}
auto& firstElement = v[0];
return firstElement.isString();
}
return false;
}

bool Configuration::isSubConfigurationList(const std::string& name) const {
bool found = false;
eckit::Value v = lookUp(name, found);
if (found && v.isList()) {
if (v.size() == 0) {
return true;
}
auto& firstElement = v[0];
return firstElement.isMap() || firstElement.isOrderedMap();
}
return false;
}

template <class T>
void Configuration::_getWithDefault(const std::string& name, T& value, const T& defaultVal) const {
if (!get(name, value)) {
Expand Down
80 changes: 80 additions & 0 deletions src/eckit/config/Configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#include <cstdint>
#include <memory>
#include <type_traits>
#include "eckit/config/Parametrisation.h"


Expand Down Expand Up @@ -130,6 +131,70 @@ class Configuration : public Parametrisation {

virtual void hash(eckit::Hash&) const;

// -- Introspection methods

bool isSubConfiguration(const std::string& name) const;

bool isIntegral(const std::string& name) const;

bool isBoolean(const std::string& name) const;

bool isFloatingPoint(const std::string& name) const;

bool isString(const std::string& name) const;

bool isList(const std::string& name) const;

bool isSubConfigurationList(const std::string& name) const;

bool isIntegralList(const std::string& name) const;

bool isBooleanList(const std::string& name) const;

bool isFloatingPointList(const std::string& name) const;

bool isStringList(const std::string& name) const;

template <typename T>
bool isConvertible(const std::string& name) const {
using _T = std::decay_t<T>;
if constexpr(std::is_base_of_v<LocalConfiguration,_T>) {
return isSubConfiguration(name);
}
else if constexpr(std::is_same_v<_T,int> || std::is_same_v<_T,long> || std::is_same_v<_T,long long> || std::is_same_v<_T,std::size_t>) {
return isIntegral(name) || isBoolean(name);
}
else if constexpr(std::is_same_v<_T,float> || std::is_same_v<_T,double>) {
return isFloatingPoint(name) || isIntegral(name) || isBoolean(name);
}
else if constexpr(std::is_same_v<_T,std::string>) {
return isString(name);
}
else if constexpr(is_vector<_T>::value) {
using _V = std::decay_t<typename _T::value_type>;
if constexpr(std::is_base_of_v<LocalConfiguration,_V>) {
return isSubConfigurationList(name);
}
else if constexpr(std::is_same_v<_V,int> || std::is_same_v<_V,long> || std::is_same_v<_V,long long> || std::is_same_v<_V,std::size_t>) {
return isIntegralList(name) || isBooleanList(name);
}
else if constexpr(std::is_same_v<_V,float> || std::is_same_v<_V,double>) {
return isFloatingPointList(name) || isIntegralList(name) || isBooleanList(name);
}
else if constexpr(std::is_same_v<_V,std::string>) {
return isStringList(name);
}
}
else {
return false;
}
}

template <typename T>
bool isConvertible(const std::string& name, T&) const {
return isConvertible<T>(name);
}

protected: // methods
Configuration(const eckit::Value&, char separator = '.');

Expand Down Expand Up @@ -167,6 +232,21 @@ class Configuration : public Parametrisation {
p.print(s);
return s;
}

private:

// Helper structs for introspection of template T in isConvertible<T> method
template<class T>
struct is_vector {
using type = T ;
constexpr static bool value = false;
};

template<class T>
struct is_vector<std::vector<T>> {
using type = std::vector<T> ;
constexpr static bool value = true;
};
};

//----------------------------------------------------------------------------------------------------------------------
Expand Down
44 changes: 44 additions & 0 deletions tests/config/test_configuration.cc
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,8 @@ CASE("test_local_configuration") {
LocalConfiguration manager;
manager.set("name", "Sidonia");
manager.set("office", 1);
manager.set("height", 1.78);
manager.set("free", false);

std::vector<LocalConfiguration> staff(2);
staff[0].set("name", "Suske");
Expand All @@ -362,13 +364,18 @@ CASE("test_local_configuration") {

local.set("manager", manager);
local.set("staff", staff);

local.set("books.count", 10);
}
const Configuration& conf = local;

LocalConfiguration manager;
std::vector<LocalConfiguration> staff;

EXPECT(conf.get("manager", manager));

EXPECT(conf.isSubConfigurationList("staff"));
EXPECT(conf.isConvertible("staff",staff));
EXPECT(conf.get("staff", staff));

std::string name;
Expand All @@ -388,6 +395,43 @@ CASE("test_local_configuration") {
EXPECT(staff[1].get("office", office));
EXPECT(name == std::string("Wiske"));
EXPECT(office == 3);

int books_count;
EXPECT(conf.has("books"));
EXPECT(conf.get("books.count", books_count));
EXPECT(books_count == 10);

LocalConfiguration books;
EXPECT(conf.isSubConfiguration("books"));
conf.get("books",books);
EXPECT(books.getInt("count") == 10);

EXPECT(conf.isConvertible<LocalConfiguration>("books"));

EXPECT(conf.isList("staff"));
EXPECT(conf.isIntegral("manager.office"));
EXPECT(!conf.isFloatingPoint("manager.office"));
EXPECT(conf.isConvertible<int>("manager.office"));
EXPECT(conf.isConvertible<long>("manager.office"));
EXPECT(conf.isConvertible<long long>("manager.office"));
EXPECT(conf.isConvertible<std::size_t>("manager.office"));
EXPECT(conf.isConvertible<double>("manager.office"));
EXPECT(conf.isConvertible<float>("manager.office"));
EXPECT(!conf.isConvertible<bool>("manager.office"));
EXPECT(!conf.isConvertible<std::string>("manager.office"));
EXPECT(!conf.isConvertible<LocalConfiguration>("manager.office"));

EXPECT(conf.isConvertible<float>("manager.height"));
EXPECT(conf.isConvertible<double>("manager.height"));
EXPECT(!conf.isConvertible<int>("manager.height"));
EXPECT(!conf.isConvertible<bool>("manager.height"));
EXPECT(!conf.isConvertible<std::string>("manager.height"));
EXPECT(!conf.isConvertible<LocalConfiguration>("manager.height"));

double manager_height;
EXPECT(conf.get("manager.height", manager_height));
EXPECT(manager_height == 1.78);

}

CASE("Hash a configuration") {
Expand Down

0 comments on commit 50c50ce

Please sign in to comment.