cepgen is hosted by Hepforge, IPPP Durham
CepGen 1.2.5
Central exclusive processes event generator
Loading...
Searching...
No Matches
ArgumentsParser.cpp
Go to the documentation of this file.
1/*
2 * CepGen: a central exclusive processes event generator
3 * Copyright (C) 2019-2023 Laurent Forthomme
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include <strings.h>
20
21#include <algorithm>
22#include <fstream>
23#include <sstream>
24
27#include "CepGen/Utils/String.h"
28#include "CepGen/Version.h"
29
30namespace cepgen {
31 ArgumentsParser::ArgumentsParser(int argc, char* argv[])
32 : command_name_(argc > 0 ? argv[0] : ""),
33 help_str_({{"help,h"}}),
34 version_str_({{"version,v"}}),
35 config_str_({{"cmd,c"}}),
36 debug_str_({{"debug,d"}}) {
37 //--- first remove the program name
38 std::vector<std::string> args_tmp;
39 if (argc > 1) {
40 args_tmp.resize(argc - 1);
41 std::copy(argv + 1, argv + argc, args_tmp.begin());
42 }
43 //--- then loop on user arguments to identify word -> value pairs
44 for (auto it_arg = args_tmp.begin(); it_arg != args_tmp.end(); ++it_arg) {
45 auto arg_val = utils::split(*it_arg, '='); // particular case for --arg=value
46 //--- check if help message is requested
47 for (const auto& str : help_str_)
48 if (arg_val.at(0) == "--" + str.name.at(0) || (str.name.size() > 1 && arg_val.at(0) == "-" + str.name.at(1)))
49 help_req_ = true;
50 //--- check if version message is requested
51 for (const auto& str : version_str_)
52 if (arg_val.at(0) == "--" + str.name.at(0) || (str.name.size() > 1 && arg_val.at(0) == "-" + str.name.at(1)))
53 version_req_ = true;
54 //--- check if debugging is requested
55 for (const auto& str : debug_str_)
56 if (arg_val.at(0) == "--" + str.name.at(0) || (str.name.size() > 1 && arg_val.at(0) == "-" + str.name.at(1))) {
57 CG_LOG_LEVEL(debug);
58 if (arg_val.size() > 1) {
59 const auto& token = arg_val.at(1);
60 if (token.find(":") == std::string::npos)
61 utils::Logger::get().output().reset(new std::ofstream(token));
62 else {
63 const auto tokens = utils::split(token, ':');
64 utils::Logger::get().output().reset(new std::ofstream(tokens.at(1)));
65 for (const auto& tok : utils::split(tokens.at(0), ';'))
66 utils::Logger::get().addExceptionRule(tok);
67 }
68 }
69 debug_req_ = true;
70 }
71 //--- check if configuration word is requested
72 auto it = std::find_if(config_str_.begin(), config_str_.end(), [&arg_val](const auto& str) {
73 return (arg_val.at(0) == "--" + str.name.at(0) ||
74 (str.name.size() > 1 && arg_val.at(0) == "-" + str.name.at(1)));
75 });
76 if (it != config_str_.end()) {
77 // if a configuration word is found, all the remaining flags are parsed as such
78 extra_config_ = std::vector<std::string>(it_arg + 1, args_tmp.end());
79 break;
80 }
81 //--- parse arguments if word found after
82 if (arg_val.size() == 1 && arg_val.at(0)[0] == '-' && it_arg != std::prev(args_tmp.end())) {
83 const auto& word = *std::next(it_arg);
84 if (word[0] != '-') {
85 arg_val.emplace_back(*std::next(it_arg));
86 ++it_arg;
87 }
88 }
89 args_.emplace_back(std::make_pair(arg_val.at(0), arg_val.size() > 1 ? arg_val.at(1) : ""));
90 }
91 }
92
94
96
97 void ArgumentsParser::dump() const {
98 CG_INFO("ArgumentsParser").log([&](auto& info) {
99 info << "List of parameters retrieved from command-line:";
100 for (const auto& par : params_)
101 info << "\n\t[--" << par.name.at(0) << (par.name.size() > 1 ? "|-" + par.name.at(1) : "")
102 << (par.optional ? ", optional" : "") << "] = " << par.value;
103 });
104 }
105
107 if (help_req_) {
108 print_help();
109 exit(0);
110 }
111 //--- dump the generator version
112 if (version_req_) {
114 exit(0);
115 }
116 if (debug_req_)
117 CG_DEBUG("ArgumentsParser") << "Debugging mode enabled.";
118 //--- loop over all parameters
119 size_t i = 0;
120 for (auto& par : params_) {
121 if (par.name.empty()) {
122 //--- no argument name ; fetching by index
123 if (i >= args_.size())
124 throw CG_FATAL("ArgumentsParser") << help_message() << " Failed to retrieve required <arg" << i << ">.";
125 par.value = !par.boolean() ? args_.at(i).second : "1";
126 } else {
127 // for each parameter, loop over arguments to find correspondence
128 auto it = std::find_if(args_.begin(), args_.end(), [&i, &par](const auto& arg) {
129 if (arg.first != "--" + par.name.at(0) && (par.name.size() < 2 || arg.first != "-" + par.name.at(1)))
130 return false;
131 par.value = arg.second;
132 if (par.boolean()) { // all particular cases for boolean arguments
133 const auto word = utils::toLower(arg.second);
134 if (word.empty() || word == "1" || word == "on" || word == "yes" || word == "true")
135 par.value = "1"; // if the flag is set, enabled by default
136 else
137 par.value = "0";
138 }
139 ++i;
140 return true;
141 });
142 if (it == args_.end()) {
143 if (args_.size() > i && args_.at(i).first[0] != '-')
144 par.value = args_.at(i).first;
145 else if (!par.optional) // no match
146 throw CG_FATAL("ArgumentsParser")
147 << help_message() << " The following parameter was not set: '" << par.name.at(0) << "'.";
148 }
149 }
150 par.parse();
151 CG_DEBUG("ArgumentsParser") << "Parameter '" << i << "|--" << par.name.at(0)
152 << (par.name.size() > 1 ? "|-" + par.name.at(1) : "") << "'"
153 << " has value '" << par.value << "'.";
154 ++i;
155 }
156 return *this;
157 }
158
159 std::string ArgumentsParser::operator[](std::string name) const {
160 for (const auto& par : params_) {
161 if ("--" + par.name.at(0) == name)
162 return par.value;
163 if (par.name.size() > 1 && "-" + par.name.at(1) == name)
164 return par.value;
165 }
166 throw CG_FATAL("ArgumentsParser") << "The parameter \"" << name << "\" was not declared "
167 << "in the arguments parser constructor!";
168 }
169
170 std::string ArgumentsParser::help_message() const {
171 std::ostringstream oss;
172 std::vector<std::pair<Parameter, size_t> > req_params, opt_params;
173 oss << "Usage: " << command_name_;
174 size_t i = 0;
175 for (const auto& par : params_) {
176 if (par.optional) {
177 opt_params.emplace_back(std::make_pair(par, i));
178 oss << " [";
179 } else {
180 req_params.emplace_back(std::make_pair(par, i));
181 oss << " ";
182 }
183 oss << (!par.name.at(0).empty() ? "--" : " <arg" + std::to_string(i) + ">") << par.name.at(0);
184 if (par.name.size() > 1)
185 oss << (!par.name.at(0).empty() ? "|" : "") << "-" << par.name.at(1);
186 if (par.optional)
187 oss << "]";
188 ++i;
189 }
190 if (req_params.size() > 0) {
191 oss << "\n " << utils::s("required argument", req_params.size(), false) << ":";
192 for (const auto& par : req_params)
193 oss << utils::format(
194 "\n\t%s%-18s\t%-30s",
195 (par.first.name.size() > 1 ? "-" + par.first.name.at(1) + "|" : "").c_str(),
196 (!par.first.name.at(0).empty() ? "--" + par.first.name.at(0) : "<arg" + std::to_string(par.second) + ">")
197 .c_str(),
198 par.first.description.c_str());
199 }
200 if (opt_params.size() > 0) {
201 oss << "\n " << utils::s("optional argument", opt_params.size(), false) << ":";
202 for (const auto& par : opt_params)
203 oss << utils::format(
204 "\n\t%s%-18s\t%-30s\tdef: '%s'",
205 (par.first.name.size() > 1 ? "-" + par.first.name.at(1) + "|" : "").c_str(),
206 (!par.first.name.at(0).empty() ? "--" + par.first.name.at(0) : "<arg" + std::to_string(par.second) + ">")
207 .c_str(),
208 par.first.description.c_str(),
209 par.first.value.c_str());
210 }
211 oss << "\n "
212 << "debugging:\n\t-d|--debug<=output file|=mod1,mod2,...:output file>\tredirect to output file, enable "
213 "module(s)";
214 oss << std::endl;
215 return oss.str();
216 }
217
218 //----- simple parameters
219
220 ArgumentsParser::Parameter::Parameter(std::string pname, std::string pdesc, std::string* var, std::string def)
221 : name(utils::split(pname, ',')), description(pdesc), value(def), str_variable_(var) {}
222
223 ArgumentsParser::Parameter::Parameter(std::string pname, std::string pdesc, double* var, double def)
224 : name(utils::split(pname, ',')), description(pdesc), value(utils::format("%g", def)), float_variable_(var) {}
225
226 ArgumentsParser::Parameter::Parameter(std::string pname, std::string pdesc, int* var, int def)
227 : name(utils::split(pname, ',')), description(pdesc), value(utils::format("%+i", def)), int_variable_(var) {}
228
229 ArgumentsParser::Parameter::Parameter(std::string pname, std::string pdesc, unsigned int* var, unsigned int def)
230 : name(utils::split(pname, ',')), description(pdesc), value(std::to_string(def)), uint_variable_(var) {}
231
232 ArgumentsParser::Parameter::Parameter(std::string pname, std::string pdesc, bool* var, bool def)
233 : name(utils::split(pname, ',')), description(pdesc), value(utils::format("%d", def)), bool_variable_(var) {}
234
235 ArgumentsParser::Parameter::Parameter(std::string pname, std::string pdesc, Limits* var, Limits def)
236 : name(utils::split(pname, ',')),
237 description(pdesc),
238 value(utils::format("%g,%g", def.min(), def.max())),
239 lim_variable_(var) {}
240
241 //----- vector of parameters
242
243 ArgumentsParser::Parameter::Parameter(std::string pname,
244 std::string pdesc,
245 std::vector<std::string>* var,
246 std::vector<std::string> def)
247 : name(utils::split(pname, ',')), description(pdesc), value(utils::merge(def, ";")), vec_str_variable_(var) {}
248
249 ArgumentsParser::Parameter::Parameter(std::string pname,
250 std::string pdesc,
251 std::vector<int>* var,
252 std::vector<int> def)
253 : name(utils::split(pname, ',')), description(pdesc), value(utils::merge(def, ";")), vec_int_variable_(var) {}
254
255 ArgumentsParser::Parameter::Parameter(std::string pname,
256 std::string pdesc,
257 std::vector<double>* var,
258 std::vector<double> def)
259 : name(utils::split(pname, ',')), description(pdesc), value(utils::merge(def, ";")), vec_float_variable_(var) {}
260
261 bool ArgumentsParser::Parameter::matches(const std::string& key) const {
262 if (key == "--" + name.at(0))
263 return true;
264 if (name.size() > 1 && key == "-" + name.at(1))
265 return true;
266 return false;
267 }
268
269 ArgumentsParser::Parameter& ArgumentsParser::Parameter::parse() {
270 CG_DEBUG("ArgumentsParser:Parameter:parse") << "Parsing argument " << name << ".";
271 if (str_variable_) {
272 *str_variable_ = value;
273 return *this;
274 }
275 if (float_variable_)
276 try {
277 *float_variable_ = std::stod(value);
278 return *this;
279 } catch (const std::invalid_argument&) {
280 throw CG_FATAL("ArgumentsParser:Parameter:parse") << "Failed to parse variable '" << name << "' as float!";
281 }
282 if (int_variable_)
283 try {
284 *int_variable_ = std::stoi(value);
285 return *this;
286 } catch (const std::invalid_argument&) {
287 throw CG_FATAL("ArgumentsParser:Parameter:parse") << "Failed to parse variable '" << name << "' as integer!";
288 }
289 if (uint_variable_)
290 try {
291 *uint_variable_ = std::stoi(value);
292 return *this;
293 } catch (const std::invalid_argument&) {
294 throw CG_FATAL("ArgumentsParser:Parameter:parse")
295 << "Failed to parse variable '" << name << "' as unsigned integer!";
296 }
297 if (bool_variable_) {
298 try {
299 *bool_variable_ = (std::stoi(value) != 0);
300 return *this;
301 } catch (const std::invalid_argument&) {
302 *bool_variable_ = (strcasecmp("true", value.c_str()) == 0 || strcasecmp("yes", value.c_str()) == 0 ||
303 strcasecmp("on", value.c_str()) == 0 || strcasecmp("1", value.c_str()) == 0) &&
304 strcasecmp("false", value.c_str()) != 0 && strcasecmp("no", value.c_str()) != 0 &&
305 strcasecmp("off", value.c_str()) != 0 && strcasecmp("0", value.c_str()) != 0;
306 }
307 }
308 if (vec_str_variable_) {
309 *vec_str_variable_ = utils::split(value, ';', true);
310 return *this;
311 }
312 if (vec_int_variable_) {
313 vec_int_variable_->clear();
314 const auto buf = utils::split(value, ';');
315 std::transform(buf.begin(), buf.end(), std::back_inserter(*vec_int_variable_), [](const std::string& str) {
316 return std::stoi(str);
317 });
318 return *this;
319 }
320 auto unpack_floats = [](const std::string& value, char delim) {
321 std::vector<double> vec_flt;
322 const auto buf = utils::split(value, delim);
323 std::transform(buf.begin(), buf.end(), std::back_inserter(vec_flt), [](const std::string& str) {
324 try {
325 return std::stod(str);
326 } catch (const std::invalid_argument&) {
327 return Limits::INVALID;
328 }
329 });
330 return vec_flt;
331 };
332 if (vec_float_variable_) {
333 *vec_float_variable_ = unpack_floats(value, ';');
334 return *this;
335 }
336 if (lim_variable_) {
337 const auto vec_flt = unpack_floats(value, ',');
338 if (vec_flt.size() == 2) {
339 if (vec_flt.at(0) != Limits::INVALID)
340 lim_variable_->min() = vec_flt.at(0);
341 if (vec_flt.at(1) != Limits::INVALID)
342 lim_variable_->max() = vec_flt.at(1);
343 }
344 return *this;
345 }
346 throw CG_FATAL("Parameter") << "Failed to parse parameter \"" << name.at(0) << "\"!";
347 }
348} // namespace cepgen
#define CG_FATAL(mod)
Definition Exception.h:61
#define CG_LOG_LEVEL(type)
Definition Logger.h:83
#define CG_LOG
Definition Message.h:212
#define CG_DEBUG(mod)
Definition Message.h:220
#define CG_INFO(mod)
Definition Message.h:216
A generic command line arguments parser.
void print_version() const
Show version.
ArgumentsParser(int argc, char *argv[])
Arguments parser constructor.
ArgumentsParser & parse()
Associate command-line arguments to parameters.
void print_help() const
Show usage.
std::string help_message() const
Usage message.
void dump() const
Dump the list of arguments into the terminal.
static constexpr double INVALID
Invalid value placeholder (single-edged or invalid limits)
Definition Limits.h:86
static Logger & get(std::ostream *=nullptr)
Retrieve the running instance of the logger.
Definition Logger.cpp:31
StreamHandler & output()
Output stream to use for all logging operations.
Definition Logger.cpp:58
std::string get(const std::string &var, const std::string &def)
Get an environment variable.
std::string format(const std::string &fmt, Args... args)
Format a string using a printf style format descriptor.
Definition String.h:61
std::string merge(const std::vector< T > &vec, const std::string &delim)
Merge a collection of a printable type in a single string.
Definition String.cpp:248
std::vector< std::string > split(const std::string &str, char delim, bool trim)
Split a string according to a separation character.
Definition String.cpp:233
Common namespace for this Monte Carlo generator.
static const std::string banner
CepGen banner.
Definition Version.h:32