1 /* ---------------------------------------------------------------------------- 2 libconfig - A library for processing structured configuration files 3 libconfig chained - Extension for reading the configuration and defining 4 the configuration specification at once. 5 Copyright (C) 2016 Richard Schubert 6 7 This file is part of libconfig contributions. 8 9 This library is free software; you can redistribute it and/or 10 modify it under the terms of the GNU Lesser General Public License 11 as published by the Free Software Foundation; either version 2.1 of 12 the License, or (at your option) any later version. 13 14 This library is distributed in the hope that it will be useful, but 15 WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 Lesser General Public License for more details. 18 19 You should have received a copy of the GNU Library General Public 20 License along with this library; if not, see 21 <http://www.gnu.org/licenses/>. 22 ---------------------------------------------------------------------------- 23 */ 24 25 #pragma once 26 #ifndef _CHAINED_LIBCONFIG_H_ 27 #define _CHAINED_LIBCONFIG_H_ 28 29 #include <libconfig.h++> 30 #include <cassert> 31 #include <fstream> 32 #include <sstream> 33 #include <iostream> 34 35 namespace libconfig 36 { 37 class ChainedSetting 38 { 39 struct Variant 40 { 41 private: 42 bool isSet; 43 Setting::Type type; 44 45 bool value_bool; 46 int64_t value_int; 47 double value_float; 48 std::string value_string; 49 50 public: 51 VariantVariant52 Variant() 53 : isSet(false) 54 , type(Setting::TypeNone) 55 { 56 } VariantVariant57 Variant(bool value) 58 { 59 value_bool = value; 60 isSet = true; 61 type = Setting::TypeBoolean; 62 } VariantVariant63 Variant(int32_t value) 64 { 65 value_int = value; 66 isSet = true; 67 type = Setting::TypeInt; 68 } VariantVariant69 Variant(int64_t value) 70 { 71 value_int = value; 72 isSet = true; 73 type = Setting::TypeInt64; 74 } VariantVariant75 Variant(double value) 76 { 77 value_float = value; 78 isSet = true; 79 type = Setting::TypeFloat; 80 } VariantVariant81 Variant(std::string& value) 82 { 83 value_string = value; 84 isSet = true; 85 type = Setting::TypeString; 86 } VariantVariant87 Variant(const char* value) 88 { 89 value_string = value; 90 isSet = true; 91 type = Setting::TypeString; 92 } 93 94 operator bool() const { return value_bool; } 95 operator int() const { return (int)value_int; } 96 operator unsigned int() const { return (unsigned int)value_int; } 97 operator long() const { return (long)value_int; } 98 operator unsigned long() const { return (unsigned long)value_int; } 99 operator long long() const { return (long long)value_int; } 100 operator unsigned long long() const { return (unsigned long long)value_int; } 101 operator double() const { return value_float; } 102 operator float() const { return (float)value_float; } stringVariant103 operator std::string() const { return value_string; } 104 IsSetVariant105 const bool IsSet() const 106 { 107 return isSet; 108 } 109 GetTypeVariant110 const Setting::Type GetType() const 111 { 112 return type; 113 } 114 }; 115 116 public: 117 118 // Starting point for method chained libconfig. 119 // Pass a custom ostream to intercept any error messages (useful for Applications with UI). 120 ChainedSetting(Setting& setting, std::ostream& err = std::cerr) 121 : name(setting.isRoot() ? "<root>" : (setting.getName() ? setting.getName() : "")) 122 , index(setting.getIndex()) 123 , parent(NULL) 124 , setting(&setting) 125 , err(err) 126 , isSettingMandatory(false) 127 , anySettingIsMissing(false) 128 , anyMandatorySettingIsMissing(false) 129 , capturedSpecification(NULL) 130 , capturedSetting(NULL) 131 { 132 } 133 134 // Starts capturing any configuration readings into the temporary config object. captureExpectedSpecification(Config * temporaryConfigSpecification)135 void captureExpectedSpecification(Config* temporaryConfigSpecification) 136 { 137 capturedSpecification = temporaryConfigSpecification; 138 capturedSetting = &capturedSpecification->getRoot(); 139 } 140 141 // Returns the captured configuration specification, 142 // premised captureExpectedSpecification() was called earlier. 143 // The path parameter is needed to write the configuration 144 // to disk before it can be read into a usable string. getCapturedSpecification(const std::string & tempFilePath)145 std::string getCapturedSpecification(const std::string& tempFilePath) 146 { 147 try 148 { 149 capturedSpecification->writeFile(tempFilePath.c_str()); 150 } 151 catch (const FileIOException&) 152 { 153 err << "I/O error while writing temporary setting file: " << tempFilePath << std::endl; 154 return ""; 155 } 156 157 std::ifstream t(tempFilePath); 158 if (!t.is_open()) 159 { 160 err << "I/O error while reading temporary setting file: " << tempFilePath << std::endl; 161 return ""; 162 } 163 std::stringstream buffer; 164 buffer << t.rdbuf(); 165 166 capturedSpecification = NULL; 167 168 return buffer.str(); 169 } 170 171 // Defines the default value for this setting if missing from config file. 172 template<typename T> defaultValue(T defaultValue)173 ChainedSetting& defaultValue(T defaultValue) 174 { 175 defaultVal = defaultValue; 176 return *this; 177 } 178 179 // Defines the inclusive minimum value for this setting. 180 // A lesser value set in a configuration file will be clamped to this limit. 181 template<typename T> min(T min)182 ChainedSetting& min(T min) 183 { 184 minVal = min; 185 return *this; 186 } 187 188 // Defines the inclusive maximum value for this setting. 189 // A greater value set in a configuration file will be clamped to this limit. 190 template<typename T> max(T max)191 ChainedSetting& max(T max) 192 { 193 maxVal = max; 194 return *this; 195 } 196 197 // Defines this setting to be mandatory. 198 // Any mandatory value missing in the configuration file will raise an error. 199 // Use isAnyMandatorySettingMissing() to check for any violations. isMandatory()200 ChainedSetting& isMandatory() 201 { 202 isSettingMandatory = true; 203 if (parent) parent->isMandatory(); 204 return *this; 205 } 206 207 template<typename T> T()208 operator T() 209 { 210 auto requestedType = GetRequestedType<T>(); 211 CheckType(defaultVal, requestedType); 212 CheckType(minVal, requestedType); 213 CheckType(maxVal, requestedType); 214 215 CaptureSetting<T>(requestedType); 216 217 if (!setting) 218 { 219 if (isSettingMandatory) 220 { 221 AlertMandatorySettingMissing<T>(); 222 } 223 PropagateAnySettingIsMissing(); 224 225 return GetDefaultValue<T>(); 226 } 227 228 try 229 { 230 T value = *setting; 231 if (minVal.IsSet()) 232 { 233 T min = minVal; 234 if (value < min) 235 { 236 err << "'" << setting->getPath() << "' setting is out of valid bounds (min: " << min << "). Value was: " << value << std::endl; 237 value = min; 238 } 239 } 240 if (maxVal.IsSet()) 241 { 242 T max = maxVal; 243 if (value > max) 244 { 245 err << "'" << setting->getPath() << "' setting is out of valid bounds (max: " << max << "). Value was: " << value << std::endl; 246 value = max; 247 } 248 } 249 return value; 250 } 251 catch (const SettingTypeException& tex) 252 { 253 err << "'" << tex.getPath() << "' setting is of wrong type." << std::endl; 254 } 255 256 return GetDefaultValue<T>(); 257 } 258 259 ChainedSetting operator[](const char *name) 260 { 261 CaptureSetting<Setting>(Setting::TypeGroup); 262 263 if (!setting) 264 { 265 return ChainedSetting(name, this); 266 } 267 268 if(setting->exists(name)) 269 { 270 return ChainedSetting((*setting)[name], this); 271 } 272 else 273 { 274 return ChainedSetting(name, this); 275 } 276 } 277 278 inline ChainedSetting operator[](const std::string &name) 279 { 280 return(operator[](name.c_str())); 281 } 282 283 ChainedSetting operator[](int index) 284 { 285 // This could also be an TypeArray but we cannot be sure here. 286 // By using TypeList we ensure it will always work. 287 CaptureSetting<Setting>(Setting::TypeList); 288 289 if (!setting) 290 { 291 return ChainedSetting(index, this); 292 } 293 294 if (index >= 0 && index < setting->getLength()) 295 { 296 return ChainedSetting((*setting)[index], this); 297 } 298 else 299 { 300 return ChainedSetting(index, this); 301 } 302 } 303 getLength()304 int getLength() const 305 { 306 return setting ? setting->getLength() : 0; 307 } 308 getType()309 Setting::Type getType() const 310 { 311 return setting ? setting->getType() : Setting::TypeNone; 312 } 313 314 // Indicates whether this setting is present in the read configuration file. exists()315 bool exists() const 316 { 317 return setting != NULL; 318 } 319 isAnyMandatorySettingMissing()320 bool isAnyMandatorySettingMissing() const 321 { 322 return anyMandatorySettingIsMissing; 323 } 324 isAnySettingMissing()325 bool isAnySettingMissing() const 326 { 327 return anySettingIsMissing; 328 } 329 clearAnySettingMissingFlag()330 void clearAnySettingMissingFlag() 331 { 332 anySettingIsMissing = false; 333 } 334 335 private: 336 ChainedSetting(Setting & setting,ChainedSetting * parent)337 ChainedSetting(Setting& setting, ChainedSetting* parent) 338 : name(setting.isRoot() ? "<root>" : (setting.getName() ? setting.getName() : "")) 339 , index(setting.getIndex()) 340 , parent(parent) 341 , setting(&setting) 342 , err(parent->err) 343 , isSettingMandatory(false) 344 , anySettingIsMissing(false) 345 , anyMandatorySettingIsMissing(false) 346 , capturedSpecification(NULL) 347 , capturedSetting(NULL) 348 { 349 } 350 ChainedSetting(const std::string & name,ChainedSetting * parent)351 ChainedSetting(const std::string& name, ChainedSetting* parent) 352 : name(name) 353 , index(-1) 354 , parent(parent) 355 , setting(NULL) 356 , err(parent->err) 357 , isSettingMandatory(false) 358 , anySettingIsMissing(true) 359 , anyMandatorySettingIsMissing(false) 360 , capturedSpecification(NULL) 361 , capturedSetting(NULL) 362 { 363 } 364 ChainedSetting(int index,ChainedSetting * parent)365 ChainedSetting(int index, ChainedSetting* parent) 366 : name("") 367 , index(index) 368 , parent(parent) 369 , setting(NULL) 370 , err(parent->err) 371 , isSettingMandatory(false) 372 , anySettingIsMissing(true) 373 , anyMandatorySettingIsMissing(false) 374 , capturedSpecification(NULL) 375 , capturedSetting(NULL) 376 { 377 } 378 379 template<typename T> ConditionalSetCapturedDefaultValue()380 void ConditionalSetCapturedDefaultValue() 381 { 382 *capturedSetting = GetDefaultValue<T>(); 383 } 384 385 386 387 template<typename T> CaptureSetting(Setting::Type type)388 void CaptureSetting(Setting::Type type) 389 { 390 if (!capturedSetting && parent && parent->capturedSetting) 391 { 392 if (name.length() > 0) 393 { 394 if (!parent->capturedSetting->exists(name)) 395 { 396 capturedSetting = &parent->capturedSetting->add(name, type); 397 } 398 else 399 { 400 capturedSetting = &(*parent->capturedSetting)[name.c_str()]; 401 } 402 } 403 else 404 { 405 if (index < parent->capturedSetting->getLength()) 406 { 407 capturedSetting = &(*parent->capturedSetting)[0]; 408 } 409 else 410 { 411 assert(index == parent->capturedSetting->getLength()); // you requested an index while omitting at least one of its previous siblings 412 capturedSetting = &parent->capturedSetting->add(type); 413 } 414 } 415 416 ConditionalSetCapturedDefaultValue<T>(); 417 } 418 } 419 420 GetPath()421 std::string GetPath() const 422 { 423 if (setting) 424 { 425 return setting->getPath(); 426 } 427 428 std::string path = (name.length() > 0) ? name : "[" + std::to_string(index) + "]"; 429 if (parent) 430 { 431 auto parentPath = parent->GetPath(); 432 return (parentPath.length() > 0) ? (parentPath + ((name.length() == 0) ? "" : ".") + path) : path; 433 } 434 return path; 435 } 436 PropagateAnySettingIsMissing()437 void PropagateAnySettingIsMissing() 438 { 439 anySettingIsMissing = true; 440 if (parent) 441 { 442 parent->PropagateAnySettingIsMissing(); 443 } 444 } 445 PropagateAnyMandatorySettingIsMissing()446 void PropagateAnyMandatorySettingIsMissing() 447 { 448 anyMandatorySettingIsMissing = true; 449 if (parent) 450 { 451 parent->PropagateAnyMandatorySettingIsMissing(); 452 } 453 } 454 455 template<typename T> AlertMandatorySettingMissing()456 void AlertMandatorySettingMissing() 457 { 458 PropagateAnyMandatorySettingIsMissing(); 459 460 err << "Missing '" << GetPath() << "' setting in configuration file." << std::endl; 461 } 462 463 template<typename T> GetUnsetDefaultValue()464 T GetUnsetDefaultValue() const 465 { 466 return (T)0; 467 } 468 469 470 471 template<typename T> GetDefaultValue()472 T GetDefaultValue() const 473 { 474 if (defaultVal.IsSet()) 475 { 476 return (T)defaultVal; 477 } 478 479 return GetUnsetDefaultValue<T>(); 480 } 481 482 template<typename T> GetRequestedType()483 Setting::Type GetRequestedType() const 484 { 485 // TODO @ Hemofektik: Check whether the outcommented line is still needed. static_assert(false) is checked on compile time and, well, asserts :) 486 // static_assert(false, "should never happen, unless you requested an unsupported type"); 487 return Setting::TypeNone; 488 } 489 490 CheckType(const Variant & variant,Setting::Type expectedType)491 void CheckType(const Variant& variant, Setting::Type expectedType) const 492 { 493 if (!variant.IsSet()) return; 494 if(expectedType != variant.GetType()) 495 { 496 assert(false); // fix your code to match the whole chain of this setting to one single type! 497 err << "'" << GetPath() << "' setting limits or default value is of incompatible type." << std::endl; 498 } 499 } 500 501 std::string name; 502 int index; 503 ChainedSetting* parent; 504 Setting* setting; 505 std::ostream& err; 506 Variant defaultVal; 507 Variant minVal; 508 Variant maxVal; 509 bool isSettingMandatory; 510 bool anySettingIsMissing; 511 bool anyMandatorySettingIsMissing; 512 Config* capturedSpecification; 513 Setting* capturedSetting; 514 }; 515 516 template<> 517 inline 518 void ChainedSetting::ConditionalSetCapturedDefaultValue<Setting>() { } 519 520 template<> 521 inline GetUnsetDefaultValue()522 std::string ChainedSetting::GetUnsetDefaultValue() const 523 { 524 return ""; 525 } 526 527 528 template<> inline Setting::Type ChainedSetting::GetRequestedType<int8_t>() const { return Setting::TypeInt; } 529 template<> inline Setting::Type ChainedSetting::GetRequestedType<uint8_t>() const { return Setting::TypeInt; } 530 template<> inline Setting::Type ChainedSetting::GetRequestedType<int16_t>() const { return Setting::TypeInt; } 531 template<> inline Setting::Type ChainedSetting::GetRequestedType<uint16_t>() const { return Setting::TypeInt; } 532 template<> inline Setting::Type ChainedSetting::GetRequestedType<int32_t>() const { return Setting::TypeInt; } 533 template<> inline Setting::Type ChainedSetting::GetRequestedType<uint32_t>() const { return Setting::TypeInt; } 534 template<> inline Setting::Type ChainedSetting::GetRequestedType<int64_t>() const { return Setting::TypeInt64; } 535 template<> inline Setting::Type ChainedSetting::GetRequestedType<uint64_t>() const { return Setting::TypeInt64; } 536 template<> inline Setting::Type ChainedSetting::GetRequestedType<float>() const { return Setting::TypeFloat; } 537 template<> inline Setting::Type ChainedSetting::GetRequestedType<double>() const { return Setting::TypeFloat; } 538 template<> inline Setting::Type ChainedSetting::GetRequestedType<std::string>() const { return Setting::TypeString; } 539 template<> inline Setting::Type ChainedSetting::GetRequestedType<bool>() const { return Setting::TypeBoolean; } 540 } 541 542 #endif