25#ifndef TIMEMORY_UTILITY_ARGPARSE_CPP_
26#define TIMEMORY_UTILITY_ARGPARSE_CPP_
30#if !defined(TIMEMORY_UTILITY_HEADER_MODE)
43 for(
int i = 0; i < argc; ++i)
52 for(
int i = 0; i < argc; ++i)
60 for(
int i = 0; i < argc; ++i)
67 std::stringstream cmdss;
69 _end = std::min<size_t>(size(), _end);
71 auto _argc = (_end - _beg) + _prepend.size();
74 char** _argv =
new char*[_argc + 1];
77 for(
size_t i = 0; i < _argc + 1; ++i)
82 for(
const auto& itr : _prepend)
86 for(
auto i = _beg; i < _end; ++i)
90 assert(_argv[_argc] ==
nullptr);
93 for(
size_t i = 0; i < _argc; ++i)
94 cmdss <<
" " << _argv[i];
95 auto cmd = cmdss.str().substr(1);
111 std::stringstream _usage;
113 _usage <<
"[" << m_desc <<
"] ";
114 _usage <<
"Usage: " << m_bin;
116 std::cerr << _usage.str();
118 std::stringstream _sshort_desc;
119 auto _indent = _usage.str().length() + 2;
121 for(
auto& a : m_arguments)
124 if(name.empty() || name.find_first_of(
'-') > name.find_first_not_of(
" -"))
127 for(
size_t n = 1; n < a.m_names.size(); ++n)
129 if(name.find(
"--") == 0)
131 else if(a.m_names.at(n).find(
"--") == 0)
133 name = a.m_names.at(n);
137 if(name.length() > 0)
140 _sshort_desc <<
"\n " << std::setw(_indent) <<
" " << name;
142 _sshort_desc <<
" " << name;
144 _sshort_desc <<
" (";
145 if(a.m_count != argument::Count::ANY)
146 _sshort_desc <<
"count: " << a.m_count;
147 else if(a.m_min_count != argument::Count::ANY)
148 _sshort_desc <<
"min: " << a.m_min_count;
149 else if(a.m_max_count != argument::Count::ANY)
150 _sshort_desc <<
"max: " << a.m_max_count;
152 _sshort_desc <<
"count: unlimited";
153 if(!a.m_dtype.empty())
154 _sshort_desc <<
", dtype: " << a.m_dtype;
155 else if(a.m_count == 0 ||
156 (a.m_count == argument::Count::ANY && a.m_max_count == 1))
157 _sshort_desc <<
", dtype: bool" << a.m_dtype;
163 if(!_sshort_desc.str().empty())
165 _short_desc.append(
"[" + _sshort_desc.str());
166 std::stringstream _tmp;
167 _tmp <<
"\n" << std::setw(_indent) <<
"]";
168 _short_desc.append(_tmp.str());
171 if(m_positional_arguments.empty())
173 std::cerr <<
" " << _short_desc <<
" " << _extra << std::endl;
177 std::cerr <<
" " << _short_desc;
178 if(!_short_desc.empty())
179 std::cerr <<
"\n" << std::setw(_indent - 2) <<
" ";
180 for(
auto& itr : m_positional_arguments)
182 std::cerr <<
" " << helpers::ltrim(itr.m_names.at(0), [](
int c) ->
bool {
183 return c != static_cast<int>(
'-');
188 for(
auto& v : m_positional_map)
190 if(v.first != argument::Position::LastArgument)
192 for(; current < v.first; ++current)
193 std::cerr <<
" [" << current <<
"]";
196 m_arguments[
static_cast<size_t>(v.second)].m_names.at(0),
197 [](
int c) ->
bool { return c != static_cast<int>(
'-'); })
202 std::cerr <<
" ... ["
204 m_arguments[
static_cast<size_t>(v.second)].m_names.at(0),
205 [](
int c) ->
bool { return c != static_cast<int>(
'-'); })
209 std::cerr <<
" " << _extra << std::endl;
212 std::cerr <<
"\nOptions:" << std::endl;
213 for(
auto& a : m_arguments)
216 for(
size_t n = 1; n < a.m_names.size(); ++n)
217 name.append(
", " + a.m_names[n]);
218 std::stringstream ss;
220 if(a.m_choices.size() > 0)
223 auto itr = a.m_choices.begin();
225 for(; itr != a.m_choices.end(); ++itr)
229 std::stringstream prefix;
230 prefix <<
" " << std::setw(m_width) << std::left << ss.str();
231 std::cerr << std::left << prefix.str();
233 auto desc = a.m_desc;
234 if(ss.str().length() >=
static_cast<size_t>(m_width))
240 const auto npos = std::string::npos;
242 std::stringstream indent;
243 indent << std::setw(prefix.str().length()) <<
"";
244 while((
pos = desc.find(indent_key)) != npos)
245 desc = desc.replace(
pos, indent_key.length(), indent.str());
251 const auto npos = std::string::npos;
253 std::stringstream indent;
254 indent << std::setw(m_width + 5) <<
"";
255 while((
pos = desc.find(indent_key)) != npos)
256 desc = desc.replace(
pos, indent_key.length(), indent.str());
259 std::cerr <<
" " << std::setw(m_width) << desc;
262 std::cerr <<
" (Required)";
263 std::cerr << std::endl;
273 auto help_check = [&](
int _argc,
char** _argv) {
274 strset_t help_args = {
"-h",
"--help",
"-?" };
275 auto _help_req = (
exists(
"help") ||
276 (_argc > 1 && help_args.find(_argv[1]) != help_args.end()));
277 if(_help_req && !
exists(
"help"))
279 for(
auto hitr : help_args)
281 auto hstr = hitr.substr(hitr.find_first_not_of(
'-'));
282 auto itr = m_name_map.find(hstr);
283 if(itr != m_name_map.end())
284 m_arguments[
static_cast<size_t>(itr->second)].m_found =
true;
292 for(
int i = 1; i < *argc; ++i)
300 auto _perrc = std::get<0>(_pargs);
301 auto _pargc = std::get<1>(_pargs);
302 auto _pargv = std::get<2>(_pargs);
305 if(help_check((_pdash) ? 0 : _pargc, _pargv))
329 for(
int i = 1; i < argc; ++i)
335 _cmdv =
new char*[_cmdc + 1];
336 _cmdv[_cmdc] =
nullptr;
339 for(
int j = i + 1; j < argc; ++j, ++k)
349 auto cmd_string = [](
int _ac,
char** _av) {
350 std::stringstream ss;
351 for(
int i = 0; i < _ac; ++i)
356 if((_cmdc > 0 && verbose_level > 0) || verbose_level > 1)
359 if(verbose_level > 1)
361 std::cerr <<
"[original]> " << cmd_string(argc,
argv) << std::endl;
362 std::cerr <<
"[cfg-args]> ";
363 for(
auto& itr :
_args)
364 std::cerr << itr <<
" ";
365 std::cerr << std::endl;
368 if(_cmdc > 0 && verbose_level > 0)
369 std::cerr <<
"[command]> " << cmd_string(_cmdc, _cmdv) <<
"\n\n";
377 if(verbose_level > 0)
379 std::cerr <<
"[argparse::parse]> parsing '";
380 for(
const auto& itr :
_args)
381 std::cerr << itr <<
" ";
382 std::cerr <<
"'" <<
'\n';
385 for(
auto& a : m_arguments)
386 a.m_callback(a.m_default);
387 for(
auto& a : m_positional_arguments)
388 a.m_callback(a.m_default);
390 using argmap_t = std::map<std::string, argument*>;
392 argmap_t m_arg_map = {};
394 int argc =
_args.size();
397 std::set<std::string> long_short_opts;
400 auto is_leading_dash = [](
int c) ->
bool {
return c !=
static_cast<int>(
'-'); };
402 for(
auto& a : m_arguments)
404 for(
auto& n : a.m_names)
406 auto nleading_dash = helpers::lcount(n, is_leading_dash);
407 std::string name = helpers::ltrim(n, is_leading_dash);
410 if(m_name_map.find(name) != m_name_map.end())
411 return arg_result(
"Duplicate of argument name: " + n);
412 m_name_map[name] = a.m_index;
413 m_arg_map[name] = &a;
414 if(nleading_dash == 1 && name.length() > 1)
415 long_short_opts.insert(name);
417 if(a.m_position >= 0 || a.m_position == argument::Position::LastArgument)
418 m_positional_map.at(a.m_position) = a.m_index;
426 for(
int argv_index = 1; argv_index < argc; ++argv_index)
428 current_arg =
_args.at(argv_index);
429 arg_len = current_arg.length();
432 if(argv_index == argc - 1 &&
433 m_positional_map.find(argument::Position::LastArgument) !=
434 m_positional_map.end())
436 err = end_argument();
438 err = add_value(current_arg, argument::Position::LastArgument);
443 return (m_error_func(*
this, err), err);
448 auto nleading_dash = helpers::lcount(current_arg, is_leading_dash);
451 bool is_arg = (nleading_dash > 0 && arg_len > 1 && arg_len != nleading_dash)
455 if(is_arg && !helpers::is_numeric(current_arg))
457 err = end_argument();
459 return (m_error_func(*
this, err), err);
461 auto name = current_arg.substr(nleading_dash);
462 auto islong = (nleading_dash > 1 || long_short_opts.count(name) > 0);
463 err = begin_argument(name, islong, argv_index);
465 return (m_error_func(*
this, err), err);
467 else if(current_arg.length() > 0)
470 err = add_value(current_arg, argv_index);
472 return (m_error_func(*
this, err), err);
478 if(m_help_enabled &&
exists(
"help"))
481 err = end_argument();
483 return (m_error_func(*
this, err), err);
486 for(
auto& a : m_arguments)
488 if(a.m_required && !a.m_found)
490 return arg_result(
"Required argument not found: " + a.m_names.at(0));
492 if(a.m_position >= 0 && argc >= a.m_position && !a.m_found)
494 return arg_result(
"argument " + a.m_names.at(0) +
" expected in position " +
495 std::to_string(a.m_position));
500 for(
auto& a : m_positional_arguments)
502 if(a.m_required && !a.m_found)
503 return arg_result(
"Required argument not found: " + a.m_names.at(0));
507 for(
auto& a : m_arguments)
509 if(a.m_found && a.m_default ==
nullptr)
511 auto cnt_err = check_count(a);
518 for(
auto& itr : m_actions)
525 for(
auto& itr : m_arg_map)
528 itr.second->execute_actions(*
this);
535 argument_parser::begin_argument(
const std::string& arg,
bool longarg,
int position)
537 auto it = m_positional_map.find(position);
538 if(it != m_positional_map.end())
540 arg_result err = end_argument();
541 argument& a = m_arguments[
static_cast<size_t>(it->second)];
542 a.m_values.push_back(arg);
548 return arg_result(
"Current argument left open");
550 size_t name_end = helpers::find_punct(arg);
554 int equal_pos = helpers::find_equiv(arg);
555 auto nmf = m_name_map.find(arg_name);
556 if(nmf == m_name_map.end())
558 arg_name = arg.substr(0, equal_pos);
559 nmf = m_name_map.find(arg_name);
561 if(nmf == m_name_map.end())
563 return arg_result(
"Unrecognized command line option '" + arg_name +
"'");
565 m_current = nmf->second;
566 m_arguments[
static_cast<size_t>(nmf->second)].m_found =
true;
567 if(equal_pos == 0 || (equal_pos < 0 && arg_name.length() < arg.length()))
570 return arg_result(
"Malformed argument: " + arg);
572 else if(equal_pos > 0)
575 add_value(arg_value, position);
581 if(arg_name.length() == 1)
583 return begin_argument(arg,
true, position);
587 for(
char& c : arg_name)
589 r = begin_argument(
std::string(1, c),
true, position);
606 argument_parser::add_value(
const std::string& value,
int location)
608 auto unnamed = [&]() {
609 auto itr = m_positional_map.find(location);
610 if(itr != m_positional_map.end())
612 argument& a = m_arguments[
static_cast<size_t>(itr->second)];
613 a.m_values.push_back(value);
618 auto idx = m_positional_values.size();
619 m_positional_values.emplace(idx, value);
620 if(idx < m_positional_arguments.size())
622 auto& a = m_positional_arguments.at(idx);
624 auto err = a.check_choice(value);
627 a.m_values.push_back(value);
628 a.execute_actions(*
this);
637 size_t c =
static_cast<size_t>(m_current);
639 argument& a = m_arguments[
static_cast<size_t>(m_current)];
641 err = a.check_choice(value);
645 auto num_values = [&]() {
return static_cast<int>(a.m_values.size()); };
648 if((a.m_count >= 0 && num_values() >= a.m_count) ||
649 (a.m_max_count >= 0 && num_values() >= a.m_max_count))
651 err = end_argument();
657 a.m_values.push_back(value);
660 if((a.m_count >= 0 && num_values() >= a.m_count) ||
661 (a.m_max_count >= 0 && num_values() >= a.m_max_count))
663 err = end_argument();
674 argument_parser::end_argument()
678 argument& a = m_arguments[
static_cast<size_t>(m_current)];
680 if(
static_cast<int>(a.m_values.size()) < a.m_count)
681 return arg_result(
"Too few arguments given for " + a.m_names.at(0));
682 if(a.m_max_count >= 0)
684 if(
static_cast<int>(a.m_values.size()) > a.m_max_count)
685 return arg_result(
"Too many arguments given for " + a.m_names.at(0));
687 else if(a.m_count >= 0)
689 if(
static_cast<int>(a.m_values.size()) > a.m_count)
690 return arg_result(
"Too many arguments given for " + a.m_names.at(0));
TIMEMORY_UTILITY_INLINE std::ostream & operator<<(std::ostream &os, const argument_parser::arg_result &r)
std::array< char *, 4 > _args
tim::mpl::apply< std::string > string
const std::string std::ostream * os
void consume_parameters(ArgsT &&...)
const std::string & what() const
bool exists(const std::string &name) const
Returns whether or not an option was found in the arguments. Only useful after a call to parse or par...
known_args_t parse_known_args(int argc, char **argv, const std::string &_delim="--", int verbose_level=0)
Basic variant of parse_known_args which does not replace argc/argv and does not provide an array of s...
std::tuple< arg_result, int, char ** > known_args_t
arg_result parse(int argc, char **argv, int verbose_level=0)
void print_help(const std::string &_extra="")
std::set< std::string > strset_t
std::vector< std::string > strvec_t
argument_vector(Args &&... args)
cargs_t get_execv(const base_type &_prepend, size_t _beg=0, size_t _end=std::numeric_limits< size_t >::max()) const
std::vector< std::string > base_type
char * strdup(const char *s)
#define TIMEMORY_UTILITY_INLINE