45 std::istringstream ss;
52template <>
inline std::string
lexical_cast(
const std::string &str) {
return str; }
57 if (str ==
"false" || str ==
"False" || str ==
"FALSE" || str ==
"0")
62template <
typename T>
inline std::string
to_string(
const T value) {
return std::to_string(value); }
64template <>
inline std::string
to_string(
const char *value) {
return std::string(value); }
66template <>
inline std::string
to_string(
const bool value) {
return value ?
"true" :
"false"; }
76 auto pos = ret.find_first_not_of(
'-');
77 if (pos == std::string::npos)
79 return ret.substr(pos);
87 std::ostringstream oss;
88 std::copy(vec.begin(), std::prev(vec.end()), std::ostream_iterator<std::string>(oss,
", "));
102 static const char *
Get() {
return typeid(T).name(); }
106 static const char *
Get() {
return "int"; }
110 static const char *
Get() {
return "vector<int>"; }
114 static const char *
Get() {
return "float"; }
118 static const char *
Get() {
return "vector<float>"; }
122 static const char *
Get() {
return "bool"; }
126 static const char *
Get() {
return "string"; }
130 static const char *
Get() {
return "vector<string>"; }
134 static const char *
Get() {
return "string"; }
138 static const char *
Get() {
return "vector<string>"; }
173 explicit Argument(
const std::string &arg_name) : _long_name{arg_name}, _names{arg_name} {}
174 explicit Argument(
const std::string &short_name,
const std::string &long_name)
175 : _short_name{short_name}, _long_name{long_name}, _names{short_name, long_name}
178 explicit Argument(
const std::string &short_name,
const std::string &long_name,
179 const std::vector<std::string> &names)
180 : _short_name{short_name}, _long_name{long_name}, _names{names}
183 auto it = std::find(names.begin(), names.end(), short_name);
184 assert(it != names.end());
185 it = std::find(names.begin(), names.end(), long_name);
186 assert(it != names.end());
205 case DataType::INT32:
208 case DataType::INT32_VEC:
209 _type =
"vector<int>";
211 case DataType::FLOAT:
214 case DataType::FLOAT_VEC:
215 _type =
"vector<float>";
223 case DataType::STR_VEC:
224 _type =
"vector<string>";
227 throw std::runtime_error(
"NYI DataType");
240 _is_required = value;
246 _is_accumulated =
true;
252 _is_accumulated = value;
258 _help_message.emplace_back(help_message);
264 if (help_messages.size() == 0)
265 throw std::runtime_error(
"Empty help message list");
267 _help_message = help_messages;
280 (_nargs > 1 &&
TypeName<std::vector<T>>::Get() == _type))
284 throw std::runtime_error(
"Type mismatch. "
285 "You called default_value() method with a type different "
286 "from the one you specified. "
287 "Please check the type of what you specified in "
288 "add_argument() method.");
296 (_nargs > 1 &&
TypeName<std::vector<T>>::Get() == _type))
303 throw std::runtime_error(
"Type mismatch. "
304 "You called default_value() method with a type different "
305 "from the one you specified. "
306 "Please check the type of what you specified in "
307 "add_argument() method.");
315 std::string _short_name;
316 std::string _long_name;
317 std::vector<std::string> _names;
318 std::string _type =
"string";
319 std::vector<std::string> _help_message;
320 std::function<void(
void)> _func;
322 bool _is_required{
false};
323 bool _is_accumulated{
false};
324 std::vector<std::string> _values;
325 std::vector<std::vector<std::string>> _accum_values;
334 explicit Arser(
const std::string &program_description = {})
335 : _program_description{program_description}
342 if (arg_name.at(0) !=
'-')
344 _positional_arg_vec.emplace_back(arg_name);
345 _arg_map[arg_name] = &_positional_arg_vec.back();
351 if (arg_name.size() < 2)
353 throw std::runtime_error(
"Too short name. The length of argument name must be 2 or more.");
355 if (arg_name ==
"--")
357 throw std::runtime_error(
358 "Too short name. Option name must contain at least one character other than dash.");
360 _optional_arg_vec.emplace_back(arg_name);
361 _optional_arg_vec.back()._short_name = arg_name;
362 _arg_map[arg_name] = &_optional_arg_vec.back();
364 return *_arg_map[arg_name];
369 assert(arg_name_vec.size() >= 2);
370 std::string long_opt, short_opt;
372 for (
const auto &arg_name : arg_name_vec)
374 if (arg_name.at(0) !=
'-')
376 throw std::runtime_error(
"Invalid argument. "
377 "Positional argument cannot have short option.");
379 assert(arg_name.size() >= 2);
380 if (long_opt.empty() && arg_name.at(0) ==
'-' && arg_name.at(1) ==
'-')
384 if (short_opt.empty() && arg_name.at(0) ==
'-' && arg_name.at(1) !=
'-')
386 short_opt = arg_name;
390 if (long_opt.empty())
392 assert(not short_opt.empty());
393 long_opt = short_opt;
395 if (short_opt.empty())
397 assert(not long_opt.empty());
398 short_opt = long_opt;
401 _optional_arg_vec.emplace_back(short_opt, long_opt, arg_name_vec);
402 for (
const auto &arg_name : arg_name_vec)
404 _arg_map[arg_name] = &_optional_arg_vec.back();
406 return _optional_arg_vec.back();
411 if (
sizeof...(arg_names) == 0)
418 return add_argument(std::vector<std::string>{arg_name, arg_names...});
425 for (
const auto &arg : _positional_arg_vec)
427 if (arg._is_required)
429 throw std::runtime_error(
"Invalid arguments. Positional argument must always be required.");
439 _program_name = argv[0];
440 _program_name.erase(0, _program_name.find_last_of(
"/\\") + 1);
443 if (!std::strcmp(argv[1],
"--help") || !std::strcmp(argv[1],
"-h"))
450 for (
const auto &arg : _arg_map)
452 const auto &func = arg.second->_func;
453 if (func && !std::strcmp(argv[1], arg.first.c_str()))
465 size_t parg_num = _positional_arg_vec.size();
467 size_t required_oarg_num = 0;
468 for (
auto arg : _optional_arg_vec)
470 if (arg._is_required)
474 for (
int c = 1; c < argc;)
476 std::string arg_name{argv[c++]};
477 auto arg = _arg_map.find(arg_name);
479 if (arg == _arg_map.end())
483 auto it = _positional_arg_vec.begin();
484 std::advance(it, _positional_arg_vec.size() - parg_num);
485 (*it)._values.clear();
486 (*it)._values.emplace_back(arg_name);
490 throw std::runtime_error(
"Invalid argument. "
491 "You've given more positional argument than necessary.");
496 if (arg->second->_is_required)
498 arg->second->_values.clear();
499 for (uint32_t n = 0; n < arg->second->_nargs; n++)
502 throw std::runtime_error(
"Invalid argument. "
503 "You must have missed some argument.");
504 arg->second->_values.emplace_back(argv[c++]);
507 if (arg->second->_is_accumulated)
509 arg->second->_accum_values.emplace_back(arg->second->_values);
511 if (arg->second->_nargs == 0)
514 arg->second->_values.emplace_back(
"1");
518 if (parg_num || required_oarg_num)
519 throw std::runtime_error(
"Invalid argument. "
520 "You must have missed some argument.");
525 auto arg = _arg_map.find(arg_name);
526 if (arg == _arg_map.end())
529 if (arg->second->_is_accumulated)
530 return arg->second->_accum_values.size() > 0 ? true :
false;
532 return arg->second->_values.size() > 0 ? true :
false;
535 template <
typename T> T
get_impl(
const std::string &arg_name, T *);
537 template <
typename T> std::vector<T>
get_impl(
const std::string &arg_name, std::vector<T> *);
539 template <
typename T>
540 std::vector<std::vector<T>>
get_impl(
const std::string &arg_name, std::vector<std::vector<T>> *);
542 template <
typename T> T
get(
const std::string &arg_name);
547 if (!parser._program_description.empty())
549 stream <<
"What " << parser._program_name <<
" does: " << parser._program_description
558 std::for_each(arg_name.begin(), arg_name.end(),
559 [&stream](
const char &c) { stream << static_cast<char>(::toupper(c)); });
561 stream <<
"Usage: ./" << parser._program_name <<
" ";
563 for (
const auto &arg : parser._optional_arg_vec)
565 if (!arg._is_required)
567 stream << arg._short_name;
568 print_usage_arg(arg);
572 for (
const auto &arg : parser._optional_arg_vec)
574 if (arg._is_required)
576 stream <<
"[" << arg._short_name;
579 print_usage_arg(arg);
585 for (
const auto &arg : parser._positional_arg_vec)
587 stream << arg._long_name <<
" ";
594 size_t length_of_longest_arg = 0;
595 for (
const auto &arg : parser._positional_arg_vec)
597 length_of_longest_arg = std::max(length_of_longest_arg,
600 for (
const auto &arg : parser._optional_arg_vec)
602 length_of_longest_arg = std::max(length_of_longest_arg,
606 const size_t message_width = 60;
607 auto print_help_args = [&](
const std::list<Argument> &args,
const std::string &title) {
610 stream << title << std::endl;
611 for (
const auto &arg : args)
613 stream.width(length_of_longest_arg);
615 for (
size_t i = 0; i < arg._help_message.size(); i++)
617 for (
size_t j = 0; j < arg._help_message[i].length(); j += message_width)
620 stream << std::string(length_of_longest_arg,
' ') <<
"\t";
621 stream << arg._help_message[i].substr(j, message_width) << std::endl;
625 std::cout << std::endl;
629 print_help_args(parser._positional_arg_vec,
"[Positional argument]");
631 print_help_args(parser._optional_arg_vec,
"[Optional argument]");
637 std::string _program_name;
638 std::string _program_description;
639 std::list<Argument> _positional_arg_vec;
640 std::list<Argument> _optional_arg_vec;
641 std::map<std::string, Argument *> _arg_map;
646 auto arg = _arg_map.find(arg_name);
647 if (arg == _arg_map.end())
648 throw std::runtime_error(
"Invalid argument. "
649 "There is no argument you are looking for: " +
652 if (arg->second->_is_accumulated)
653 throw std::runtime_error(
655 "You called get using a type different from the one you specified."
656 "Accumulated argument is returned as std::vector of the specified type: " +
660 throw std::runtime_error(
"Type mismatch. "
661 "You called get() method with a type different "
662 "from the one you specified. "
663 "Please check the type of what you specified in "
664 "add_argument() method: " +
667 if (arg->second->_values.size() == 0)
668 throw std::runtime_error(
"Wrong access. "
669 "You must make sure that the argument is given before accessing it. "
670 "You can do it by calling arser[\"" +
673 return internal::lexical_cast<T>(arg->second->_values[0]);
676template <
typename T> std::vector<T>
Arser::get_impl(
const std::string &arg_name, std::vector<T> *)
678 auto arg = _arg_map.find(arg_name);
679 if (arg == _arg_map.end())
680 throw std::runtime_error(
"Invalid argument. "
681 "There is no argument you are looking for: " +
685 if (arg->second->_is_accumulated)
688 throw std::runtime_error(
690 "You called get using a type different from the one you specified: " +
694 for (
auto values : arg->second->_accum_values)
696 assert(values.size() == 1);
697 data.emplace_back(internal::lexical_cast<T>(values[0]));
702 if (arg->second->_type !=
TypeName<std::vector<T>>::Get())
703 throw std::runtime_error(
705 ". You called get using a type different from the one you specified: " +
709 std::transform(arg->second->_values.begin(), arg->second->_values.end(), std::back_inserter(data),
710 [](std::string str) -> T { return internal::lexical_cast<T>(str); });
717 std::vector<std::vector<T>> *)
719 auto arg = _arg_map.find(arg_name);
720 if (arg == _arg_map.end())
721 throw std::runtime_error(
"Invalid argument. "
722 "There is no argument you are looking for: " +
725 if (not arg->second->_is_accumulated)
726 throw std::runtime_error(
"Type mismatch. "
727 "You called get using a type different from the one you specified: " +
730 if (arg->second->_type !=
TypeName<std::vector<T>>::Get())
731 throw std::runtime_error(
733 "You called get using a type different from the one you specified."
734 "Accumulated argument is returned as std::vector of the specified type: " +
737 std::vector<std::vector<T>> result;
738 for (
auto values : arg->second->_accum_values)
741 std::transform(values.begin(), values.end(), std::back_inserter(data),
742 [](std::string str) -> T { return internal::lexical_cast<T>(str); });
743 result.emplace_back(data);
749template <
typename T> T
Arser::get(
const std::string &arg_name)
751 return get_impl(arg_name,
static_cast<T *
>(
nullptr));
759 arser.add_argument(
"--version")
762 .default_value(
false)
763 .help(
"Show version information and exit")
769 arser.add_argument(
"-V",
"--verbose")
772 .default_value(
false)
773 .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()