diff options
Diffstat (limited to 'contrib/chained/libconfig_chained.h')
-rw-r--r-- | contrib/chained/libconfig_chained.h | 542 |
1 files changed, 542 insertions, 0 deletions
diff --git a/contrib/chained/libconfig_chained.h b/contrib/chained/libconfig_chained.h new file mode 100644 index 0000000..3ff2b46 --- /dev/null +++ b/contrib/chained/libconfig_chained.h @@ -0,0 +1,542 @@ +/* ---------------------------------------------------------------------------- + libconfig - A library for processing structured configuration files + libconfig chained - Extension for reading the configuration and defining + the configuration specification at once. + Copyright (C) 2016 Richard Schubert + + This file is part of libconfig contributions. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, see + <http://www.gnu.org/licenses/>. + ---------------------------------------------------------------------------- +*/ + +#pragma once +#ifndef _CHAINED_LIBCONFIG_H_ +#define _CHAINED_LIBCONFIG_H_ + +#include <libconfig.h++> +#include <cassert> +#include <fstream> +#include <sstream> +#include <iostream> + +namespace libconfig +{ + class ChainedSetting + { + struct Variant + { + private: + bool isSet; + Setting::Type type; + + bool value_bool; + int64_t value_int; + double value_float; + std::string value_string; + + public: + + Variant() + : isSet(false) + , type(Setting::TypeNone) + { + } + Variant(bool value) + { + value_bool = value; + isSet = true; + type = Setting::TypeBoolean; + } + Variant(int32_t value) + { + value_int = value; + isSet = true; + type = Setting::TypeInt; + } + Variant(int64_t value) + { + value_int = value; + isSet = true; + type = Setting::TypeInt64; + } + Variant(double value) + { + value_float = value; + isSet = true; + type = Setting::TypeFloat; + } + Variant(std::string& value) + { + value_string = value; + isSet = true; + type = Setting::TypeString; + } + Variant(const char* value) + { + value_string = value; + isSet = true; + type = Setting::TypeString; + } + + operator bool() const { return value_bool; } + operator int() const { return (int)value_int; } + operator unsigned int() const { return (unsigned int)value_int; } + operator long() const { return (long)value_int; } + operator unsigned long() const { return (unsigned long)value_int; } + operator long long() const { return (long long)value_int; } + operator unsigned long long() const { return (unsigned long long)value_int; } + operator double() const { return value_float; } + operator float() const { return (float)value_float; } + operator std::string() const { return value_string; } + + const bool IsSet() const + { + return isSet; + } + + const Setting::Type GetType() const + { + return type; + } + }; + + public: + + // Starting point for method chained libconfig. + // Pass a custom ostream to intercept any error messages (useful for Applications with UI). + ChainedSetting(Setting& setting, std::ostream& err = std::cerr) + : name(setting.isRoot() ? "<root>" : (setting.getName() ? setting.getName() : "")) + , index(setting.getIndex()) + , parent(NULL) + , setting(&setting) + , err(err) + , isSettingMandatory(false) + , anySettingIsMissing(false) + , anyMandatorySettingIsMissing(false) + , capturedSpecification(NULL) + , capturedSetting(NULL) + { + } + + // Starts capturing any configuration readings into the temporary config object. + void captureExpectedSpecification(Config* temporaryConfigSpecification) + { + capturedSpecification = temporaryConfigSpecification; + capturedSetting = &capturedSpecification->getRoot(); + } + + // Returns the captured configuration specification, + // premised captureExpectedSpecification() was called earlier. + // The path parameter is needed to write the configuration + // to disk before it can be read into a usable string. + std::string getCapturedSpecification(const std::string& tempFilePath) + { + try + { + capturedSpecification->writeFile(tempFilePath.c_str()); + } + catch (const FileIOException&) + { + err << "I/O error while writing temporary setting file: " << tempFilePath << std::endl; + return ""; + } + + std::ifstream t(tempFilePath); + if (!t.is_open()) + { + err << "I/O error while reading temporary setting file: " << tempFilePath << std::endl; + return ""; + } + std::stringstream buffer; + buffer << t.rdbuf(); + + capturedSpecification = NULL; + + return buffer.str(); + } + + // Defines the default value for this setting if missing from config file. + template<typename T> + ChainedSetting& defaultValue(T defaultValue) + { + defaultVal = defaultValue; + return *this; + } + + // Defines the inclusive minimum value for this setting. + // A lesser value set in a configuration file will be clamped to this limit. + template<typename T> + ChainedSetting& min(T min) + { + minVal = min; + return *this; + } + + // Defines the inclusive maximum value for this setting. + // A greater value set in a configuration file will be clamped to this limit. + template<typename T> + ChainedSetting& max(T max) + { + maxVal = max; + return *this; + } + + // Defines this setting to be mandatory. + // Any mandatory value missing in the configuration file will raise an error. + // Use isAnyMandatorySettingMissing() to check for any violations. + ChainedSetting& isMandatory() + { + isSettingMandatory = true; + if (parent) parent->isMandatory(); + return *this; + } + + template<typename T> + operator T() + { + auto requestedType = GetRequestedType<T>(); + CheckType(defaultVal, requestedType); + CheckType(minVal, requestedType); + CheckType(maxVal, requestedType); + + CaptureSetting<T>(requestedType); + + if (!setting) + { + if (isSettingMandatory) + { + AlertMandatorySettingMissing<T>(); + } + PropagateAnySettingIsMissing(); + + return GetDefaultValue<T>(); + } + + try + { + T value = *setting; + if (minVal.IsSet()) + { + T min = minVal; + if (value < min) + { + err << "'" << setting->getPath() << "' setting is out of valid bounds (min: " << min << "). Value was: " << value << std::endl; + value = min; + } + } + if (maxVal.IsSet()) + { + T max = maxVal; + if (value > max) + { + err << "'" << setting->getPath() << "' setting is out of valid bounds (max: " << max << "). Value was: " << value << std::endl; + value = max; + } + } + return value; + } + catch (const SettingTypeException& tex) + { + err << "'" << tex.getPath() << "' setting is of wrong type." << std::endl; + } + + return GetDefaultValue<T>(); + } + + ChainedSetting operator[](const char *name) + { + CaptureSetting<Setting>(Setting::TypeGroup); + + if (!setting) + { + return ChainedSetting(name, this); + } + + if(setting->exists(name)) + { + return ChainedSetting((*setting)[name], this); + } + else + { + return ChainedSetting(name, this); + } + } + + inline ChainedSetting operator[](const std::string &name) + { + return(operator[](name.c_str())); + } + + ChainedSetting operator[](int index) + { + // This could also be an TypeArray but we cannot be sure here. + // By using TypeList we ensure it will always work. + CaptureSetting<Setting>(Setting::TypeList); + + if (!setting) + { + return ChainedSetting(index, this); + } + + if (index >= 0 && index < setting->getLength()) + { + return ChainedSetting((*setting)[index], this); + } + else + { + return ChainedSetting(index, this); + } + } + + int getLength() const + { + return setting ? setting->getLength() : 0; + } + + Setting::Type getType() const + { + return setting ? setting->getType() : Setting::TypeNone; + } + + // Indicates whether this setting is present in the read configuration file. + bool exists() const + { + return setting != NULL; + } + + bool isAnyMandatorySettingMissing() const + { + return anyMandatorySettingIsMissing; + } + + bool isAnySettingMissing() const + { + return anySettingIsMissing; + } + + void clearAnySettingMissingFlag() + { + anySettingIsMissing = false; + } + + private: + + ChainedSetting(Setting& setting, ChainedSetting* parent) + : name(setting.isRoot() ? "<root>" : (setting.getName() ? setting.getName() : "")) + , index(setting.getIndex()) + , parent(parent) + , setting(&setting) + , err(parent->err) + , isSettingMandatory(false) + , anySettingIsMissing(false) + , anyMandatorySettingIsMissing(false) + , capturedSpecification(NULL) + , capturedSetting(NULL) + { + } + + ChainedSetting(const std::string& name, ChainedSetting* parent) + : name(name) + , index(-1) + , parent(parent) + , setting(NULL) + , err(parent->err) + , isSettingMandatory(false) + , anySettingIsMissing(true) + , anyMandatorySettingIsMissing(false) + , capturedSpecification(NULL) + , capturedSetting(NULL) + { + } + + ChainedSetting(int index, ChainedSetting* parent) + : name("") + , index(index) + , parent(parent) + , setting(NULL) + , err(parent->err) + , isSettingMandatory(false) + , anySettingIsMissing(true) + , anyMandatorySettingIsMissing(false) + , capturedSpecification(NULL) + , capturedSetting(NULL) + { + } + + template<typename T> + void ConditionalSetCapturedDefaultValue() + { + *capturedSetting = GetDefaultValue<T>(); + } + + + + template<typename T> + void CaptureSetting(Setting::Type type) + { + if (!capturedSetting && parent && parent->capturedSetting) + { + if (name.length() > 0) + { + if (!parent->capturedSetting->exists(name)) + { + capturedSetting = &parent->capturedSetting->add(name, type); + } + else + { + capturedSetting = &(*parent->capturedSetting)[name.c_str()]; + } + } + else + { + if (index < parent->capturedSetting->getLength()) + { + capturedSetting = &(*parent->capturedSetting)[0]; + } + else + { + assert(index == parent->capturedSetting->getLength()); // you requested an index while omitting at least one of its previous siblings + capturedSetting = &parent->capturedSetting->add(type); + } + } + + ConditionalSetCapturedDefaultValue<T>(); + } + } + + + std::string GetPath() const + { + if (setting) + { + return setting->getPath(); + } + + std::string path = (name.length() > 0) ? name : "[" + std::to_string(index) + "]"; + if (parent) + { + auto parentPath = parent->GetPath(); + return (parentPath.length() > 0) ? (parentPath + ((name.length() == 0) ? "" : ".") + path) : path; + } + return path; + } + + void PropagateAnySettingIsMissing() + { + anySettingIsMissing = true; + if (parent) + { + parent->PropagateAnySettingIsMissing(); + } + } + + void PropagateAnyMandatorySettingIsMissing() + { + anyMandatorySettingIsMissing = true; + if (parent) + { + parent->PropagateAnyMandatorySettingIsMissing(); + } + } + + template<typename T> + void AlertMandatorySettingMissing() + { + PropagateAnyMandatorySettingIsMissing(); + + err << "Missing '" << GetPath() << "' setting in configuration file." << std::endl; + } + + template<typename T> + T GetUnsetDefaultValue() const + { + return (T)0; + } + + + + template<typename T> + T GetDefaultValue() const + { + if (defaultVal.IsSet()) + { + return (T)defaultVal; + } + + return GetUnsetDefaultValue<T>(); + } + + template<typename T> + Setting::Type GetRequestedType() const + { + // TODO @ Hemofektik: Check whether the outcommented line is still needed. static_assert(false) is checked on compile time and, well, asserts :) + // static_assert(false, "should never happen, unless you requested an unsupported type"); + return Setting::TypeNone; + } + + + void CheckType(const Variant& variant, Setting::Type expectedType) const + { + if (!variant.IsSet()) return; + if(expectedType != variant.GetType()) + { + assert(false); // fix your code to match the whole chain of this setting to one single type! + err << "'" << GetPath() << "' setting limits or default value is of incompatible type." << std::endl; + } + } + + std::string name; + int index; + ChainedSetting* parent; + Setting* setting; + std::ostream& err; + Variant defaultVal; + Variant minVal; + Variant maxVal; + bool isSettingMandatory; + bool anySettingIsMissing; + bool anyMandatorySettingIsMissing; + Config* capturedSpecification; + Setting* capturedSetting; + }; + + template<> + inline + void ChainedSetting::ConditionalSetCapturedDefaultValue<Setting>() { } + + template<> + inline + std::string ChainedSetting::GetUnsetDefaultValue() const + { + return ""; + } + + + template<> inline Setting::Type ChainedSetting::GetRequestedType<int8_t>() const { return Setting::TypeInt; } + template<> inline Setting::Type ChainedSetting::GetRequestedType<uint8_t>() const { return Setting::TypeInt; } + template<> inline Setting::Type ChainedSetting::GetRequestedType<int16_t>() const { return Setting::TypeInt; } + template<> inline Setting::Type ChainedSetting::GetRequestedType<uint16_t>() const { return Setting::TypeInt; } + template<> inline Setting::Type ChainedSetting::GetRequestedType<int32_t>() const { return Setting::TypeInt; } + template<> inline Setting::Type ChainedSetting::GetRequestedType<uint32_t>() const { return Setting::TypeInt; } + template<> inline Setting::Type ChainedSetting::GetRequestedType<int64_t>() const { return Setting::TypeInt64; } + template<> inline Setting::Type ChainedSetting::GetRequestedType<uint64_t>() const { return Setting::TypeInt64; } + template<> inline Setting::Type ChainedSetting::GetRequestedType<float>() const { return Setting::TypeFloat; } + template<> inline Setting::Type ChainedSetting::GetRequestedType<double>() const { return Setting::TypeFloat; } + template<> inline Setting::Type ChainedSetting::GetRequestedType<std::string>() const { return Setting::TypeString; } + template<> inline Setting::Type ChainedSetting::GetRequestedType<bool>() const { return Setting::TypeBoolean; } +} + +#endif
\ No newline at end of file |