45 std::istringstream ss;
55 if (str ==
"false" || str ==
"False" || str ==
"FALSE" || str ==
"0")
60template <
typename T>
inline std::string
to_string(
const T value) {
return std::to_string(value); }
62template <>
inline std::string
to_string(
const char *value) {
return std::string(value); }
64template <>
inline std::string
to_string(
const bool value) {
return value ?
"true" :
"false"; }
74 auto pos = ret.find_first_not_of(
'-');
75 if (pos == std::string::npos)
77 return ret.substr(pos);
85 std::ostringstream oss;
86 std::copy(vec.begin(), std::prev(vec.end()), std::ostream_iterator<std::string>(oss,
", "));
100 static const char *
Get() {
return typeid(T).name(); }
104 static const char *
Get() {
return "int"; }
108 static const char *
Get() {
return "vector<int>"; }
112 static const char *
Get() {
return "float"; }
116 static const char *
Get() {
return "vector<float>"; }
120 static const char *
Get() {
return "bool"; }
124 static const char *
Get() {
return "string"; }
128 static const char *
Get() {
return "vector<string>"; }
132 static const char *
Get() {
return "string"; }
136 static const char *
Get() {
return "vector<string>"; }
171 explicit Argument(
const std::string &arg_name) : _long_name{arg_name}, _names{arg_name} {}
172 explicit Argument(
const std::string &short_name,
const std::string &long_name)
173 : _short_name{short_name}, _long_name{long_name}, _names{short_name, long_name}
176 explicit Argument(
const std::string &short_name,
const std::string &long_name,
177 const std::vector<std::string> &names)
178 : _short_name{short_name}, _long_name{long_name}, _names{names}
181 auto it = std::find(names.begin(), names.end(), short_name);
182 assert(it != names.end());
183 it = std::find(names.begin(), names.end(), long_name);
184 assert(it != names.end());
203 case DataType::INT32:
206 case DataType::INT32_VEC:
207 _type =
"vector<int>";
209 case DataType::FLOAT:
212 case DataType::FLOAT_VEC:
213 _type =
"vector<float>";
221 case DataType::STR_VEC:
222 _type =
"vector<string>";
225 throw std::runtime_error(
"NYI DataType");
238 _is_required = value;
244 _is_accumulated =
true;
250 _is_accumulated = value;
256 _help_message.emplace_back(help_message);
262 if (help_messages.size() == 0)
263 throw std::runtime_error(
"Empty help message list");
265 _help_message = help_messages;
278 (_nargs > 1 &&
TypeName<std::vector<T>>::Get() == _type))
282 throw std::runtime_error(
"Type mismatch. "
283 "You called default_value() method with a type different "
284 "from the one you specified. "
285 "Please check the type of what you specified in "
286 "add_argument() method.");
294 (_nargs > 1 &&
TypeName<std::vector<T>>::Get() == _type))
301 throw std::runtime_error(
"Type mismatch. "
302 "You called default_value() method with a type different "
303 "from the one you specified. "
304 "Please check the type of what you specified in "
305 "add_argument() method.");
313 std::string _short_name;
314 std::string _long_name;
315 std::vector<std::string> _names;
316 std::string _type =
"string";
317 std::vector<std::string> _help_message;
318 std::function<void(
void)> _func;
320 bool _is_required{
false};
321 bool _is_accumulated{
false};
322 std::vector<std::string> _values;
323 std::vector<std::vector<std::string>> _accum_values;
332 explicit Arser(
const std::string &program_description = {})
333 : _program_description{program_description}
340 if (arg_name.at(0) !=
'-')
342 _positional_arg_vec.emplace_back(arg_name);
343 _arg_map[arg_name] = &_positional_arg_vec.back();
349 if (arg_name.size() < 2)
351 throw std::runtime_error(
"Too short name. The length of argument name must be 2 or more.");
353 if (arg_name ==
"--")
355 throw std::runtime_error(
356 "Too short name. Option name must contain at least one character other than dash.");
358 _optional_arg_vec.emplace_back(arg_name);
359 _optional_arg_vec.back()._short_name = arg_name;
360 _arg_map[arg_name] = &_optional_arg_vec.back();
362 return *_arg_map[arg_name];
367 assert(arg_name_vec.size() >= 2);
368 std::string long_opt, short_opt;
370 for (
const auto &arg_name : arg_name_vec)
372 if (arg_name.at(0) !=
'-')
374 throw std::runtime_error(
"Invalid argument. "
375 "Positional argument cannot have short option.");
377 assert(arg_name.size() >= 2);
378 if (long_opt.empty() && arg_name.at(0) ==
'-' && arg_name.at(1) ==
'-')
382 if (short_opt.empty() && arg_name.at(0) ==
'-' && arg_name.at(1) !=
'-')
384 short_opt = arg_name;
388 if (long_opt.empty())
390 assert(not short_opt.empty());
391 long_opt = short_opt;
393 if (short_opt.empty())
395 assert(not long_opt.empty());
396 short_opt = long_opt;
399 _optional_arg_vec.emplace_back(short_opt, long_opt, arg_name_vec);
400 for (
const auto &arg_name : arg_name_vec)
402 _arg_map[arg_name] = &_optional_arg_vec.back();
404 return _optional_arg_vec.back();
409 if (
sizeof...(arg_names) == 0)
416 return add_argument(std::vector<std::string>{arg_name, arg_names...});
423 for (
const auto &arg : _positional_arg_vec)
425 if (arg._is_required)
427 throw std::runtime_error(
"Invalid arguments. Positional argument must always be required.");
437 _program_name = argv[0];
438 _program_name.erase(0, _program_name.find_last_of(
"/\\") + 1);
441 if (!std::strcmp(argv[1],
"--help") || !std::strcmp(argv[1],
"-h"))
448 for (
const auto &arg : _arg_map)
450 const auto &func = arg.second->_func;
451 if (func && !std::strcmp(argv[1], arg.first.c_str()))
463 size_t parg_num = _positional_arg_vec.size();
465 size_t required_oarg_num = 0;
466 for (
auto arg : _optional_arg_vec)
468 if (arg._is_required)
472 for (
int c = 1; c < argc;)
474 std::string arg_name{argv[c++]};
475 auto arg = _arg_map.find(arg_name);
477 if (arg == _arg_map.end())
481 auto it = _positional_arg_vec.begin();
482 std::advance(it, _positional_arg_vec.size() - parg_num);
483 (*it)._values.clear();
484 (*it)._values.emplace_back(arg_name);
488 throw std::runtime_error(
"Invalid argument. "
489 "You've given more positional argument than necessary.");
494 if (arg->second->_is_required)
496 arg->second->_values.clear();
497 for (uint32_t n = 0; n < arg->second->_nargs; n++)
500 throw std::runtime_error(
"Invalid argument. "
501 "You must have missed some argument.");
502 arg->second->_values.emplace_back(argv[c++]);
505 if (arg->second->_is_accumulated)
507 arg->second->_accum_values.emplace_back(arg->second->_values);
509 if (arg->second->_nargs == 0)
512 arg->second->_values.emplace_back(
"1");
516 if (parg_num || required_oarg_num)
517 throw std::runtime_error(
"Invalid argument. "
518 "You must have missed some argument.");
523 auto arg = _arg_map.find(arg_name);
524 if (arg == _arg_map.end())
527 if (arg->second->_is_accumulated)
528 return arg->second->_accum_values.size() > 0 ? true :
false;
530 return arg->second->_values.size() > 0 ? true :
false;
533 template <
typename T> T
get_impl(
const std::string &arg_name, T *);
535 template <
typename T> std::vector<T>
get_impl(
const std::string &arg_name, std::vector<T> *);
537 template <
typename T>
538 std::vector<std::vector<T>>
get_impl(
const std::string &arg_name, std::vector<std::vector<T>> *);
540 template <
typename T> T
get(
const std::string &arg_name);
545 if (!parser._program_description.empty())
547 stream <<
"What " << parser._program_name <<
" does: " << parser._program_description
556 std::for_each(arg_name.begin(), arg_name.end(),
557 [&stream](
const char &c) { stream << static_cast<char>(::toupper(c)); });
559 stream <<
"Usage: ./" << parser._program_name <<
" ";
561 for (
const auto &arg : parser._optional_arg_vec)
563 if (!arg._is_required)
565 stream << arg._short_name;
566 print_usage_arg(arg);
570 for (
const auto &arg : parser._optional_arg_vec)
572 if (arg._is_required)
574 stream <<
"[" << arg._short_name;
577 print_usage_arg(arg);
583 for (
const auto &arg : parser._positional_arg_vec)
585 stream << arg._long_name <<
" ";
592 size_t length_of_longest_arg = 0;
593 for (
const auto &arg : parser._positional_arg_vec)
595 length_of_longest_arg = std::max(length_of_longest_arg,
598 for (
const auto &arg : parser._optional_arg_vec)
600 length_of_longest_arg = std::max(length_of_longest_arg,
604 const size_t message_width = 60;
605 auto print_help_args = [&](
const std::list<Argument> &args,
const std::string &title) {
608 stream << title << std::endl;
609 for (
const auto &arg : args)
611 stream.width(length_of_longest_arg);
613 for (
size_t i = 0; i < arg._help_message.size(); i++)
615 for (
size_t j = 0; j < arg._help_message[i].length(); j += message_width)
618 stream << std::string(length_of_longest_arg,
' ') <<
"\t";
619 stream << arg._help_message[i].substr(j, message_width) << std::endl;
623 std::cout << std::endl;
627 print_help_args(parser._positional_arg_vec,
"[Positional argument]");
629 print_help_args(parser._optional_arg_vec,
"[Optional argument]");
635 std::string _program_name;
636 std::string _program_description;
637 std::list<Argument> _positional_arg_vec;
638 std::list<Argument> _optional_arg_vec;
639 std::map<std::string, Argument *> _arg_map;
644 auto arg = _arg_map.find(arg_name);
645 if (arg == _arg_map.end())
646 throw std::runtime_error(
"Invalid argument. "
647 "There is no argument you are looking for: " +
650 if (arg->second->_is_accumulated)
651 throw std::runtime_error(
653 "You called get using a type different from the one you specified."
654 "Accumulated argument is returned as std::vector of the specified type: " +
658 throw std::runtime_error(
"Type mismatch. "
659 "You called get() method with a type different "
660 "from the one you specified. "
661 "Please check the type of what you specified in "
662 "add_argument() method: " +
665 if (arg->second->_values.size() == 0)
666 throw std::runtime_error(
"Wrong access. "
667 "You must make sure that the argument is given before accessing it. "
668 "You can do it by calling arser[\"" +
671 return internal::lexical_cast<T>(arg->second->_values[0]);
674template <
typename T> std::vector<T>
Arser::get_impl(
const std::string &arg_name, std::vector<T> *)
676 auto arg = _arg_map.find(arg_name);
677 if (arg == _arg_map.end())
678 throw std::runtime_error(
"Invalid argument. "
679 "There is no argument you are looking for: " +
683 if (arg->second->_is_accumulated)
686 throw std::runtime_error(
688 "You called get using a type different from the one you specified: " +
692 for (
auto values : arg->second->_accum_values)
694 assert(values.size() == 1);
695 data.emplace_back(internal::lexical_cast<T>(values[0]));
700 if (arg->second->_type !=
TypeName<std::vector<T>>::Get())
701 throw std::runtime_error(
703 ". You called get using a type different from the one you specified: " +
707 std::transform(arg->second->_values.begin(), arg->second->_values.end(), std::back_inserter(data),
708 [](std::string str) -> T { return internal::lexical_cast<T>(str); });
715 std::vector<std::vector<T>> *)
717 auto arg = _arg_map.find(arg_name);
718 if (arg == _arg_map.end())
719 throw std::runtime_error(
"Invalid argument. "
720 "There is no argument you are looking for: " +
723 if (not arg->second->_is_accumulated)
724 throw std::runtime_error(
"Type mismatch. "
725 "You called get using a type different from the one you specified: " +
728 if (arg->second->_type !=
TypeName<std::vector<T>>::Get())
729 throw std::runtime_error(
731 "You called get using a type different from the one you specified."
732 "Accumulated argument is returned as std::vector of the specified type: " +
735 std::vector<std::vector<T>> result;
736 for (
auto values : arg->second->_accum_values)
739 std::transform(values.begin(), values.end(), std::back_inserter(data),
740 [](std::string str) -> T { return internal::lexical_cast<T>(str); });
741 result.emplace_back(data);
747template <
typename T> T
Arser::get(
const std::string &arg_name)
749 return get_impl(arg_name,
static_cast<T *
>(
nullptr));
757 arser.add_argument(
"--version")
760 .default_value(
false)
761 .help(
"Show version information and exit")
767 arser.add_argument(
"-V",
"--verbose")
770 .default_value(
false)
771 .help(
"output additional information to stdout or stderr");
Argument & type(DataType type)
Argument(const std::string &arg_name)
Argument & help(std::initializer_list< std::string > help_messages)
Argument & default_value(const T value)
Argument & exit_with(const std::function< void(void)> &func)
Argument & nargs(uint32_t num)
Argument & help(std::string help_message)
Argument(const std::string &short_name, const std::string &long_name, const std::vector< std::string > &names)
Argument & accumulated(bool value)
Argument & default_value(const T value, const Ts... values)
Argument & accumulated(void)
friend std::ostream & operator<<(std::ostream &, const Arser &)
Argument & required(bool value)
Argument(const std::string &short_name, const std::string &long_name)
Argument & required(void)
void validate_arguments(void)
Arser(const std::string &program_description={})
bool operator[](const std::string &arg_name)
friend std::ostream & operator<<(std::ostream &stream, const Arser &parser)
T get_impl(const std::string &arg_name, T *)
Argument & add_argument(const std::string &arg_name, Ts... arg_names)
T get(const std::string &arg_name)
Argument & add_argument(const std::string &arg_name)
Argument & add_argument(const std::vector< std::string > &arg_name_vec)
void parse(int argc, char **argv)
static void add_version(Arser &arser, const std::function< void(void)> &func)
static void add_verbose(Arser &arser)
std::string to_string(const T value)
std::string make_comma_concatenated(const std::vector< std::string > &vec)
Returns the string that created by concatenating the elements of a vector with commas.
T lexical_cast(const std::string &str)
std::string remove_dash(const std::string &str)
Returns the string with the leading dash removed.
static const char * Get()
static const char * Get()
static const char * Get()
static const char * Get()
static const char * Get()
static const char * Get()
static const char * Get()
static const char * Get()
static const char * Get()
static const char * Get()