2 * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
35 template <typename T> T lexical_cast(const std::string &str)
37 std::istringstream ss;
44 template <> bool lexical_cast(const std::string &str)
47 if (str == "false" || str == "False" || str == "FALSE" || str == "0")
52 template <typename T> inline std::string to_string(const T value) { return std::to_string(value); }
54 template <> inline std::string to_string(const char *value) { return std::string(value); }
56 template <> inline std::string to_string(const bool value) { return value ? "true" : "false"; }
63 // TypeName declaration
64 template <typename T> struct TypeName
66 static const char *Get() { return typeid(T).name(); }
68 template <> struct TypeName<int>
70 static const char *Get() { return "int"; }
72 template <> struct TypeName<std::vector<int>>
74 static const char *Get() { return "vector<int>"; }
76 template <> struct TypeName<float>
78 static const char *Get() { return "float"; }
80 template <> struct TypeName<std::vector<float>>
82 static const char *Get() { return "vector<float>"; }
84 template <> struct TypeName<bool>
86 static const char *Get() { return "bool"; }
88 template <> struct TypeName<std::string>
90 static const char *Get() { return "string"; }
92 template <> struct TypeName<std::vector<std::string>>
94 static const char *Get() { return "vector<string>"; }
96 template <> struct TypeName<const char *>
98 static const char *Get() { return "string"; }
100 template <> struct TypeName<std::vector<const char *>>
102 static const char *Get() { return "vector<string>"; }
105 // supported DataType
122 explicit Argument(const std::string &arg_name) : _name{arg_name} {}
124 Argument &nargs(uint32_t num)
134 Argument &type(DataType type)
138 case DataType::INT32:
141 case DataType::INT32_VEC:
142 _type = "vector<int>";
144 case DataType::FLOAT:
147 case DataType::FLOAT_VEC:
148 _type = "vector<float>";
156 case DataType::STR_VEC:
157 _type = "vector<string>";
160 throw std::runtime_error("NYI DataType");
165 Argument &required(void)
171 Argument &required(bool value)
173 _is_required = value;
177 Argument &help(std::string help_message)
179 _help_message = help_message;
183 Argument &exit_with(const std::function<void(void)> &func)
189 template <typename T> Argument &default_value(const T value)
191 if ((_nargs <= 1 && TypeName<T>::Get() == _type) ||
192 (_nargs > 1 && TypeName<std::vector<T>>::Get() == _type))
193 _values.emplace_back(::to_string(value));
196 throw std::runtime_error("Type mismatch. "
197 "You called default_value() method with a type different "
198 "from the one you specified. "
199 "Please check the type of what you specified in "
200 "add_argument() method.");
205 template <typename T, typename... Ts> Argument &default_value(const T value, const Ts... values)
207 if ((_nargs <= 1 && TypeName<T>::Get() == _type) ||
208 (_nargs > 1 && TypeName<std::vector<T>>::Get() == _type))
210 _values.emplace_back(::to_string(value));
211 default_value(values...);
215 throw std::runtime_error("Type mismatch. "
216 "You called default_value() method with a type different "
217 "from the one you specified. "
218 "Please check the type of what you specified in "
219 "add_argument() method.");
227 std::string _help_message;
228 std::function<void(void)> _func;
230 bool _is_required{false};
231 std::vector<std::string> _values;
234 friend std::ostream &operator<<(std::ostream &, const Arser &);
240 explicit Arser(const std::string &program_description = {})
241 : _program_description{program_description}
243 add_argument("--help").help("Show help message and exit").nargs(0);
246 Argument &add_argument(const std::string &arg_name)
248 if (arg_name.at(0) != '-')
250 _positional_arg_vec.emplace_back(arg_name);
251 _arg_map[arg_name] = &_positional_arg_vec.back();
255 _optional_arg_vec.emplace_back(arg_name);
256 _arg_map[arg_name] = &_optional_arg_vec.back();
258 return *_arg_map[arg_name];
261 void parse(int argc, char **argv)
263 _program_name = argv[0];
264 _program_name.erase(0, _program_name.find_last_of("/\\") + 1);
267 if (!std::strcmp(argv[1], "--help"))
274 for (const auto &arg : _arg_map)
276 const auto &func = arg.second->_func;
277 if (func && !std::strcmp(argv[1], arg.second->_name.c_str()))
286 ** ./program_name [optional argument] [positional argument]
288 // get the number of positioanl argument
289 size_t parg_num = _positional_arg_vec.size();
290 // get the number of "required" optional argument
291 size_t required_oarg_num = 0;
292 for (auto arg : _optional_arg_vec)
294 if (arg._is_required)
298 for (int c = 1; c < argc;)
300 std::string arg_name{argv[c++]};
301 auto arg = _arg_map.find(arg_name);
302 // check whether arg is positional or not
303 if (arg == _arg_map.end())
307 auto it = _positional_arg_vec.begin();
308 std::advance(it, _positional_arg_vec.size() - parg_num);
309 (*it)._values.clear();
310 (*it)._values.emplace_back(arg_name);
314 throw std::runtime_error("Invalid argument. "
315 "You've given more positional argument than necessary.");
317 else // optional argument
319 // check whether arg is required or not
320 if (arg->second->_is_required)
322 arg->second->_values.clear();
323 for (uint32_t n = 0; n < arg->second->_nargs; n++)
326 throw std::runtime_error("Invalid argument. "
327 "You must have missed some argument.");
328 arg->second->_values.emplace_back(argv[c++]);
330 if (arg->second->_nargs == 0)
332 // TODO std::boolalpha for true or false
333 arg->second->_values.emplace_back("1");
337 if (parg_num || required_oarg_num)
338 throw std::runtime_error("Invalid argument. "
339 "You must have missed some argument.");
342 bool operator[](const std::string &arg_name)
344 auto arg = _arg_map.find(arg_name);
345 if (arg == _arg_map.end())
348 return arg->second->_values.size() > 0 ? true : false;
351 template <typename T> T get_impl(const std::string &arg_name, T *);
353 template <typename T> std::vector<T> get_impl(const std::string &arg_name, std::vector<T> *);
355 template <typename T> T get(const std::string &arg_name);
358 std::string _program_name;
359 std::string _program_description;
360 std::list<Argument> _positional_arg_vec;
361 std::list<Argument> _optional_arg_vec;
362 std::map<std::string, Argument *> _arg_map;
364 friend std::ostream &operator<<(std::ostream &, const Arser &);
367 template <typename T> T Arser::get_impl(const std::string &arg_name, T *)
369 auto arg = _arg_map.find(arg_name);
370 if (arg == _arg_map.end())
371 throw std::runtime_error("Invalid argument. "
372 "There is no argument you are looking for.");
374 if (arg->second->_type != TypeName<T>::Get())
375 throw std::runtime_error("Type mismatch. "
376 "You called get() method with a type different "
377 "from the one you specified. "
378 "Please check the type of what you specified in "
379 "add_argument() method.");
381 if (arg->second->_values.size() == 0)
382 throw std::runtime_error("Wrong access. "
383 "You must make sure that the argument is given before accessing it. "
384 "You can do it by calling arser[\"argument\"].");
386 return ::lexical_cast<T>(arg->second->_values[0]);
389 template <typename T> std::vector<T> Arser::get_impl(const std::string &arg_name, std::vector<T> *)
391 auto arg = _arg_map.find(arg_name);
392 if (arg == _arg_map.end())
393 throw std::runtime_error("Invalid argument. "
394 "There is no argument you are looking for.");
396 if (arg->second->_type != TypeName<std::vector<T>>::Get())
397 throw std::runtime_error("Type mismatch. "
398 "You called get using a type different from the one you specified.");
401 std::transform(arg->second->_values.begin(), arg->second->_values.end(), std::back_inserter(data),
402 [](std::string str) -> T { return ::lexical_cast<T>(str); });
406 template <typename T> T Arser::get(const std::string &arg_name)
408 return get_impl(arg_name, static_cast<T *>(nullptr));
411 std::ostream &operator<<(std::ostream &stream, const Arser &parser)
414 if (!parser._program_description.empty())
416 stream << "What " << parser._program_name << " does: " << parser._program_description << "\n\n";
421 stream << "Usage: ./" << parser._program_name << " ";
422 // required optional argument
423 for (const auto &arg : parser._optional_arg_vec)
425 if (!arg._is_required)
427 stream << arg._name << " ";
428 std::string arg_name = arg._name.substr(2);
429 std::for_each(arg_name.begin(), arg_name.end(),
430 [&stream](const char &c) { stream << static_cast<char>(::toupper(c)); });
433 // rest of the optional argument
434 for (const auto &arg : parser._optional_arg_vec)
436 if (arg._is_required)
438 stream << "[" << arg._name;
442 std::string arg_name = arg._name.substr(2);
443 std::for_each(arg_name.begin(), arg_name.end(),
444 [&stream](const char &c) { stream << static_cast<char>(::toupper(c)); });
449 // positional arguement
450 for (const auto &arg : parser._positional_arg_vec)
452 stream << arg._name << " ";
456 ** print argument list and its help message
458 // get the length of the longest argument
459 size_t length_of_longest_arg = 0;
460 for (const auto &arg : parser._positional_arg_vec)
462 length_of_longest_arg = std::max(length_of_longest_arg, arg._name.length());
464 for (const auto &arg : parser._optional_arg_vec)
466 length_of_longest_arg = std::max(length_of_longest_arg, arg._name.length());
469 const size_t message_width = 60;
470 // positional argument
471 if (!parser._positional_arg_vec.empty())
473 stream << "[Positional argument]" << std::endl;
474 for (const auto &arg : parser._positional_arg_vec)
476 stream.width(length_of_longest_arg);
477 stream << std::left << arg._name << "\t";
478 for (size_t i = 0; i < arg._help_message.length(); i += message_width)
481 stream << std::string(length_of_longest_arg, ' ') << "\t";
482 stream << arg._help_message.substr(i, message_width) << std::endl;
485 std::cout << std::endl;
488 if (!parser._optional_arg_vec.empty())
490 stream << "[Optional argument]" << std::endl;
491 for (const auto &arg : parser._optional_arg_vec)
493 stream.width(length_of_longest_arg);
494 stream << std::left << arg._name << "\t";
495 for (size_t i = 0; i < arg._help_message.length(); i += message_width)
498 stream << std::string(length_of_longest_arg, ' ') << "\t";
499 stream << arg._help_message.substr(i, message_width) << std::endl;