ʻOhana
Population structure, admixture history, and selection using learning methods.
jade.args.hpp
1 /* -------------------------------------------------------------------------
2  Ohana
3  Copyright (c) 2015-2020 Jade Cheng (\___/)
4  Jade Cheng <info@jade-cheng.com> (='.'=)
5  ------------------------------------------------------------------------- */
6 
7 #ifndef JADE_ARGS_HPP__
8 #define JADE_ARGS_HPP__
9 
10 #include "jade.error.hpp"
11 
12 namespace jade
13 {
14  ///
15  /// A template for a class that helps process command-line arguments.
16  ///
17  template <typename TChar>
18  class basic_args
19  {
20  public:
21  typedef TChar char_type; ///< The character type.
22 
23  ///
24  /// Initializes a new instance of the class based on the specified
25  /// arguments. The first argument is ignored.
26  ///
27  /// \param values The command line arguments.
28  ///
29  template <typename TValue>
30  explicit basic_args(const std::initializer_list<TValue> & values)
31  : _m ()
32  #ifndef NDEBUG
33  , is_flag_read (false)
34  #endif // NDEBUG
35  {
36  auto iter = values.begin();
37 
38  if (iter == values.end())
39  throw error("invalid command-line arguments");
40 
41  while (++iter != values.end())
42  {
43  if (*iter == nullptr)
44  throw error("invalid command-line arguments");
45 
46  _m.emplace_back(*iter);
47  }
48  }
49 
50  ///
51  /// Initializes a new instance of the class based on the specified
52  /// arguments. The first argument is ignored.
53  ///
55  const int argc, ///< The argument count.
56  const char_type * argv[]) ///< The argument values.
57  : _m ()
58  #ifndef NDEBUG
59  , is_flag_read (false)
60  #endif // NDEBUG
61  {
62  if (argc < 1 || argv == nullptr)
63  throw error("invalid command-line arguments");
64 
65  for (auto i = 1; i < argc; i++)
66  {
67  const auto s = argv[i];
68  if (s == nullptr)
69  throw error("invalid command-line arguments");
70 
71  _m.emplace_back(s);
72  }
73  }
74 
75  ///
76  /// \return The number of arguments remaining to be processed.
77  ///
78  inline size_t get_length() const
79  {
80  return _m.size();
81  }
82 
83  ///
84  /// \return True if there are no more arguments to be processed.
85  ///
86  inline bool is_empty() const
87  {
88  return _m.empty();
89  }
90 
91  ///
92  /// \return The next argument from the stack or throws an exception if
93  /// there are no more arguments to be processed.
94  ///
95  template <typename TValue>
96  TValue pop()
97  {
98  if (_m.empty())
99  throw error("not enough arguments");
100 
101  const auto value = _parse<TValue>(0);
102  _m.erase(_m.begin());
103  return value;
104  }
105 
106  ///
107  /// Reads and removes an option with one argument. If the option was
108  /// not specified, then the fallback value is returned.
109  ///
110  /// \param long_name The long option name.
111  /// \param short_name The short option name.
112  /// \param fallback The optional fallback value.
113  ///
114  /// \return The value after the flag or the fallback value.
115  ///
116  template <typename TValue>
117  TValue read(
118  char_type const * const long_name,
119  char_type const * const short_name,
120  const TValue fallback = TValue())
121  {
122  assert(!is_flag_read);
123 
124  const auto index = _find(long_name, short_name);
125  if (index < 0)
126  return fallback;
127 
128  if (index + 1 >= int(get_length()))
129  throw error() << "missing argument for option " << long_name;
130 
131  const auto value = _parse<TValue>(size_t(index + 1));
132  _m.erase(_m.begin() + index, _m.begin() + index + 2);
133  return value;
134  }
135 
136  ///
137  /// Reads and removes an option with no arguments. If the options was
138  /// not specified, then false is returned.
139  ///
140  /// \return True if the flag is found; otherwise, false.
141  ///
142  bool read_flag(
143  char_type const * const long_name, ///< The long option name.
144  char_type const * const short_name) ///< The short option name.
145  {
146  const auto index = _find(long_name, short_name);
147  if (index < 0)
148  return false;
149 
150  #ifndef NDEBUG
151  is_flag_read = true;
152  #endif // NDEBUG
153 
154  _m.erase(_m.begin() + index);
155  return true;
156  }
157 
158  ///
159  /// Throws an exception if there are more arguments to be processed.
160  ///
161  void validate_empty() const
162  {
163  if (_m.empty())
164  return;
165 
166  if (_m[0][0] == '-')
167  throw error() << "unexpected option " << _m[0];
168 
169  throw error() << "unexpected argument '" << _m[0] << "'";
170  }
171 
172  ///
173  /// Throws an exception if the number of remaining arguments is not the
174  /// expected number.
175  ///
177  const size_t length) ///< The number of remaining arguments.
178  const
179  {
180  if (_m.size() != length)
181  throw error("invalid syntax; try --help");
182  }
183 
184  private:
185  typedef std::basic_string<char_type> string_type;
186  typedef std::vector<string_type> vector_type;
187 
188  vector_type _m;
189 
190  #ifndef NDEBUG
191  bool is_flag_read;
192  #endif // NDEBUG
193 
194  // --------------------------------------------------------------------
195  int _find(
196  char_type const * const long_name,
197  char_type const * const short_name)
198  {
199  const auto n = get_length();
200 
201  for (size_t i = 0; i < n; i++)
202  {
203  if (!_match(long_name, short_name, i))
204  continue;
205 
206  for (size_t j = i + 1; j < n; j++)
207  if (_match(long_name, short_name, j))
208  throw error() << "duplicate option for " << long_name;
209 
210  return int(i);
211  }
212 
213  return -1;
214  }
215 
216  // --------------------------------------------------------------------
217  bool _match(
218  char_type const * const long_name,
219  char_type const * const short_name,
220  const size_t index)
221  {
222  const auto & s = _m[index];
223  return s == long_name || s == short_name;
224  }
225 
226  // --------------------------------------------------------------------
227  template <typename TValue>
228  TValue _parse(const size_t index)
229  {
230  const auto & s = _m[index];
231 
232  typedef std::basic_istringstream<char_type> stream_type;
233  stream_type in (s);
234 
235  TValue value;
236  if (!(in >> value))
237  throw error() << "invalid argument '" << s << "'";
238 
239  return value;
240  }
241  };
242 
243  ///
244  /// A class that helps process command-line arguments.
245  ///
246  typedef basic_args<char> args;
247 }
248 
249 #endif // JADE_ARGS_HPP__
jade::basic_args::validate_empty
void validate_empty() const
Throws an exception if there are more arguments to be processed.
Definition: jade.args.hpp:161
jade::basic_args::char_type
TChar char_type
The character type.
Definition: jade.args.hpp:21
jade::basic_args::read_flag
bool read_flag(char_type const *const long_name, char_type const *const short_name)
Reads and removes an option with no arguments. If the options was not specified, then false is return...
Definition: jade.args.hpp:142
jade::basic_args::basic_args
basic_args(const int argc, const char_type *argv[])
Initializes a new instance of the class based on the specified arguments. The first argument is ignor...
Definition: jade.args.hpp:54
jade::basic_args::basic_args
basic_args(const std::initializer_list< TValue > &values)
Initializes a new instance of the class based on the specified arguments. The first argument is ignor...
Definition: jade.args.hpp:30
jade::basic_args::get_length
size_t get_length() const
Definition: jade.args.hpp:78
jade::basic_args::is_empty
bool is_empty() const
Definition: jade.args.hpp:86
jade::basic_args::pop
TValue pop()
Definition: jade.args.hpp:96
jade::basic_args::read
TValue read(char_type const *const long_name, char_type const *const short_name, const TValue fallback=TValue())
Reads and removes an option with one argument. If the option was not specified, then the fallback val...
Definition: jade.args.hpp:117
jade::basic_args
A template for a class that helps process command-line arguments.
Definition: jade.args.hpp:19
jade::basic_args::validate_length
void validate_length(const size_t length) const
Throws an exception if the number of remaining arguments is not the expected number.
Definition: jade.args.hpp:176
jade::basic_error
A template for a class representing an exception thrown from this namespace.
Definition: jade.error.hpp:20