PYTHIA  8.313
InputParser.h
1 // InputParser.h is a part of the PYTHIA event generator.
2 // Copyright (C) 2025 Christian Bierlich, Torbjorn Sjostrand.
3 // PYTHIA is licenced under the GNU GPL v2 or later, see COPYING for details.
4 // Please respect the MCnet Guidelines, see GUIDELINES for details.
5 
6 #ifndef Pythia8_InputParser_H
7 #define Pythia8_InputParser_H
8 
9 // Includes.
10 #include "Pythia8/PythiaStdlib.h"
11 
12 namespace Pythia8 {
13 
14 //==========================================================================
15 
16 // Convenience class for parsing C++ command-line arguments on the form
17 // ./mainXX -a A -b B etc.
18 //
19 // Usage: (1) Create an InputParser object in your main program. (2)
20 // Specify the command-line options that should be available, including
21 // default values, using add. (3) Call the parse(int& argc, char** argv)
22 // method, with the two aruments forwarded from argc and argv from your
23 // main. (4) Extract given command-line values of type T, using get<T>.
24 
25 class InputParser {
26 public:
27 
28  // Constructor.
29  // usage: Text to print when describing usage with help.
30  // examples: Vector of examples to print with help.
31  // extra: Extra text to print with help.
32  // stream: Optional pointer to stream to print messages to.
33  // optName: Option name for the help flag.
34  // aliases: Aliases for the help flag.
35  InputParser(string usage = "", vector<string> examples = {},
36  string extra = "", ostream* stream = &cout,
37  string optName = "h", set<string> aliases = {"-help","H"}) :
38  usageText(usage), examplesText(examples), extraText(extra),
39  streamPtr(stream), helpFlag(optName) {
40  add(optName, "false", "Show this help message and exit.", aliases);
41  }
42 
43  // Add an optional command line option to the parser. Do this before parsing.
44  // Parameters:
45  // optName: The name of the option.
46  // defString: The default value for the option.
47  // helpText: The help text describing the option (optional).
48  // aliases: A set of aliases for the option (optional).
49  bool add(const string& optName, const string& defString,
50  const string& helpText = "", set<string> aliases = {}) {
51  // Check for name conflicts with existing options or aliases.
52  if (options.find(optName) != options.end() ||
53  aliasMap.find(optName) != aliasMap.end()) {
54  print("Name conflict for '" + optName + "'.\n");
55  return false;
56  }
57  // Create an OptionInfo object and add it to the options map.
58  OptionInfo optInfo(optName, defString, helpText, aliases);
59  options[optName] = optInfo;
60  // Add aliases to the alias map.
61  for (const string& alias : aliases) {
62  if (options.find(alias) != options.end() ||
63  aliasMap.find(alias) != aliasMap.end()) {
64  print("Name conflict for alias '" + alias + "'.\n");
65  return false;
66  }
67  aliasMap[alias] = optName;
68  }
69  return true;
70  }
71 
72  // Add required command line option to the parser. Do this before parsing.
73  bool require(const string& optName, const string& helpText = "",
74  set<string> aliases = {}) {
75  if (!add(optName, "", helpText, aliases)) return false;
76  options[optName].required = true;
77  return true;
78  }
79 
80  // Initialize the parser. This includes parsing, printing help if
81  // requested, and checking the required options.
82  enum Status {Valid = -1, Invalid = EXIT_FAILURE, Help = EXIT_SUCCESS};
83  Status init(int& argc, char** argv) {
84 
85  // Parse the arguments.
86  bool valid = parse(argc, argv);
87  if (!valid) return Status::Invalid;
88 
89  // Print the help if requested.
90  if (options.find(helpFlag) != options.end() && get<bool>(helpFlag)) {
91  string out;
92  if (usageText != "") out = "Usage: " + usageText;
93  if (examplesText.size() > 0) {
94  out += "\nExample" + string(examplesText.size() > 1 ? "s:" : ":");
95  for (const string& text: examplesText) out += "\n\t" + text;
96  }
97  if (options.size() > 0) {
98  out += "\nOption" + string(options.size() > 1 ? "s:\n" : ":\n")
99  + help() + "\n";
100  }
101  print(out + extraText);
102  return Status::Help;
103  }
104 
105  // Check the required options are set.
106  for (map<string, OptionInfo>::iterator opt = options.begin();
107  opt != options.end(); opt++) {
108  if (opt->second.required && !opt->second.provided) {
109  print("Option '-" + opt->first + "' is required but was not set.\n"
110  "\t -" + opt->first + " " + opt->second.helpText + "\n");
111  valid = false;
112  }
113  }
114  return valid ? Status::Valid : Status::Invalid;
115 
116  }
117 
118  // Method to parse command line arguments.
119  // Returns true if parsing was successful, false otherwise.
120  // Print error messages to stream.
121  // The hflag option specifies the help option.
122  bool parse(int& argc, char** argv) {
123  for (int i = 1; i < argc; ++i) {
124  string arg = argv[i];
125  // Check if the argument is an option (starts with '-').
126  if (arg[0] == '-') {
127  string optName = arg.substr(1);
128  // Check for aliases and get the actual option name.
129  if (aliasMap.find(optName) != aliasMap.end())
130  optName = aliasMap[optName];
131  // Check if the option is defined.
132  if (options.find(optName) != options.end()) {
133  // If the next argument is not an option, set the value for
134  // this option.
135  if (i + 1 < argc && argv[i + 1][0] != '-') {
136  options[optName].stringValues.push_back(argv[++i]);
137  options[optName].provided = true;
138  } else {
139  // Treat as boolean flag and flip its value.
140  const string& def = options[optName].defaultValue;
141  if (def == "false" || def == "False" || def == "0"
142  || def == "off") {
143  options[optName].stringValues.push_back("true");
144  options[optName].provided = true;
145  } else if (def == "true" || def == "True" || def == "1"
146  || def == "on") {
147  options[optName].stringValues.push_back("false");
148  options[optName].provided = true;
149  } else {
150  print("Failed to parse command line arguments.\n"
151  "No value passed for option '" + string(arg) + "'.\n");
152  return false;
153  }
154  }
155  } else {
156  print("Failed to parse command line arguments.\n"
157  "Unknown option '" + string(arg) + "'.\n");
158  return false;
159  }
160  }
161  }
162  return true;
163  }
164 
165  // Check if an option is defined.
166  // Returns true if the option is defined, false otherwise.
167  bool has(const string& optName) const {
168  return options.find(optName) != options.end();
169  }
170 
171  // Templated method to get the last value of an option.
172  // Returns the last value of the option converted to the specified type.
173  template<typename T>
174  T get(const string& optName) {
175  if (!has(optName)) {
176  print("Failed to find option '" + optName + "'.\n");
177  return T();
178  }
179  // Retrieve the OptionInfo object for the given option name.
180  const OptionInfo& optInfo = options.at(optName);
181  // Return default-constructed T if string is empty.
182  if (optInfo.stringValues.empty()) return T();
183  if (optInfo.stringValues.back().empty()) return T();
184  // Convert the string value to the specified type.
185  stringstream conv(optInfo.stringValues.back());
186  T value;
187  conv >> std::boolalpha >> value;
188  // Error message and default constructed T() is conversion failed.
189  if (conv.fail()) {
190  print("Failed to convert '" + optInfo.optName + "'.\n");
191  return T();
192  }
193  return value;
194  }
195 
196  // Templated method to get all the values of an option.
197  template<typename T>
198  vector<T> getVector(const string& optName) {
199  vector<T> values;
200  if (!has(optName)) {
201  print("Failed to find option '" + optName + "'.\n");
202  return values;
203  }
204  // Retrieve the OptionInfo object for the given option name.
205  const OptionInfo& optInfo = options.at(optName);
206  // Return empty vector if no values.
207  if (optInfo.stringValues.empty()) return values;
208  // Convert the string value to the specified type.
209  for (const string& stringValue: optInfo.stringValues) {
210  if (stringValue.empty()) {
211  values.push_back(T());
212  continue;
213  }
214  stringstream conv(stringValue);
215  T value;
216  conv >> std::boolalpha >> value;
217  // Error message and default constructed T() is conversion failed.
218  if (conv.fail()) {
219  print("Failed to convert '" + optInfo.optName + "'.\n");
220  return values;
221  }
222  values.push_back(value);
223  }
224  return values;
225  }
226 
227  // Method to generate the help text for all options.
228  // Returns a formatted string containing the help text.
229  const string help() const {
230  stringstream out;
231  auto oItr = options.cbegin();
232  while (oItr != options.cend()) {
233  out << "\t-" << oItr->second.optName;
234  if (!oItr->second.aliases.empty()) {
235  out << " (";
236  auto aItr = oItr->second.aliases.cbegin();
237  while(aItr != oItr->second.aliases.cend()) {
238  out << "-" << *aItr;
239  if (++aItr != oItr->second.aliases.cend()) out << ", ";
240  }
241  out << ")";
242  }
243  else out << "\t";
244  out << "\t" << oItr->second.helpText;
245  if (oItr->second.required)
246  out << " (required)";
247  else if (oItr->second.defaultValue != "")
248  out << " (default: " << oItr->second.defaultValue << ")";
249  if (++oItr != options.cend()) out << "\n";
250  }
251  return out.str();
252  }
253 
254 private:
255 
256  // Struct to hold information about each option.
257  struct OptionInfo {
258  // Default constructor must exist.
259  OptionInfo() {}
260  // Constructor to initialize all fields.
261  OptionInfo(const string& n, const string& v, const string& h,
262  set<string> a) : optName(n), defaultValue(v),
263  stringValues(1, v), helpText(h), aliases(a) {}
264  string optName; // Name of the option.
265  string defaultValue; // Default value of the option.
266  vector<string> stringValues; // Values of the option.
267  string helpText; // Help text for the option.
268  set<string> aliases; // Aliases for the option.
269  bool required{false}; // Flag if this option must be provided.
270  bool provided{false}; // Flag that the user provided this option.
271  };
272 
273  // Members.
274  string usageText{}; // Text for usage.
275  vector<string> examplesText{}; // Text for examples.
276  string extraText{}; // Extra text to print.
277  ostream* streamPtr{}; // Optional stream to print messages.
278  string helpFlag{}; // The flag used to access help.
279 
280  // Method to print to stream.
281  void print(string out) {if (streamPtr != nullptr) *streamPtr << out;}
282 
283  // Map to store options with their names as keys.
284  map<string, OptionInfo> options;
285  // Map to store aliases with aliases as keys and option names as values.
286  map<string, string> aliasMap;
287 
288 };
289 
290 } // end namespace Pythia8
291 
292 #endif // end Pythia8_InputParser_H
bool has(const string &optName) const
Definition: InputParser.h:167
InputParser(string usage="", vector< string > examples={}, string extra="", ostream *stream=&cout, string optName="h", set< string > aliases={"-help","H"})
Definition: InputParser.h:35
bool require(const string &optName, const string &helpText="", set< string > aliases={})
Add required command line option to the parser. Do this before parsing.
Definition: InputParser.h:73
const string help() const
Definition: InputParser.h:229
Status init(int &argc, char **argv)
Definition: InputParser.h:83
Definition: InputParser.h:25
bool add(const string &optName, const string &defString, const string &helpText="", set< string > aliases={})
Definition: InputParser.h:49
Status
Definition: InputParser.h:82
vector< T > getVector(const string &optName)
Templated method to get all the values of an option.
Definition: InputParser.h:198
bool parse(int &argc, char **argv)
Definition: InputParser.h:122
Header for classes to set beam momentum and interaction vertex spread.
Definition: Analysis.h:20