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