HILA
Loading...
Searching...
No Matches
cmdline.cpp
1#include <stdio.h>
2#include <stdlib.h>
3#include <string>
4#include <map>
5#include <vector>
6#include <regex>
7#include <charconv>
8#include "hila.h"
9
10using strvec = std::vector<std::string>;
11
12////////////////////////////////////////////////////////////////////////////////
13/// @brief Prints the help texts associated with recognized flags and quits the
14/// program
15////////////////////////////////////////////////////////////////////////////////
16void cmdlinearguments::quit_with_help() {
17 print_help();
19}
20
21////////////////////////////////////////////////////////////////////////////////
22/// @struct argmap_val
23/// @brief Struct to hold structured information on used command line arguments
24///
25/// @var strvec argmap_val::val_strings
26/// @brief Strings corresponding to read in cmdline parameters
27///
28/// @var std::string argmap_val::help_text
29/// @brief A string describing the use of the flag
30///
31/// @var int argmap_val::number
32/// @brief number of args for each flag. < 0: unspecified
33///
34/// @var bool argmap_val::present
35/// @brief A boolean telling whether the flag was found in argv
36////////////////////////////////////////////////////////////////////////////////
37struct argmap_val {
39 std::string aux;
40 std::string help_text;
41 int number;
42 bool present;
43};
44
45// class instance cmdline is globally available through hila.h
46namespace hila {
47cmdlinearguments cmdline;
48}
49
50cmdlinearguments::cmdlinearguments() {}
51
52////////////////////////////////////////////////////////////////////////////////
53/// @brief Copies provided argc and argv into internal variables.
54///
55/// @param argc length of argv
56/// @param argv array of pointers to command-line arguments
57////////////////////////////////////////////////////////////////////////////////
58void cmdlinearguments::initialise_args(int argc0, char **argv0) {
59 argc = argc0;
60 argv = (const char **)malloc(argc * sizeof(const char *));
61 for (int i = 0; i < argc; i++)
62 argv[i] = argv0[i];
63
64 // Proceed to parse argv
65 fill_argmap();
66}
67
68////////////////////////////////////////////////////////////////////////////////
69/// @brief Returns the vector of strings associated with flag 'flag'.
70///
71/// @param flag flag given as string
72/// @return vector of arguments
73////////////////////////////////////////////////////////////////////////////////
74strvec cmdlinearguments::values(const std::string &flag) {
75 strvec valvec = argmap[flag].val_strings;
76 if (argmap.count(flag) == 0) {
77 hila::out0 << "Flag '" << flag << "' is not recognized!\n";
78 quit_with_help();
79 }
80
81 if (valvec.size() == 0) {
82 hila::out0 << "\n\nFlag '" << flag << "' has no entries"
83 << " and the associated vector of strings is of length zero!\n\n";
84 }
85 return valvec;
86}
87
88////////////////////////////////////////////////////////////////////////////////
89/// @brief Adds a new recognized flag 'flag' and associated information to the
90/// argmap.
91///
92/// @param flag string with the name of the flag, always of format "^-[a-z].*"
93/// (no whitespace allowed), e.g. '-my_flag123'
94/// @param help_text a string containing useful knowledge of the usage of the flag.
95/// Will be printed when error occurs. For formatting, a newline
96/// can be used to split the help_text, as it will help with
97/// correct formatting in print_help().
98/// @param number required number of args for each key appearance. default -1: number
99/// unspecified
100////////////////////////////////////////////////////////////////////////////////
101void cmdlinearguments::add_flag(const std::string &flag, const std::string &help_text,
102 std::string aux, int number) {
103 if (argmap.count(flag) > 0) {
104 hila::out0 << "\n###################################################\n";
105 hila::out0 << "# Flag " << flag << " is already set! Terminating.\n";
106 hila::out0 << "###################################################\n\n";
107 quit_with_help();
108 } else {
109 argmap[flag] = {std::vector<std::string>(), aux, help_text, number, false};
110 }
111}
112
113////////////////////////////////////////////////////////////////////////////////
114/// @brief Given flag '-flag' argv is scanned for valid entries that follow.
115/// @details Once '-flag' is found the following strings are scanned. If they
116/// are not in the format of a flag (described in add_flag()) they are
117/// considered valid input. argv with
118/// "-flag a -b_flag b -flag c d -flag -flag 4"
119/// should result in {"a", "c", "d", "4"}.
120///
121/// @param flag string corresponding to the flag
122/// @return Vector containing the found entries.
123////////////////////////////////////////////////////////////////////////////////
124strvec cmdlinearguments::read_arg_vector(const char *flag) {
125 strvec uargs;
126
127 std::vector<int> u_ind(argc);
128 for (int i = 0; i < argc; i++)
129 u_ind[i] = -1;
130 int *p_ind = u_ind.data();
131
132 // Anything of the format "-[a letter][whatever]" is parsed as a flag
133 const std::regex forbidden_user_input("^-[a-zA-Z].*");
134
135 for (int i = 0; i < argc; i++) {
136 const char *p = argv[i];
137 // check if its the flag
138 int n = 0;
139 bool match = false;
140 if (std::strcmp(p, flag) == 0) {
141 match = true;
142 // Indicate that the flag has been spotted in argv
143 argmap[flag].present = true;
144 // Slate for removal and move onto the
145 // options
146 *(p_ind++) = 1;
147 i++;
148 if (i < argc)
149 p = argv[i];
150 else {
151 break;
152 }
153 // Check if the string is a valid input
154 // (Not of type ^-[a-zA-Z].*)
155 // and push into vector uargs
156 while (!std::regex_match(p, forbidden_user_input)) {
157 *(p_ind++) = 1;
158 uargs.push_back(std::string(p));
159 i++;
160 n++;
161 if (i < argc)
162 p = argv[i];
163 else
164 break;
165 }
166
167 // If we stopped on another 'flag' for whatever
168 // reason, set the current index to be removed
169 // and compensate for the for loop increase in
170 // i and p_ind
171 if (std::strcmp(p, flag) == 0) {
172 *(p_ind--) = 1;
173 i--;
174 }
175 }
176
177 if (match) {
178
179 if (argmap[flag].number >= 0) {
180 if (argmap[flag].number != n) {
181 hila::out0 << "Error: command line flag " << flag << " requires "
182 << argmap[flag].number << " arguments\n";
183 quit_with_help();
184 }
185 }
186 }
187
188 p_ind++;
189 }
190 // Effectively remove user arguments from argv
191 int j = 0;
192 for (int i = 0; i < argc; i++)
193 if (u_ind[i] < 0)
194 argv[j++] = argv[i];
195 argc = j;
196
197 return uargs;
198}
199
200////////////////////////////////////////////////////////////////////////////////
201/// @brief Loops over the known flags and fills the argmap struct from argv
202////////////////////////////////////////////////////////////////////////////////
203void cmdlinearguments::fill_argmap() {
204 // Loop through the flags (keys of the map)
205 for (auto const &p : argmap) {
206 // Get corresponding input and set it to uarg[flag]
207 std::vector<std::string> arg_vec = read_arg_vector(p.first.c_str());
208 argmap[std::string(p.first)].val_strings = arg_vec;
209 }
210 if (argc > 1) {
211 hila::out0 << "There remain unprocessed command-line arguments:\n";
212 for (int i = 1; i < argc; i++)
213 hila::out0 << "'" << argv[i] << "', ";
214 hila::out0 << "\n";
215 hila::out0 << "Terminating.\n";
216 quit_with_help();
217 }
218}
219
220////////////////////////////////////////////////////////////////////////////////
221/// @brief Splits a help_text string into a vector of strings at newline.
222/// @return A vector containing the lines.
223////////////////////////////////////////////////////////////////////////////////
224strvec cmdlinearguments::parse_help(std::string help_text) {
225 strvec lines;
226 std::stringstream stream(help_text);
227 std::string line;
228
229 while (std::getline(stream, line)) {
230 lines.push_back(line);
231 }
232
233 return lines;
234}
235
236////////////////////////////////////////////////////////////////////////////////
237/// @brief Prints the recognized flags and their help texts.
238////////////////////////////////////////////////////////////////////////////////
239void cmdlinearguments::print_help() {
240 hila::out0 << "Recognized command-line flags and their possible arguments:\n";
241
242 const std::string padding = " ";
243 for (auto const &p : argmap) {
244 const std::string &flag = p.first;
245 const std::string &help = p.second.help_text;
246 const std::string &aux = p.second.aux;
247 strvec help_vec = parse_help(help);
248 hila::out0 << " " << flag << " " << aux << std::setw(22 - flag.length() - aux.length() - 1)
249 << ": " << help_vec[0] << "\n";
250 for (int i = 1; i < help_vec.size(); i++) {
251 hila::out0 << padding << help_vec[i] << "\n";
252 }
253 }
254}
255
256////////////////////////////////////////////////////////////////////////////////
257/// @brief Checks whether a flag has entries by returning the count. Does not
258/// differentiate on whether the flag is known.
259///
260/// @param flag
261////////////////////////////////////////////////////////////////////////////////
262int cmdlinearguments::flag_set(const char *flag) {
263 if (argmap.count(flag) > 0)
264 return argmap[flag].val_strings.size();
265 else
266 return 0;
267}
268
269////////////////////////////////////////////////////////////////////////////////
270/// @brief Checks if a flag has been found in argv
271///
272/// @return Boolean indicating the statement.
273////////////////////////////////////////////////////////////////////////////////
274bool cmdlinearguments::flag_present(const char *flag) {
275 return argmap[flag].present;
276}
277
278////////////////////////////////////////////////////////////////////////////////
279/// @brief Attempts to return a long integer from i:th entry of flag 'flag'.
280/// Failure results in terminating the program.
281/// @param flag
282/// @param i index of the desired entry (default = 0);
283/// @return long conversion of the desired entry
284////////////////////////////////////////////////////////////////////////////////
285long cmdlinearguments::get_int(const char *flag, int i) {
286 // If flag is found and non-empty
287 int set = flag_set(flag);
288 if (set) {
289 std::string opt;
290 if (i < set)
291 opt = argmap[flag].val_strings[i];
292 else {
293 hila::out0 << "Flag '" << flag << "' has only " << set + 1
294 << " entries. Can't return index " << i << ".\n";
295 hila::out0 << "Terminating.\n";
296 quit_with_help();
297 }
298
299 long val = 0;
300 // Check for format
301 const std::regex permitted_user_input("^[+-]?[0-9]+");
302 if (std::regex_match(opt, permitted_user_input)) {
303 val = std::stol(opt);
304 } else {
305 hila::out0 << "Expected a number (integer) after command line parameter '" << flag
306 << "'\n";
307 quit_with_help();
308 }
309 return val;
310 } else {
311 hila::out0 << "Flag '" << flag << "' is missing an entry!\n";
312 hila::out0 << "Terminating.\n";
313 quit_with_help();
314 return -1;
315 }
316}
317
318////////////////////////////////////////////////////////////////////////////////
319/// @brief Attempts to return a double from i:th entry of flag 'flag'.
320/// Failure results in terminating the program. If std::stod
321/// doesn't complain, the conversion is considered valid.
322/// @param flag
323/// @param i index of the desired entry (default = 0);
324/// @return double conversion of the desired entry
325////////////////////////////////////////////////////////////////////////////////
326double cmdlinearguments::get_double(const char *flag, int i) {
327 // If flag is found and non-empty
328 int set = flag_set(flag);
329 if (set) {
330 std::string opt;
331 if (i < set)
332 opt = argmap[flag].val_strings[i];
333 else {
334 hila::out0 << "Flag '" << flag << "' has only " << set
335 << " entries. Can't return index " << i << ".\n";
336 hila::out0 << "Terminating.\n";
337 quit_with_help();
338 return -1;
339 }
340
341 double val;
342 // We're not going to manually check the format for this
343 auto [p, ec] = std::from_chars(opt.data(), opt.data() + opt.size(), val);
344 if (p == opt.data()) {
345 hila::out0 << "Expected a number (double) after command line parameter '" << flag
346 << "'\n";
347 quit_with_help();
348 }
349
350 // try {
351 // val = std::stod(opt);
352 // } catch (std::exception &e) {
353 // hila::out0 << "Expected a number (double) after command line parameter '" << flag
354 // << "'\n";
355 // quit_with_help();
356 // }
357
358 return val;
359 }
360 // if not found
361 else {
362 hila::out0 << "Flag '" << flag << "' is missing an entry!\n";
363 hila::out0 << "Terminating.\n";
364 quit_with_help();
365 return -1;
366 }
367}
368
369////////////////////////////////////////////////////////////////////////////////
370/// @brief Attempts to return the i:th entry of flag 'flag'.
371/// Failure results in terminating the program.
372///
373/// @param flag
374/// @param i index of the desired entry (default = 0);
375/// @return the desired entry
376////////////////////////////////////////////////////////////////////////////////
377std::string cmdlinearguments::get_string(const char *flag, int i, bool clear) {
378 int set = flag_set(flag);
379 if (set) {
380 if (i < set) {
381 auto t = argmap[flag].val_strings[i];
382 if (clear)
383 argmap[flag].val_strings[i].clear();
384 return t;
385 } else {
386 hila::out0 << "Flag '" << flag << "' has only " << set + 1
387 << " entries. Can't return index " << i << ".\n";
388 hila::out0 << "Terminating.\n";
389 quit_with_help();
390 return "";
391 }
392 } else {
393 hila::out0 << "Flag '" << flag << "' is missing an entry!\n";
394 hila::out0 << "Terminating.\n";
395 quit_with_help();
396 return "";
397 }
398}
Implement hila::swap for gauge fields.
Definition array.h:982
std::ostream out0
This writes output only from main process (node 0)
void finishrun()
Normal, controlled exit - all nodes must call this. Prints timing information and information about c...
Struct to hold structured information on used command line arguments.
Definition cmdline.cpp:37
bool present
A boolean telling whether the flag was found in argv.
Definition cmdline.cpp:42
strvec val_strings
Strings corresponding to read in cmdline parameters.
Definition cmdline.cpp:38
std::string help_text
A string describing the use of the flag.
Definition cmdline.cpp:40
int number
number of args for each flag. < 0: unspecified
Definition cmdline.cpp:41