51 GVariable(
"conf_yaml",
"saved_configuration",
"the prefix for filename that store the used options"),
52 "The default value appends \"_saved_configuration\" to the executable name.");
56 "Timeout in milliseconds for the code tests that have GUI. ");
61 vector<GVariable> version = {
62 {
"release", gversion,
"release version number"},
63 {
"release_date", grelease_date,
"release date"},
64 {
"Reference", greference,
"article reference"},
65 {
"Homepage", gweb,
"homepage"},
66 {
"Author", gauthor,
"author"}
71 string help =
"Levels: \n \n";
72 help +=
" - 0: (default) = shush\n";
73 help +=
" - 1: log detailed information\n";
74 help +=
" - 2: log extra detailed information\n \n";
75 help +=
"Example: -verbosity.general=1 \n \n";
76 help +=
"This option can be repeated.\n \n";
80 help =
"Debug information Types: \n \n";
81 help +=
" - false: (default): do not print debug information\n";
82 help +=
" - true: print debug information\n\n";
83 help +=
"Example: -debug.general=true \n \n";
84 help +=
"This option can be repeated.\n \n";
89 for (
int i = 1; i < argc; i++) {
90 if (strcmp(argv[i],
"-h") == 0 || strcmp(argv[i],
"--h") == 0 ||
91 strcmp(argv[i],
"-help") == 0 || strcmp(argv[i],
"--help") == 0) {
94 else if (strcmp(argv[i],
"-hweb") == 0) {
97 else if (strcmp(argv[i],
"-v") == 0 || strcmp(argv[i],
"--v") == 0 ||
98 strcmp(argv[i],
"-version") == 0 || strcmp(argv[i],
"--version") == 0) {
102 else if (strcmp(argv[i],
"help") == 0) {
103 printOptionOrSwitchHelp(argv[i + 1]);
110 yaml_files = findYamls(argc, argv);
111 for (
auto& yaml_file : yaml_files) {
112 cout <<
" Parsing " << yaml_file << endl;
113 setOptionsValuesFromYamlFile(yaml_file);
117 for (
int i = 1; i < argc; i++) {
118 string candidate = argv[i];
119 if (candidate.empty())
continue;
122 if (find(yaml_files.begin(), yaml_files.end(), candidate) != yaml_files.end())
continue;
124 if (candidate[0] ==
'-') {
125 string argStr = candidate.substr(1);
126 size_t eqPos = argStr.find(
'=');
128 if (eqPos != string::npos) {
129 string keyPart = argStr.substr(0, eqPos);
130 string valuePart = argStr.substr(eqPos + 1);
133 if (!valuePart.empty() && valuePart.front() ==
'"' && valuePart.back() ==
'"') {
134 valuePart = valuePart.substr(1, valuePart.length() - 2);
138 size_t dotPos = keyPart.find(
'.');
139 if (dotPos != string::npos) {
140 string mainOption = keyPart.substr(0, dotPos);
141 string subOption = keyPart.substr(dotPos + 1);
144 auto it = getOptionIterator(mainOption);
145 it->set_sub_option_value(subOption, valuePart);
148 cerr <<
"The option " << mainOption <<
" is not known to this system." << endl;
155 setOptionValuesFromCommandLineArgument(keyPart, valuePart);
158 cerr <<
"The option " << keyPart <<
" is not known to this system." << endl;
165 const string& possibleSwitch = argStr;
166 if (switches.find(possibleSwitch) != switches.end()) {
167 switches[possibleSwitch].turnOn();
170 cerr <<
"The switch " << possibleSwitch <<
" is not known to this system." << endl;
176 cerr <<
"The command-line argument \"" << candidate <<
"\" is not valid." << endl;
185 string yamlConf_filename = executableName +
"." +
getScalarString(
"conf_yaml") +
".yaml";
186 cout <<
" Saving options to " << yamlConf_filename << endl << endl;
187 yamlConf =
new std::ofstream(yamlConf_filename);
193 if (switches.find(name) == switches.end()) {
194 switches[name] =
GSwitch(description);
198 <<
" switch is already present." << std::endl;
207 <<
" option is already present." << std::endl;
217 const std::vector<GVariable>& gvars,
218 const std::string& help) {
221 <<
" option is already present." << std::endl;
225 goptions.emplace_back(name, description, gvars, help);
231 auto it = getOptionIterator(tag);
234 <<
" was not found." << endl;
237 return it->value.begin()->second.as<
int>();
242 auto it = getOptionIterator(tag);
245 <<
" was not found." << endl;
248 return it->value.begin()->second.as<
double>();
253 auto it = getOptionIterator(tag);
256 <<
" was not found." << std::endl;
259 const YAML::Node node = it->value.begin()->second;
260 if (node.IsNull())
return "NULL";
261 return node.as<std::string>();
266void GOptions::printOptionOrSwitchHelp(
const std::string& tag)
const {
267 auto switchIt = switches.find(tag);
268 if (switchIt != switches.end()) {
269 cout <<
KGRN <<
"-" << tag <<
RST <<
": " << switchIt->second.getDescription() << endl << endl;
270 cout <<
TPOINTITEM <<
"Default value is " << (switchIt->second.getStatus() ?
"on" :
"off") << endl << endl;
273 for (
const auto& goption :
goptions) {
274 if (goption.name == tag) {
275 goption.printHelp(
true);
280 <<
" option is not known to this system." << endl;
285vector<string> GOptions::findYamls(
int argc,
char* argv[]) {
286 vector<string> yaml_files;
287 for (
int i = 1; i < argc; i++) {
288 string arg = argv[i];
289 size_t pos = arg.find(
".yaml");
290 if (pos != string::npos) yaml_files.push_back(arg);
291 pos = arg.find(
".yml");
292 if (pos != string::npos) yaml_files.push_back(arg);
302 [&tag](
const auto& option) {
303 return option.name == tag;
308void GOptions::setOptionsValuesFromYamlFile(
const std::string& yaml) {
311 config = YAML::LoadFile(yaml);
313 catch (YAML::ParserException& e) {
315 <<
" yaml file." << endl;
316 cerr << e.what() << endl;
317 cerr <<
"Try validating the yaml file with an online yaml validator, e.g., https://www.yamllint.com" << endl;
321 for (
auto it = config.begin(); it != config.end(); ++it) {
322 auto option_name = it->first.as<std::string>();
323 auto option_it = getOptionIterator(option_name);
327 if (switches.find(option_name) == switches.end()) {
329 <<
" is not known to this system." << endl;
333 switches[option_name].turnOn();
337 YAML::NodeType::value type = it->second.Type();
339 case YAML::NodeType::Scalar:
340 option_it->set_scalar_value(it->second.as<std::string>());
342 case YAML::NodeType::Sequence:
343 option_it->set_value(it->second);
345 case YAML::NodeType::Map:
346 option_it->set_value(it->second);
356void GOptions::setOptionValuesFromCommandLineArgument(
const std::string& optionName,
357 const std::string& possibleYamlNode) {
358 YAML::Node node = YAML::Load(possibleYamlNode);
359 auto option_it = getOptionIterator(optionName);
361 if (node.Type() == YAML::NodeType::Scalar) {
362 option_it->set_scalar_value(possibleYamlNode);
365 option_it->set_value(node);
370std::vector<GOption>::iterator GOptions::getOptionIterator(
const std::string& name) {
372 [&name](
GOption& option) { return option.name == name; });
376std::vector<GOption>::const_iterator GOptions::getOptionIterator(
const std::string& name)
const {
378 [&name](
const GOption& option) { return option.name == name; });
383 auto it = switches.find(tag);
384 if (it != switches.end()) {
385 return it->second.getStatus();
389 <<
" was not found." << std::endl;
398 for (
auto seq_item : sequence_node) {
399 for (
auto map_item = seq_item.begin(); map_item != seq_item.end(); ++map_item) {
400 if (map_item->first.as<
string>() == map_key) {
401 return map_item->second;
414 if (node[variable_name]) {
415 return node[variable_name].as<T>();
417 return default_value;
422 const int& default_value);
424 const double& default_value);
426 const string& default_value);
428 const bool& default_value);
434 if (
v.begin()->first.as<
string>() ==
tag) {
435 return v.begin()->second.as<
int>();
440 std::cerr <<
KRED <<
" Invalid verbosity or debug requested: " <<
tag <<
RST << std::endl;
448 if (
d.begin()->first.as<
string>() ==
tag) {
449 YAML::Node
valNode =
d.begin()->second;
452 if (
s ==
"true")
return 1;
453 if (
s ==
"false")
return 0;
458 catch (
const YAML::BadConversion&) {
459 std::cerr <<
"Invalid debug value for " <<
tag << std::endl;
465 std::cerr <<
KRED <<
" Invalid verbosity or debug requested: " <<
tag <<
RST << std::endl;
470void GOptions::printHelp()
const {
473 cout <<
KGRN <<
KBOLD <<
" " << executableName <<
RST <<
" [options] [yaml files]" << endl << endl;
474 cout <<
" Switches: " << endl << endl;
475 for (
auto&
s : switches) {
476 string help =
"-" +
s.first +
RST +
" ";
477 cout <<
KGRN <<
" " << left;
480 cout <<
": " <<
s.second.getDescription() << endl;
483 cout <<
" Options: " << endl << endl;
488 cout << endl <<
" Help / Search / Introspection: " << endl << endl;
489 vector<string>
helps = {
490 string(
"-h, --h, -help, --help") +
RST,
491 string(
"print this help and exit"),
492 string(
"-hweb") +
RST,
493 string(
"print this help in web format and exit"),
494 string(
"-v, --v, -version, --version") +
RST,
495 string(
"print the version and exit\n"),
496 string(
"help <value>") +
RST,
497 string(
"print detailed help for option <value> and exit"),
498 string(
"search <value>") +
RST,
499 string(
"list all options containing <value> in the description and exit\n")
503 cout <<
KGRN <<
" " << left;
505 cout <<
helps[
i * 2] <<
": " <<
helps[
i * 2 + 1] << endl;
508 cout <<
" Note: command line options overwrite YAML file(s)." << endl << endl;
513void GOptions::printWebHelp()
const {
518void GOptions::saveOptions()
const {
519 for (
auto&
s : switches) {
520 string status =
s.second.getStatus() ?
"true" :
"false";
521 *yamlConf <<
s.first +
": " + status <<
"," << endl;
524 option.saveOption(yamlConf);
530void GOptions::print_version() {
531 string asterisks =
"*******************************************************************";
534 cout <<
" Called from: " <<
KGRN << executableCallingDir <<
RST << endl;
535 cout <<
" Install: " <<
KGRN << installDir <<
"/bin" <<
RST << endl;
538 cout <<
" GEMC Homepage: " <<
KGRN <<
gweb <<
RST << endl;
Stores one configuration option (scalar or structured), including schema defaults and current value.
Parses, stores, and exposes command-line options and YAML configuration values.
bool getSwitch(const std::string &tag) const
Retrieves the status of a switch.
YAML::Node getOptionNode(const std::string &tag) const
Retrieves the YAML node for the specified option.
void defineSwitch(const std::string &name, const std::string &description)
Defines and adds a command-line switch.
std::string getScalarString(const std::string &tag) const
Retrieves the value of a scalar string option.
GOptions()
Default constructor.
void defineOption(const GVariable &gvar, const std::string &help)
Defines and adds a scalar option.
YAML::Node getOptionMapInNode(const std::string &option_name, const std::string &map_key) const
Retrieves a map entry value from a structured option stored as a sequence of maps.
T get_variable_in_option(const YAML::Node &node, const std::string &variable_name, const T &default_value)
Retrieves a typed variable from a YAML node within an option.
double getScalarDouble(const std::string &tag) const
Retrieves the value of a scalar double option.
bool doesOptionExist(const std::string &tag) const
Checks if an option exists.
int getScalarInt(const std::string &tag) const
Retrieves the value of a scalar integer option.
void addGOptions(const GOptions &src)
Merges options and switches from another GOptions : into this one.
int getDebugFor(const std::string &tag) const
Retrieves the debug level for the specified tag.
int getVerbosityFor(const std::string &tag) const
Retrieves the verbosity level for the specified tag.
std::vector< GVariable > option_verbosity_names
Schema entries used to define the verbosity and debug structured options.
Represents a boolean command-line switch with a description and a status.
Conventions, constants, and error codes for the GOptions : / GOption : subsystem.
#define EC__BAD_CONVERSION
YAML value could not be converted to requested type.
#define EC__NOOPTIONFOUND
Option/switch/key not found, or invalid command-line token.
#define EC__YAML_PARSING_ERROR
YAML file failed to parse (syntax error or parser failure).
#define HELPFILLSPACE
Padding used when printing option/switch help.
#define EC__DEFINED_SWITCHALREADYPRESENT
Attempted to define a switch name more than once.
#define EC__DEFINED_OPTION_ALREADY_PRESENT
Attempted to define an option name more than once.
#define GVERSION_STRING
Reserved option tag used to store version information.
GOptions & operator+=(GOptions &gopts, const GOptions &goptions_to_add)
Overloaded operator to add options and switches from one GOptions : to another.
Public interface for GOptions : the YAML + command-line configuration manager.
std::filesystem::path gemc_root()
string getDirFromPath(const std::string &path)
string getFileFromPath(const std::string &path)
Describes a schema entry: key name, default value, and user-facing description.
std::string name
Variable name (option name for scalar options, schema key name for structured options).