7 #include "gutilities.h"
23 executableName = gutilities::getFileFromPath(argv[0]);
28 addGOptions(user_defined_options);
31 defineSwitch(
"gui",
"use Graphical User Interface");
32 defineOption(
GVariable(
"conf_yaml",
"saved_configuration",
"the yaml filename prefix where all used options are saved"),
33 "The default value appends \"_saved_configuration\" to the executable name.");
39 {
"release", gversion,
"release version number"},
40 {
"release_date", grelease_date,
"release date"},
41 {
"Reference", greference,
"article reference"},
42 {
"Homepage", gweb,
"homepage"},
43 {
"Author", gauthor,
"author"}
45 defineOption(
GVERSION_STRING,
"version information",
version,
"Version information. Not settable by user.");
48 vector <GVariable> verbosity = {
49 {
"ghits", 0,
"ghits verbosity"},
50 {
"gmaterials", 0,
"gmaterials verbosity"},
51 {
"gevent_dispenser", 0,
"event dispenser verbosity"},
52 {
"grun", 0,
"run verbosity"},
53 {
"g4display", 0,
"g4display verbositythe "},
54 {
"gsystem", 0,
"gsystem verbositythe "},
55 {
"gfield", 0,
"general fields verbosity"},
56 {
"g4system", 0,
"g4system verbosity"},
57 {
"gparticle", 0,
"gparticle verbosity"},
58 {
"gphysics", 0,
"gphysics verbosity"},
59 {
"gstreamer_ev", 0,
"gstreamer event verbosity"},
60 {
"gstreamer_fr", 0,
"gstreamer frame verbosity"},
61 {
"gsensitivity", 0,
"sensitivity verbosity"},
62 {
"general", 0,
"general verbosity"},
63 {
"event", 0,
"event verbosity"},
66 string help =
"Levels: \n \n";
68 help +=
"1: summaryt\n";
69 help +=
"2: details\n";
70 help +=
"3: everything\n \n";
71 help +=
"Example: -verbosity=\"[{gsystem: 3}, {grun: 1}]\" \n";
72 defineOption(
"verbosity",
"Sets the log verbosity for various categories", verbosity, help);
76 for (
int i = 1; i < argc; i++) {
77 if (strcmp(argv[i],
"-h") == 0 || strcmp(argv[i],
"--h") == 0 || strcmp(argv[i],
"-help") == 0 || strcmp(argv[i],
"--help") == 0) {
79 }
else if (strcmp(argv[i],
"-hweb") == 0) {
81 }
else if (strcmp(argv[i],
"-v") == 0 || strcmp(argv[i],
"--v") == 0 || strcmp(argv[i],
"-version") == 0 || strcmp(argv[i],
"--version") == 0) {
84 }
else if (strcmp(argv[i],
"help") == 0) {
85 printOptionOrSwitchHelp(argv[i + 1]);
91 yaml_files = findYamls(argc, argv);
94 for (
auto &yaml_file: yaml_files) {
95 cout <<
" Parsing " << yaml_file << endl;
96 setOptionsValuesFromYamlFile(yaml_file);
102 for (
int i = 1; i < argc; i++) {
103 string candidate = string(argv[i]);
105 if (candidate ==
"")
continue;
107 if (find(yaml_files.begin(), yaml_files.end(), candidate) == yaml_files.end()) {
112 if (candidate[0] ==
'-') {
115 string possible_switch = candidate.substr(1, candidate.size() - 1);
118 if (switches.find(possible_switch) != switches.end()) {
119 switches[possible_switch].turnOn();
123 if (possible_switch.find(
"=") != string::npos) {
125 string possible_option = possible_switch.substr(0, candidate.find(
"=") - 1);
128 if (doesOptionExist(possible_option)) {
129 string possible_yaml_node = possible_switch.substr(candidate.find(
"="), candidate.size() - 1);
130 setOptionValuesFromCommandLineArgument(possible_option, possible_yaml_node);
133 cerr << FATALERRORL <<
"the " << YELLOWHHL << candidate << RSTHHR <<
" option is not known to this system. " << endl;
134 cerr << endl <<
" " << executableName <<
" -h for help." << endl << endl;
140 cerr << FATALERRORL << YELLOWHHL << candidate << RSTHHR <<
" is not a valid command line option or switch. " << endl;
141 cerr <<
" Note: switches start with a dash; options start with a dash, and are followed by an equal sign and their desired value."
143 cerr << endl <<
" Usage: " << endl << endl;
144 cerr <<
" " << executableName <<
" [options] [yaml files]" << endl;
145 cerr <<
" " << executableName <<
" -h for help." << endl << endl;
151 cerr << FATALERRORL <<
"the " << YELLOWHHL << candidate << RSTHHR <<
" command line is not known to this system. " << endl;
152 cerr << endl <<
" Usage: " << endl << endl;
153 cerr <<
" " << executableName <<
" [options] [yaml files]" << endl;
154 cerr <<
" " << executableName <<
" -h for help." << endl << endl;
164 string yamlConf_filename = executableName +
"." + getScalarString(
"conf_yaml") +
".yaml";
165 cout <<
" Saving options to " << yamlConf_filename << endl << endl;
166 yamlConf =
new std::ofstream(yamlConf_filename);
173 if (switches.find(name) == switches.end()) {
174 switches[name] =
GSwitch(description);
176 std::cerr << FATALERRORL <<
"the " << YELLOWHHL << name << RSTHHR <<
" switch is already present." << std::endl;
184 if (doesOptionExist(gvar.
name)) {
185 std::cerr << FATALERRORL <<
"the " << YELLOWHHL << gvar.
name << RSTHHR <<
" option is already present." << std::endl;
193 void GOptions::defineOption(
const std::string &name,
const std::string &description,
const std::vector <GVariable> &g_vars,
const std::string &help) {
195 if (doesOptionExist(name)) {
196 std::cerr << FATALERRORL <<
"the " << YELLOWHHL << name << RSTHHR <<
" option is already present." << std::endl;
204 auto it = getOptionIterator(tag);
208 cerr << FATALERRORL <<
"The option " << YELLOWHHL << tag << RSTHHR <<
" was not found." << endl;
212 return it->value.begin()->second.as<
int>();
216 auto it = getOptionIterator(tag);
220 cerr << FATALERRORL <<
"The option " << YELLOWHHL << tag << RSTHHR <<
" was not found." << endl;
224 return it->value.begin()->second.as<
float>();
228 auto it = getOptionIterator(tag);
232 cerr << FATALERRORL <<
"The option " << YELLOWHHL << tag << RSTHHR <<
" was not found." << endl;
236 return it->value.begin()->second.as<
double>();
240 auto it = getOptionIterator(tag);
244 cerr << FATALERRORL <<
"The option " << YELLOWHHL << tag << RSTHHR <<
" was not found." << endl;
248 return it->value.begin()->second.as<
string>();
251 void GOptions::printOptionOrSwitchHelp(
const std::string& tag)
const {
253 auto switchIt = switches.find(tag);
254 if (switchIt != switches.end()) {
255 cout << KGRN <<
"-" << tag << RST <<
": " << switchIt->second.getDescription() << endl << endl;
256 cout << TPOINTITEM <<
"Default value is " << (switchIt->second.getStatus() ?
"on" :
"off") << endl << endl;
261 for (
const auto& goption :
goptions) {
262 if (goption.name == tag) {
263 goption.printHelp(
true);
269 cerr << FATALERRORL <<
"The " << YELLOWHHL << tag << RSTHHR <<
" option is not known to this system." << endl;
275 vector <string> GOptions::findYamls(
int argc,
char *argv[]) {
276 vector <string> yaml_files;
278 for (
int i = 1; i < argc; i++) {
279 string arg = argv[i];
281 size_t pos = arg.find(
".yaml");
282 if (pos != string::npos) yaml_files.push_back(arg);
283 pos = arg.find(
".yml");
284 if (pos != string::npos) yaml_files.push_back(arg);
292 bool GOptions::doesOptionExist(
const std::string& tag)
const {
293 for (
const auto& goption :
goptions) {
295 if (goption.name == tag) {
302 void GOptions::setOptionsValuesFromYamlFile(
const std::string& yaml) {
306 config = YAML::LoadFile(yaml);
308 }
catch (YAML::ParserException &e) {
309 cerr << FATALERRORL <<
"Error parsing " << YELLOWHHL << yaml << RSTHHR <<
" yaml file." << endl;
310 cerr << e.what() << endl;
311 cerr <<
"Try validating the yaml file with an online yaml validator, for example: https://www.yamllint.com" << endl;
315 for (YAML::const_iterator it = config.begin(); it != config.end(); ++it) {
317 string option_name = it->first.as<std::string>();
318 auto option_it = getOptionIterator(option_name);
321 if (switches.find(option_name) == switches.end()) {
322 cerr << FATALERRORL <<
"The option or switch " << YELLOWHHL << option_name << RSTHHR <<
" is not known to this system." << endl;
325 switches[option_name].turnOn();
328 YAML::NodeType::value type = it->second.Type();
331 case YAML::NodeType::Scalar:
332 option_it->set_scalar_value(it->second.as<std::string>());
334 case YAML::NodeType::Sequence:
335 option_it->set_value(it->second);
337 case YAML::NodeType::Map:
338 option_it->set_value(it->second);
349 void GOptions::setOptionValuesFromCommandLineArgument(
const std::string& optionName,
const std::string& possibleYamlNode) {
350 YAML::Node node = YAML::Load(possibleYamlNode);
352 auto option_it = getOptionIterator(optionName);
354 if (node.Type() == YAML::NodeType::Scalar) {
355 option_it->set_scalar_value(possibleYamlNode);
357 option_it->set_value(node);
362 std::vector<GOption>::iterator GOptions::getOptionIterator(
const std::string& name) {
364 [&name](
GOption& option) { return option.name == name; });
368 std::vector<GOption>::const_iterator GOptions::getOptionIterator(
const std::string& name)
const {
370 [&name](
const GOption& option) { return option.name == name; });
376 auto it = switches.find(tag);
379 if (it != switches.end()) {
380 return it->second.getStatus();
382 std::cerr << FATALERRORL <<
"The switch " << YELLOWHHL << tag << RSTHHR <<
" was not found." << std::endl;
390 auto sequence_node = getOptionNode(option_name);
392 for (
auto seq_item: sequence_node) {
393 for (
auto map_item = seq_item.begin(); map_item != seq_item.end(); ++map_item) {
394 if (map_item->first.as<
string>() == map_key) {
395 return map_item->second;
401 cerr << FATALERRORL <<
"The key " << YELLOWHHL << map_key << RSTHHR <<
" was not found in " << YELLOWHHL << option_name << RSTHHR << endl;
404 return sequence_node;
409 if (node[variable_name]) {
410 return node[variable_name].as<T>();
412 return default_value;
416 template int GOptions::get_variable_in_option<int>(
const YAML::Node &node,
const std::string &variable_name,
const int &default_value);
418 template float GOptions::get_variable_in_option<float>(
const YAML::Node &node,
const std::string &variable_name,
const float &default_value);
420 template double GOptions::get_variable_in_option<double>(
const YAML::Node &node,
const std::string &variable_name,
const double &default_value);
422 template string GOptions::get_variable_in_option<string>(
const YAML::Node &node,
const std::string &variable_name,
const string &default_value);
424 template bool GOptions::get_variable_in_option<bool>(
const YAML::Node &node,
const std::string &variable_name,
const bool &default_value);
427 auto verbosity_node = getOptionNode(
"verbosity");
429 for (
auto v: verbosity_node) {
430 if (v.begin()->first.as<
string>() == tag) {
431 return v.begin()->second.as<
int>();
440 void GOptions::printHelp()
const {
445 cout << KGRN << KBOLD <<
" " << executableName << RST <<
" [options] [yaml files]" << endl << endl;
446 cout <<
" Switches: " << endl << endl;
449 for (
auto &s: switches) {
450 string help =
"-" + s.first + RST +
" ";
451 cout << KGRN <<
" " << left;
452 cout.width(fill_width);
454 cout <<
": " << s.second.getDescription() << endl;
458 cout <<
" Options: " << endl << endl;
461 option.printHelp(
false);
466 cout << endl <<
" Help / Search / Introspection: " << endl << endl;
468 vector <string> helps = {
469 string(
"-h, --h, -help, --help") + RST,
470 string(
"print this help and exit"),
471 string(
"-hweb") + RST,
472 string(
"print this help in web format and exit"),
473 string(
"-v, --v, -version, --version") + RST,
474 string(
"print the version and exit\n"), string(
"help <value>") + RST,
475 string(
"print detailed help for option <value> and exit"),
476 string(
"search <value>") + RST,
477 string(
"list all options containing <value> in the description and exit\n")
479 unsigned half_help = helps.size() / 2;
480 for (
unsigned i = 0; i < half_help; i++) {
481 cout << KGRN <<
" " << left;
482 cout.width(fill_width);
483 cout << helps[i * 2] <<
": " << helps[i * 2 + 1] << endl;
488 cout <<
" Note: command line options overwrite yaml file(s). " << endl << endl;
495 void GOptions::printWebHelp()
const {
502 void GOptions::saveOptions()
const {
504 for (
auto &s: switches) {
505 string status =
"false";
506 if (s.second.getStatus()) status =
"true";
507 *yamlConf << s.first +
": " + status <<
"," << endl;
510 for (
const auto& option:
goptions) {
511 option.saveOption(yamlConf);
518 void GOptions::print_version() {
519 string asterisks =
"**************************************************************";
520 cout << endl << asterisks << endl;
521 cout <<
" " << KGRN << KBOLD << executableName << RST <<
" version: " << KGRN << gversion << RST << endl;
522 cout <<
" Released on: " << KGRN << grelease_date << RST << endl;
523 cout <<
" Reference: " << KGRN << greference << RST << endl;
524 cout <<
" Homepage: " << KGRN << gweb << RST << endl;
525 cout <<
" Author: " << KGRN << gauthor << RST << endl << endl;
526 cout << asterisks << endl << endl;
Represents a configurable option with a name, value, description, and help text.
The GOptions class manages command-line options and switches.
std::string getScalarString(const std::string &tag) const
Retrieves the value of a scalar string option.
bool getSwitch(const std::string &tag) const
Retrieves the status of a switch.
T get_variable_in_option(const YAML::Node &node, const string &variable_name, const T &default_value)
Retrieves a variable from a YAML::Node.
void defineSwitch(const std::string &name, const std::string &description)
Defines and adds a command-line switch to the map of switches.
float getScalarFloat(const std::string &tag) const
Retrieves the value of a scalar float option.
GOptions()
Default constructor.
void defineOption(const GVariable &gvar, const std::string &help)
Defines and adds a scalar option to the map of options.
double getScalarDouble(const std::string &tag) const
Retrieves the value of a scalar double option.
void addGOptions(const GOptions &goptions_to_add)
Adds a set of GOptions to the current options.
int getScalarInt(const std::string &tag) const
Retrieves the value of a scalar integer option.
int getVerbosityFor(const std::string &tag) const
Retrieves the verbosity level for a given tag.
YAML::Node getOptionMapInNode(string option_name, string map_key)
Retrieves a map option within a YAML::Node.
Represents a switch with a description and a status.
#define EC__NOOPTIONFOUND
#define EC__YAML_PARSING_ERROR
#define EC__DEFINED_SWITCHALREADYPRESENT
#define EC__DEFINED_OPTION_ALREADY_PRESENT
GOptions & operator+=(GOptions &gopts, const GOptions &goptions_to_add)
Encapsulates a variable with a name, value, and description.
string name
The name of the variable.