ArgParser.hpp
Go to the documentation of this file.
1 
15 /* TODO
16  * str_to_type_ -> make -B pretty -j3
17  * doc
18  *
19  * implement split_clean()
20  *
21  * V2:
22  * contract pwd nicely!
23  * struct converter {
24  * std::vector<double> apply(std::string const &);
25  * std::vector<double> apply(double const &); //check perhaps ArgParser<int_conv, double_conv, string_conv, custom_conv>
26  *
27  * ap.get<converter>("x", std::vector<double>({1,2,3,4}))
28  *
29  *
30  */
31 
32 #ifndef FSC_ARGPARSER_HEADER
33 #define FSC_ARGPARSER_HEADER
34 
35 #include "ArgParser/poly_type.hpp"
36 #include "ArgParser/boost_any.hpp"
37 #include "ArgParser/fsc_except.hpp"
38 
39 #include <map>
40 #include <vector>
41 #include <fstream>
42 #include <sstream>
43 #include <iostream>
44 #include <iterator>
45 #include <unistd.h>
46 #include <algorithm>
47 
48 namespace fsc {
50  namespace detail {
51  std::string get_progname(std::string pwd_name) {
52  auto pos = pwd_name.rfind("/");
53  if(pos == std::string::npos)
54  return pwd_name;
55 
56  pwd_name.erase(0, pos+1);
57  return pwd_name;
58  }
59  std::string get_pwd(std::string pwd_name, std::string const & cwd) {
60  auto pos = pwd_name.rfind("/");
61  if(pos == std::string::npos)
62  return cwd;
63 
64  pwd_name.erase(pos, pwd_name.size());
65 
66  return cwd + "/" + pwd_name;
67  }
68  std::vector<std::string> split_clean(std::string const & str) {
69  std::stringstream iss(str);
70  return std::vector<std::string>{std::istream_iterator<std::string>{iss}
71  , std::istream_iterator<std::string>{}};
72  }
73  }//end namespace detail
75 
83  class ArgParser {
84  //~ using poly_type = __future_impl;
85  using map_type = std::map<std::string, poly_type>;
86  template<typename U>
87  using vec_type = std::vector<U>;
88  using size_type = vec_type<poly_type>::size_type;
89  public:
90  //------------------- structors -------------------
95  ArgParser() noexcept: cwd_(getcwd(NULL, 0))
96  , pwd_("")
97  , progname_("") {
98  }
106  ArgParser(int const & argc, char * argv[]): cwd_(getcwd(NULL, 0))
107  , pwd_(detail::get_pwd(argv[0], cwd_))
108  , progname_(detail::get_progname(argv[0])) {
109  vec_type<std::string> arg;
110  for(int i = 1; i < argc; ++i) // 1 since we dont need the progname
111  arg.push_back(argv[i]);
112  parse_(arg);
113  }
121  explicit ArgParser(std::string const & cline): cwd_(getcwd(NULL, 0))
122  , pwd_("")
123  , progname_("") {
124  parse_(detail::split_clean(cline));
125  }
126  //------------------- const getter -------------------
132  poly_type const & operator[](std::string const & key) const {
133  if(n_args_.find(key) == n_args_.end())
134  throw std::runtime_error("ArgParser error: named argument '" + key + "' not found");
135  return n_args_.at(key);
136  }
143  poly_type const & operator[](size_type const & key) const {
144  if(key >= freeargc())
145  throw std::runtime_error("ArgParser error: free argument '" + std::to_string(key) + "' not found");
146  return f_args_[key];
147  }
155  template<typename T>
156  T get(std::string const & key, T const & def) const {
157  if(innamed_(key))
158  return n_args_.at(key);
159  else
160  return def;
161  }
169  std::string get(std::string const & key, char const * def) const {
170  return get<std::string>(key, def);
171  }
180  template<typename T>
181  T get(size_type const & pos, T const & def) const {
182  if(is_set(pos))
183  return f_args_.at(pos);
184  else
185  return def;
186  }
195  std::string get(size_type const & pos, char const * def) const {
196  return get<std::string>(pos, def);
197  }
202  std::string const & cwd() const noexcept {
203  return cwd_;
204  }
210  std::string const & pwd() const {
211  if(pwd_ == "")
212  throw std::runtime_error("ArgParser error: pwd not set");
213  return pwd_;
214  }
220  std::string const & progname() const {
221  if(progname_ == "")
222  throw std::runtime_error("ArgParser error: progname not set");
223  return progname_;
224  }
227  size_type freeargc() const noexcept {
228  return f_args_.size();
229  }
236  bool is_set(std::string const & key) const noexcept {
237  return inflag_(key) or innamed_(key);
238  }
244  bool is_set(size_type const & pos) const noexcept {
245  return pos < freeargc();
246  }
247  //------------------- modifier -------------------
256  template<typename T>
257  void def(std::string const & key, T const & def) {
258  if(!innamed_(key))
259  setnamed_(key, poly_type(def));
260  }
267  void def(std::string const & key) { // for O3
268  setflag_(key);
269  }
270 
289  void merge(ArgParser const & rhs, bool const & overwrite = true) noexcept {
290  //------------------- n_args_ -------------------
291  for(auto const & it: rhs.n_args_) {
292  std::string key = it.first;
293  poly_type val = it.second;
294  setnamed_(key, val, overwrite);
295  }
296  //------------------- f_args_ -------------------
297  if(overwrite) {
298  f_args_ = rhs.f_args_;
299  }
300  //------------------- flags_ -------------------
301  for(auto const & it: rhs.flags_) {
302  setflag_(it);
303  }
304  //------------------- special member -------------------
305  if(overwrite) {
306  cwd_ = rhs.cwd_;
307  pwd_ = rhs.pwd_;
308  progname_ = rhs.progname_;
309  }
310  }
319  bool parse_file(std::string const & filename, bool const & overwrite = true) {
320  vec_type<std::string> arg;
321  std::ifstream ifs(filename, std::ios_base::in);
322  std::string res = "";
323  if(ifs.is_open()) {
324  std::stringstream buffer;
325  buffer << ifs.rdbuf();
326  res = buffer.str();
327  ifs.close();
328  } else
329  return false;
330 
331  merge(ArgParser(res), overwrite);
332  return true;
333  }
334  //------------------- const fct -------------------
338  template<typename S>
339  void print(S & os) const noexcept {
340  os << "=======ArgParser=======" << std::endl;
341  os << "free_args:" << std::endl;
342  for(auto const & it: f_args_)
343  os << " " << it << std::endl;
344  os << "named_args:" << std::endl;
345  for(auto const & it: n_args_)
346  os << " " << it.first << " \t" << it.second << std::endl;
347  os << "flags:" << std::endl;
348  for(auto const & it: flags_)
349  os << " " << it << std::endl;
350  os << "special member:" << std::endl;
351  os << " cwd \t" << cwd_ << std::endl;
352  os << " pwd \t" << pwd_ << std::endl;
353  os << " progname \t" << progname_;
354  }
355  private:
356  enum arg_type {named_m_stick, named_m, named_mm, named_eq, free, flag_m, flag_mm, named_m_eq, named_mm_eq, invalid};
357  inline bool inflag_(std::string const & flag) const noexcept {
358  return std::find(flags_.begin(), flags_.end(), flag) != flags_.end();
359  }
360  inline bool innamed_(std::string const & key) const noexcept {
361  return n_args_.find(key) != n_args_.end();
362  }
363  void setflag_(std::string const & flag) {
364  if(inflag_(flag))
365  std::cout << "ArgParser warning: setting flag '" << flag << "' for a second time has no effect" << std::endl;
366  else {
367  if(innamed_(flag))
368  throw cat_on_your_keyboard_error("ArgParser error: there cannot be a flag and named argument with the same name '" + flag + "'");
369  flags_.push_back(flag);
370  }
371  }
372  template<typename T>
373  void setnamed_(std::string const & key, T const & val, bool const & overwrite = true) {
374  if(innamed_(key)) {
375  if(overwrite) {
376  std::cout << "ArgParser warning: named argument '" << key << "' is overwritten (" << n_args_[key] << " -> " << val << ")" << std::endl;
377  n_args_[key] = val;
378  }
379  } else {
380  if(inflag_(key))
381  throw cat_on_your_keyboard_error("ArgParser error: there cannot be a flag and named argument with the same name '" + key + "'");
382  n_args_[key] = val;
383  }
384  }
385  arg_type find_type_(vec_type<std::string> const & arg, uint const & idx) const {
386  if(idx >= arg.size())
387  return invalid;
388 
389  if(arg[idx][0] == '-') { // flag_m, flag_mm, named_m_stick, named_m, named_mm, named_m_eq, named_mm_eq
390  if(arg[idx][1] == '-') { // flag_mm, named_mm, named_mm_eq
391  if(arg[idx].find("=") != std::string::npos)
392  return named_mm_eq;
393  else if(find_type_(arg, idx+1) == free)
394  return named_mm;
395  else
396  return flag_mm;
397  }
398  if(arg[idx].size() == 2) { //flag_m, named_m
399  if(find_type_(arg, idx+1) == free)
400  return named_m;
401  else
402  return flag_m;
403  } else { // named_m_stick, named_m_eq
404  if(arg[idx][2] == '=')
405  return named_m_eq;
406  else
407  return named_m_stick;
408  }
409 
410  } else { // free or named_eq
411  if(arg[idx].find("=") != std::string::npos) // named_eq
412  return named_eq;
413 
414  return free;
415  }
416  }
417  void parse_(vec_type<std::string> const & arg) {
418  for(uint i = 0; i < arg.size(); ++i) {
419  auto t = find_type_(arg, i);
420 
421  std::string arg1 = arg[i];
422  std::string help = "";
423  uint64_t pos;
424 
425  switch(t) {
426  case(named_m_stick):
427  help.push_back(arg1[1]);
428  arg1.erase(0,2);
429  setnamed_(help, str_to_type_(poly_type(arg1)));
430  arg1 = help;
431  break;
432  case(named_mm):
433  arg1.erase(0,1);
434  case(named_m):
435  arg1.erase(0,1);
436  setnamed_(arg1, str_to_type_(poly_type(arg[i+1])));
437  ++i;
438  break;
439  case(named_mm_eq):
440  arg1.erase(0,1);
441  case(named_m_eq):
442  arg1.erase(0,1);
443  case(named_eq):
444  pos = arg1.find("=");
445  help = arg1;
446  help.erase(0, pos + 1);
447  arg1.erase(pos, arg1.size());
448  setnamed_(arg1, str_to_type_(poly_type(help)));
449  break;
450  case(free):
451  f_args_.push_back(str_to_type_(poly_type(arg1)));
452  break;
453  case(flag_mm):
454  arg1.erase(0,1);
455  case(flag_m):
456  arg1.erase(0,1);
457  setflag_(arg1);
458  break;
459  default:
460  break;
461  }
462  }
463  }
464  poly_type str_to_type_(poly_type const & a) {
465  std::string val = a;
466 
467  size_t ipos = 0;
468  int ival = 0;
469  try {
470  ival = std::stoi(val, & ipos);
471  } catch(...) {}
472 
473  size_t dpos = 0;
474  double dval = 0;
475  try {
476  dval = std::stod(val, & dpos);
477  return poly_type(dval);
478  } catch(...) {}
479 
480  if(ipos == val.size())
481  return poly_type(ival);
482  else if(dpos == val.size())
483  return poly_type(dval);
484  else
485  return poly_type(val);
486  }
487  private:
488  map_type n_args_;
489  vec_type<poly_type> f_args_;
490  vec_type<std::string> flags_;
491  std::string cwd_;
492  std::string pwd_;
493  std::string progname_;
494  };
497  std::ostream & operator<<(std::ostream & os, ArgParser const & arg) {
498  arg.print(os);
499  return os;
500  }
501 
503 
505 
507 
508 }//end namespace fsc
509 #endif //FSC_ARGPARSER_HEADER
void print(S &os) const noexcept
prints the argument parser in a verbose form
Definition: ArgParser.hpp:339
std::string const & pwd() const
returns the program working directory
Definition: ArgParser.hpp:210
bool is_set(std::string const &key) const noexcept
check if flag or named argument is set
Definition: ArgParser.hpp:236
std::string const & progname() const
returns the program name
Definition: ArgParser.hpp:220
poly_type const & operator[](std::string const &key) const
get named argument if set
Definition: ArgParser.hpp:132
void def(std::string const &key, T const &def)
set a named argument if not already set
Definition: ArgParser.hpp:257
ArgParser() noexcept
default constructor
Definition: ArgParser.hpp:95
bool is_set(size_type const &pos) const noexcept
check if free argument is set
Definition: ArgParser.hpp:244
std::string const & cwd() const noexcept
returns the current working directory
Definition: ArgParser.hpp:202
void def(std::string const &key)
set a flag
Definition: ArgParser.hpp:267
ArgParser(std::string const &cline)
string constructor
Definition: ArgParser.hpp:121
ArgParser(int const &argc, char *argv[])
construct from command line
Definition: ArgParser.hpp:106
an argument parser that does not require registration.
Definition: ArgParser.hpp:83
bool parse_file(std::string const &filename, bool const &overwrite=true)
read a file, parse the content and merge into this
Definition: ArgParser.hpp:319
poly_type const & operator[](size_type const &key) const
get free argument if set
Definition: ArgParser.hpp:143
void merge(ArgParser const &rhs, bool const &overwrite=true) noexcept
merge two argument parsers
Definition: ArgParser.hpp:289
size_type freeargc() const noexcept
returns the number of free arguments
Definition: ArgParser.hpp:227