#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 #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 typedef std::variant GroupKVData; typedef std::vector MultiPart; typedef std::map> GroupKVMapMapVariant; class GroupKV { private: GroupKVMapMapVariant gkv = {}; public: GroupKV(GroupKVMapMapVariant defaultMap) : gkv(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(); } bool group_exists(const std::string &group) { if (gkv.find(group) == gkv.end()) { return false; } return true; } template void set_value(const std::string &group, const MultiPart &keyParts, const SupportedDataType &value, const std::string &callerName="") { auto key = joiner(keyParts); auto &gm = gkv[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. std::string dump(const std::string &group, const std::string &msgTag = "") { std::stringstream ss; for (auto gm: gkv) { if (!group.empty() && (gm.first != group)) { LDBUG_LN("DBUG:GKV:%s:%s:%s:Skipping...", __func__, msgTag.c_str(), gm.first.c_str()); continue; } ss << "\n" << msgTag << gm.first << ":\n"; for(auto k: gm.second) { ss << msgTag << "\t" << k.first << ":" << to_str(k.second) << "\n"; } } return ss.str(); } template SupportedDataType get_value(const std::string &group, const MultiPart &keyParts) { auto key = joiner(keyParts); auto gm = gkv[group]; if (gm.find(key) == gm.end()) { std::stringstream ss; ss << "WARN:GKV:" << __func__ << ":" << group << ":Key [" << key << "] not found"; throw std::range_error(ss.str()); } auto value = gm[key]; return std::get(value); } template SupportedDataType get_value(const std::string &group, const MultiPart &keyParts, const SupportedDataType &defaultValue, const std::string &callerName="") { try { auto value = get_value(group, keyParts); LDBUG_LN("DBUG:GKV:%s_%s:%s:%s:%s", __func__, callerName.c_str(), group.c_str(), to_str(keyParts).c_str(), to_str(value).c_str()); return value; } catch (std::exception &e) { } LDBUG_LN("WARN:GKV:%s_%s:%s:%s:%s[default]", __func__, callerName.c_str(), group.c_str(), to_str(keyParts).c_str(), to_str(defaultValue).c_str()); return defaultValue; } 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 = gkv[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; } };