#pragma once /** * Allows grouping key-value pairs into groups. * by Humans for All * * Allows one to maintain a bunch of groups, where each group contains key-value pairs in them. * The values could belong to any one of these types ie strings, int, float and bool. * * It also tries to provide a crude expanded form of array wrt any of the above supported types. * For this one needs to define keys using the pattern TheKeyName-0, TheKeyName-1, .... */ #include #include #include #include #include #define GKV_DEBUG #define GKV_TEST_PRG #ifdef GKV_TEST_PRG #include #include #define LINFO_LN(FMT, ...) fprintf(stdout, FMT"\n", ##__VA_ARGS__) #ifdef GKV_DEBUG #define LDBUG(FMT, ...) fprintf(stderr, FMT, ##__VA_ARGS__) #define LDBUG_LN(FMT, ...) fprintf(stderr, FMT"\n", ##__VA_ARGS__) #else #define LDBUG_LN(...) #define LDBUG(...) #endif #define LERRR_LN(FMT, ...) fprintf(stderr, FMT"\n", ##__VA_ARGS__) #define LWARN_LN(FMT, ...) fprintf(stderr, FMT"\n", ##__VA_ARGS__) #else #include "log.h" #define LINFO_LN LOG_TEELN #ifdef GKV_DEBUG #define LDBUG LOG #define LDBUG_LN LOGLN #else #define LDBUG_LN(...) #define LDBUG(...) #endif #define LERRR_LN LOG_TEELN #define LWARN_LN LOG_TEELN #endif typedef std::variant GroupKVData; typedef std::vector MultiPart; typedef std::map> GroupKVMapMapVariant; class GroupKV { private: GroupKVMapMapVariant mapV = {}; public: GroupKV(GroupKVMapMapVariant defaultMap) : mapV(defaultMap) {} static std::string joiner(const MultiPart& parts) { std::stringstream joined; int iCnt = 0; for(auto part: parts) { if (iCnt != 0) { joined << "-"; } iCnt += 1; joined << part; } return joined.str(); } std::string to_str(const GroupKVData &value) { auto visitor = [](auto value) -> auto { std::stringstream ss; ss << value; return ss.str(); }; return std::visit(visitor, value); } template std::string to_str(std::vector values) { std::stringstream ss; ss << "[ "; int cnt = 0; for(auto value: values) { cnt += 1; if (cnt != 1) ss << ", "; ss << value; } ss << " ]"; return ss.str(); } template void set_value(const std::string &group, const MultiPart &keyParts, const SupportedDataType &value, const std::string &callerName="") { auto key = joiner(keyParts); auto &gm = mapV[group]; gm[key] = value; LDBUG_LN("DBUG:GKV:%s_%s:%s:%s:%s", __func__, callerName.c_str(), group.c_str(), key.c_str(), to_str(value).c_str()); } // Dump info about the specified group. // If group is empty, then dump info about all groups maintained in this instance. void dump(const std::string &group) { for (auto gm: mapV) { if (!group.empty() && (gm.first != group)) { LINFO_LN("INFO:GKV:%s:%s:Skipping...", __func__, gm.first.c_str()); continue; } for(auto k: gm.second) { LINFO_LN("DBUG:GKV:%s:%s:Iterate:%s:%s", __func__, gm.first.c_str(), k.first.c_str(), to_str(k.second).c_str()); } } } template SupportedDataType get_value(const std::string &group, const MultiPart &keyParts, const SupportedDataType &defaultValue, const std::string &callerName="") { auto key = joiner(keyParts); auto gm = mapV[group]; if (gm.find(key) == gm.end()) { LDBUG_LN("WARN:GKV:%s_%s:%s:%s:%s[default]", __func__, callerName.c_str(), group.c_str(), key.c_str(), to_str(defaultValue).c_str()); return defaultValue; } auto value = gm[key]; LDBUG_LN("DBUG:GKV:%s_%s:%s:%s:%s", __func__, callerName.c_str(), group.c_str(), key.c_str(), to_str(value).c_str()); return std::get(value); } template std::vector get_vector(const std::string &group, const MultiPart &keyParts, const std::vector &defaultValue, const std::string &callerName="") { auto key = joiner(keyParts); auto gm = mapV[group]; std::vector array; int i = 0; while(true) { std::stringstream ssArrayKey; ssArrayKey << key << "-" << i; auto arrayKey = ssArrayKey.str(); if (gm.find(arrayKey) == gm.end()) { break; } array.push_back(std::get(gm[arrayKey])); i += 1; } if (array.empty()) { LDBUG_LN("WARN:GKV:%s_%s:%s:%s:%s[default]", __func__, callerName.c_str(), group.c_str(), key.c_str(), to_str(defaultValue).c_str()); return defaultValue; } LDBUG_LN("DBUG:GKV:%s_%s:%s:%s:%s", __func__, callerName.c_str(), group.c_str(), key.c_str(), to_str(array).c_str()); return array; } }; #ifdef GKV_TEST_PRG void gkv_inited() { GroupKV gkv = {{ {"Group1",{ {"testkey11", 11}, {"testkey12", true} }}, {"Group2", { {"key21", "val21"}, {"key22", 22}, {"key23", 2.3} }} }}; std::cout << "**** gkv inited **** " << std::endl; gkv.dump(""); } void gkv_set() { std::cout << "**** gkv set **** " << std::endl; GroupKV gkv = {{}}; gkv.dump(""); gkv.get_value("testme", {"key101b"}, false); gkv.get_value("testme", {"key101s"}, "Not found"); gkv.get_value("testme", {"key101i"}, 123456); gkv.get_value("testme", {"key101d"}, 123456.789); gkv.set_value("testme", {"key201b"}, true); gkv.set_value("testme", {"key201s"}, "hello world"); gkv.set_value("testme", {"key201i"}, 987654); gkv.set_value("testme", {"key201d"}, 9988.7766); gkv.dump("testme"); gkv.get_value("testme", {"key201b"}, false); gkv.get_value("testme", {"key201s"}, "Not found"); gkv.get_value("testme", {"key201i"}, 123456); gkv.get_value("testme", {"key201d"}, 123456.789); gkv.get_vector("testme", {"keyA100"}, {1, 2, 3}); gkv.get_vector("testme", {"keyA100"}, { "A", "അ", "अ", "ಅ" }); gkv.set_value("testme", {"keyA300-0"}, 330); gkv.set_value("testme", {"keyA300-1"}, 331); gkv.set_value("testme", {"keyA300-2"}, 332); gkv.set_value("testme", {"keyA301-0"}, "India"); gkv.set_value("testme", {"keyA301", "1"}, "World"); gkv.set_value("testme", {"keyA301", "2"}, "AkashaGanga"); gkv.get_vector("testme", {"keyA300"}, {1, 2, 3}); gkv.get_vector("testme", {"keyA301"}, { "yes 1", "No 2", "very well 3" }); } int main(int argc, char **argv) { gkv_inited(); gkv_set(); return 0; } #endif