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{
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 bool argmap_val::present
32/// @brief A boolean telling whether the flag was found in argv
33////////////////////////////////////////////////////////////////////////////////
35{
37 std::string help_text;
38 bool present;
39};
40
41// class instance cmdline is globally available through hila.h
42namespace hila {
43cmdlinearguments cmdline;
44}
45
46cmdlinearguments::cmdlinearguments() {}
47
48////////////////////////////////////////////////////////////////////////////////
49/// @brief Copies provided argc and argv into internal variables.
50///
51/// @param argc length of argv
52/// @param argv array of pointers to command-line arguments
53////////////////////////////////////////////////////////////////////////////////
54void cmdlinearguments::initialise_args(int argc0, char **argv0) {
55 argc = argc0;
56 argv = (const char **)malloc(argc * sizeof(const char *));
57 for (int i = 0; i < argc; i++)
58 argv[i] = argv0[i];
59
60 // Proceed to parse argv
61 fill_argmap();
62}
63
64////////////////////////////////////////////////////////////////////////////////
65/// @brief Returns the vector of strings associated with flag 'flag'.
66///
67/// @param flag flag given as string
68/// @return vector of arguments
69////////////////////////////////////////////////////////////////////////////////
70strvec cmdlinearguments::values(std::string flag)
71{
72 strvec valvec = argmap[flag].val_strings;
73 if (argmap.count(flag) == 0)
74 {
75 hila::out0 << "Flag '" << flag << "' is not recognized!\n";
76 quit_with_help();
77 }
78
79 if (valvec.size() == 0)
80 {
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////////////////////////////////////////////////////////////////////////////////
98void cmdlinearguments::add_flag(std::string flag, std::string help_text)
99{
100 if (argmap.count(flag) > 0)
101 {
102 hila::out0 << "\n###################################################\n";
103 hila::out0 << "# Flag " << flag << " is already set! Terminating.\n";
104 hila::out0 << "###################################################\n\n";
105 quit_with_help();
106 }
107 else
108 {
109 argmap[flag] = {std::vector<std::string>(), help_text, 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{
126 strvec uargs;
127
128 int u_ind[argc];
129 for (int i = 0; i < argc; i++) u_ind[i] = -1;
130 int *p_ind = u_ind;
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 {
137 const char *p = argv[i];
138 // check if its the flag
139 if (std::strcmp(p, flag) == 0)
140 {
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 {
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 {
158 *(p_ind++) = 1;
159 uargs.push_back(std::string(p));
160 i++;
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 {
173 *(p_ind--) = 1;
174 i--;
175 }
176 }
177 p_ind++;
178 }
179 // Effectively remove user arguments from argv
180 int j = 0;
181 for (int i = 0; i < argc; i++) if (u_ind[i] < 0) argv[j++] = argv[i];
182 argc = j;
183
184 return uargs;
185}
186
187////////////////////////////////////////////////////////////////////////////////
188/// @brief Loops over the known flags and fills the argmap struct from argv
189////////////////////////////////////////////////////////////////////////////////
190void cmdlinearguments::fill_argmap()
191{
192 // Loop through the flags (keys of the map)
193 for (auto const& p : argmap)
194 {
195 // Get corresponding input and set it to uarg[flag]
196 std::vector<std::string> arg_vec = read_arg_vector(p.first.c_str());
197 argmap[std::string(p.first)].val_strings = arg_vec;
198 }
199 if (argc > 1)
200 {
201 hila::out0 << "There remain unprocessed command-line arguments:\n";
202 for (int i = 1; i < argc; i++)
203 hila::out0 << "'" << argv[i] << "', ";
204 hila::out0 << "\n";
205 hila::out0 << "Terminating.\n";
206 quit_with_help();
207 }
208}
209
210////////////////////////////////////////////////////////////////////////////////
211/// @brief Splits a help_text string into a vector of strings at newline.
212/// @return A vector containing the lines.
213////////////////////////////////////////////////////////////////////////////////
214strvec cmdlinearguments::parse_help(std::string help_text)
215{
216 strvec lines;
217 std::stringstream stream(help_text);
218 std::string line;
219
220 while (std::getline(stream, line))
221 {
222 lines.push_back(line);
223 }
224
225 return lines;
226}
227
228////////////////////////////////////////////////////////////////////////////////
229/// @brief Prints the recognized flags and their help texts.
230////////////////////////////////////////////////////////////////////////////////
231void cmdlinearguments::print_help()
232{
233 hila::out0 << "Recognized command-line flags and their possible arguments:\n";
234
235 for (auto const& p : argmap)
236 {
237 std::string flag = p.first;
238 std::string help = p.second.help_text;
239 strvec help_vec = parse_help(help);
240 hila::out0 << " " << flag << std::setw(20 - flag.length())
241 << ": " << help_vec[0] << "\n";
242 for (int i = 1; i < help_vec.size(); i++)
243 {
244 std::string padding = " ";
245 hila::out0 << padding << help_vec[i] << "\n";
246 }
247 }
248
249}
250
251////////////////////////////////////////////////////////////////////////////////
252/// @brief Checks whether a flag has entries by returning the count. Does not
253/// differentiate on whether the flag is known.
254///
255/// @param flag
256////////////////////////////////////////////////////////////////////////////////
257int cmdlinearguments::flag_set(const char *flag)
258{
259 if (argmap.count(flag) > 0)
260 return argmap[flag].val_strings.size();
261 else return 0;
262}
263
264////////////////////////////////////////////////////////////////////////////////
265/// @brief Checks if a flag has been found in argv
266///
267/// @return Boolean indicating the statement.
268////////////////////////////////////////////////////////////////////////////////
269bool cmdlinearguments::flag_present(const char *flag)
270{
271 return argmap[flag].present;
272}
273
274////////////////////////////////////////////////////////////////////////////////
275/// @brief Attempts to return a long integer from i:th entry of flag 'flag'.
276/// Failure results in terminating the program.
277/// @param flag
278/// @param i index of the desired entry (default = 0);
279/// @return long conversion of the desired entry
280////////////////////////////////////////////////////////////////////////////////
281long cmdlinearguments::get_int(const char *flag, int i)
282{
283 // If flag is found and non-empty
284 int set = flag_set(flag);
285 if (set)
286 {
287 std::string opt;
288 if (i < set)
289 opt = argmap[flag].val_strings[i];
290 else {
291 hila::out0 << "Flag '" << flag << "' has only " << set + 1
292 << " entries. Can't return index " << i << ".\n";
293 hila::out0 << "Terminating.\n";
294 quit_with_help();
295 }
296
297 long val = 0;
298 // Check for format
299 const std::regex permitted_user_input("^[+-]?[0-9]+");
300 if (std::regex_match(opt, permitted_user_input)) {
301 val = std::stol(opt);
302 }
303 else
304 {
305 hila::out0 << "Expected a number (integer) after command line parameter '" << flag
306 << "'\n";
307 quit_with_help();
308 }
309 return val;
310 }
311 else {
312 hila::out0 << "Flag '" << flag << "' is missing an entry!\n";
313 hila::out0 << "Terminating.\n";
314 quit_with_help();
315 return -1;
316 }
317}
318
319////////////////////////////////////////////////////////////////////////////////
320/// @brief Attempts to return a double from i:th entry of flag 'flag'.
321/// Failure results in terminating the program. If std::stod
322/// doesn't complain, the conversion is considered valid.
323/// @param flag
324/// @param i index of the desired entry (default = 0);
325/// @return double conversion of the desired entry
326////////////////////////////////////////////////////////////////////////////////
327double cmdlinearguments::get_double(const char *flag, int i)
328{
329 // If flag is found and non-empty
330 int set = flag_set(flag);
331 if (set)
332 {
333 std::string opt;
334 if (i < set)
335 opt = argmap[flag].val_strings[i];
336 else {
337 hila::out0 << "Flag '" << flag << "' has only " << set
338 << " entries. Can't return index " << i << ".\n";
339 hila::out0 << "Terminating.\n";
340 quit_with_help();
341 return -1;
342 }
343
344 double val;
345 // We're not going to manually check the format for this
346 try
347 {
348 val = std::stod(opt);
349 }
350 catch(std::exception &e)
351 {
352 hila::out0 << "Expected a number (double) after command line parameter '" << flag
353 << "'\n";
354 quit_with_help();
355 }
356 return val;
357 }
358 // if not found
359 else
360 {
361 hila::out0 << "Flag '" << flag << "' is missing an entry!\n";
362 hila::out0 << "Terminating.\n";
363 quit_with_help();
364 return -1;
365 }
366}
367
368////////////////////////////////////////////////////////////////////////////////
369/// @brief Attempts to return the i:th entry of flag 'flag'.
370/// Failure results in terminating the program.
371///
372/// @param flag
373/// @param i index of the desired entry (default = 0);
374/// @return the desired entry
375////////////////////////////////////////////////////////////////////////////////
376std::string cmdlinearguments::get_string(const char *flag, int i)
377{
378 int set = flag_set(flag);
379 if (set)
380 {
381 if (i < set)
382 return argmap[flag].val_strings[i];
383 else {
384 hila::out0 << "Flag '" << flag << "' has only " << set + 1
385 << " entries. Can't return index " << i << ".\n";
386 hila::out0 << "Terminating.\n";
387 quit_with_help();
388 return "";
389 }
390 }
391 else
392 {
393 hila::out0 << "Flag '" << flag << "' is missing an entry!\n";
394 hila::out0 << "Terminating.\n";
395 quit_with_help();
396 return "";
397 }
398}
399
Invert diagonal + const. matrix using Sherman-Morrison formula.
Definition array.h:920
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:35
bool present
A boolean telling whether the flag was found in argv.
Definition cmdline.cpp:38
strvec val_strings
Strings corresponding to read in cmdline parameters.
Definition cmdline.cpp:36
std::string help_text
A string describing the use of the flag.
Definition cmdline.cpp:37