SERiF 0.0.1a
3+1D Stellar Structure and Evolution
Loading...
Searching...
No Matches
config.h
Go to the documentation of this file.
1/* ***********************************************************************
2//
3// Copyright (C) 2025 -- The 4D-STAR Collaboration
4// File Author: Emily Boudreaux
5// Last Modified: March 26, 2025
6//
7// 4DSSE is free software; you can use it and/or modify
8// it under the terms and restrictions the GNU General Library Public
9// License version 3 (GPLv3) as published by the Free Software Foundation.
10//
11// 4DSSE is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14// See the GNU Library General Public License for more details.
15//
16// You should have received a copy of the GNU Library General Public License
17// along with this software; if not, write to the Free Software
18// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19//
20// *********************************************************************** */
21#pragma once
22
23#include <string>
24#include <iostream>
25#include <sstream>
26#include <vector>
27#include <map>
28#include <algorithm>
29#include <stdexcept>
30
31// Required for YAML parsing
32#include "yaml-cpp/yaml.h"
33
34// -- Forward Def of Resource manager to let it act as a friend of Config --
35namespace serif::resource { class ResourceManager; }
36
37class configTestPrivateAccessor; // Forward declaration for test utility
38
39namespace serif::config {
40
45 class Config {
46 private:
50 Config();
51
55 ~Config();
56
57 YAML::Node yamlRoot;
58 std::string configFilePath;
59 bool debug = false;
60 bool loaded = false;
61
62 std::map<std::string, YAML::Node> configMap;
63 std::vector<std::string> unknownKeys;
64
72 template <typename T>
73 T getFromCache(const std::string &key, T defaultValue) {
74 if (configMap.find(key) != configMap.end()) {
75 try {
76 return configMap[key].as<T>();
77 } catch (const YAML::Exception& e) {
78 return defaultValue;
79 }
80 }
81 return defaultValue;
82 }
83
84
90 bool isKeyInCache(const std::string &key);
91
97 void addToCache(const std::string &key, const YAML::Node &node);
98
103 void registerUnknownKey(const std::string &key);
104
105 bool m_loaded = false;
106
107 // Only friends can access get without a default value
108 template <typename T>
109 T get(const std::string &key) {
110 if (!m_loaded) {
111 throw std::runtime_error("Error! Config file not loaded");
112 }
113 if (has(key)) {
114 return getFromCache<T>(key, T());
115 } else {
116 throw std::runtime_error("Error! Key not found in config file");
117 }
118 }
119
120 public:
125 static Config& getInstance();
126
127 Config (const Config&) = delete;
128 Config& operator= (const Config&) = delete;
129 Config (Config&&) = delete;
130 Config& operator= (Config&&) = delete;
131
132 void setDebug(bool debug) { this->debug = debug; }
133
139 bool loadConfig(const std::string& configFilePath);
140
145 std::string getInputTable() const;
146
160 template <typename T>
161 T get(const std::string &key, T defaultValue) {
162 if (!m_loaded) {
163 // ONLY THROW ERROR IF HARSH OR WARN CONFIGURATION
164#if defined(CONFIG_HARSH)
165 throw std::runtime_error("Error! Config file not loaded. To disable this error, recompile with CONFIG_HARSH=0");
166#elif defined(CONFIG_WARN)
167 std::cerr << "Warning! Config file not loaded. This instance of 4DSSE was compiled with CONFIG_WARN so the code will continue using only default values" << std::endl;
168#endif
169 }
170 // --- Check if the key has already been checked for existence
171 if (std::find(unknownKeys.begin(), unknownKeys.end(), key) != unknownKeys.end()) {
172 return defaultValue; // If the key has already been added to the unknown cache do not traverse the YAML tree or hit the cache
173 }
174
175 // --- Check if the key is already in the cache (avoid traversing YAML nodes)
176 if (isKeyInCache(key)) {
177 return getFromCache<T>(key, defaultValue);
178 }
179 // --- If the key is not in the cache, check the YAML file
180 else {
181 YAML::Node node = YAML::Clone(yamlRoot);
182 std::istringstream keyStream(key);
183 std::string subKey;
184 while (std::getline(keyStream, subKey, ':')) {
185 if (!node[subKey]) {
186 // Key does not exist
187 registerUnknownKey(key);
188 return defaultValue;
189 }
190 node = node[subKey]; // go deeper
191 }
192
193 try {
194 // Key exists and is of the requested type
195 addToCache(key, node);
196 return node.as<T>();
197 } catch (const YAML::Exception& e) {
198 // Key is not of the requested type
199 registerUnknownKey(key);
200 return defaultValue; // return default value if the key does not exist
201 }
202 }
203 }
204
210 bool has(const std::string &key);
211
216 std::vector<std::string> keys() const;
217
224 friend std::ostream& operator<<(std::ostream& os, const Config& config) {
225 if (!config.m_loaded) {
226 os << "Config file not loaded" << std::endl;
227 return os;
228 }
229 if (!config.debug) {
230 os << "Config file: " << config.configFilePath << std::endl;
231 } else{
232 // Print entire YAML file from root
233 os << "Config file: " << config.configFilePath << std::endl;
234 os << config.yamlRoot << std::endl;
235 }
236 return os;
237 }
238
239 // Setup gTest class as a friend
240 friend class ::configTestPrivateAccessor; // Friend declaration for global test accessor
241 // -- Resource Manager is a friend of config so it can create a seperate instance
242 friend class serif::resource::ResourceManager; // Adjusted friend declaration
243 };
244
245}
std::ostream & operator<<(std::ostream &os, const GlobalComposition &comp)