diff --git a/cppargparse.cc b/cppargparse.cc index 7293d71..31be787 100644 --- a/cppargparse.cc +++ b/cppargparse.cc @@ -1,454 +1,461 @@ /** * @file cppargparse.cc * * @author Nicolas Richart * * @date creation: Thu Apr 03 2014 * @date last modification: Mon Sep 15 2014 * * @brief implementation of the ArgumentParser * * @section LICENSE * * Copyright (©) 2014 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "cppargparse.hh" #include #include #include #include #include #include #include #include #include #include #include namespace cppargparse { /* -------------------------------------------------------------------------- */ static inline std::string to_upper(const std::string & str) { std::string lstr = str; std::transform(lstr.begin(), lstr.end(), lstr.begin(), (int(*)(int))std::toupper); return lstr; } /* -------------------------------------------------------------------------- */ /* ArgumentParser */ /* -------------------------------------------------------------------------- */ - ArgumentParser::ArgumentParser() : external_exit(NULL), prank(0), psize(1) { +ArgumentParser::ArgumentParser() : external_exit(NULL), prank(0), psize(1) { this->addArgument("-h;--help", "show this help message and exit", 0, _boolean, false, true); } +/* -------------------------------------------------------------------------- */ ArgumentParser::~ArgumentParser() { for(_Arguments::iterator it = arguments.begin(); it != arguments.end(); ++it) { delete it->second; } } +/* -------------------------------------------------------------------------- */ void ArgumentParser::setParallelContext(int prank, int psize) { this->prank = prank; this->psize = psize; } +/* -------------------------------------------------------------------------- */ void ArgumentParser::_exit(const std::string & msg, int status) { if(prank == 0) { if(msg != "") { std::cerr << msg << std::endl; std::cerr << std::endl; } this->print_help(std::cerr); } if(external_exit) (*external_exit)(status); else { exit(status); } } +/* -------------------------------------------------------------------------- */ const ArgumentParser::Argument & ArgumentParser::operator[](const std::string & name) const { Arguments::const_iterator it = success_parsed.find(name); if(it != success_parsed.end()) { return *(it->second); } else { throw std::range_error("No argument named \'" + name + "\' was found in the parsed argument," + " make sur to specify it \'required\'" + " or to give it a default value"); } } - + +/* -------------------------------------------------------------------------- */ bool ArgumentParser::has(const std::string & name) const { return (success_parsed.find(name) != success_parsed.end()); } /* -------------------------------------------------------------------------- */ void ArgumentParser::addArgument(const std::string & name_or_flag, const std::string & help, int nargs, ArgumentType type) { _addArgument(name_or_flag, help, nargs, type); } + +/* -------------------------------------------------------------------------- */ ArgumentParser::_Argument & ArgumentParser::_addArgument(const std::string & name, const std::string & help, int nargs, ArgumentType type) { _Argument * arg = NULL; switch(type) { case _string: { arg = new ArgumentStorage(); break; } case _float: { arg = new ArgumentStorage(); break; } case _integer: { arg = new ArgumentStorage(); break; } case _boolean: { arg = new ArgumentStorage(); break; } } arg->help = help; arg->nargs = nargs; arg->type = type; std::stringstream sstr(name); std::string item; std::vector tmp_keys; while (std::getline(sstr, item, ';')) { tmp_keys.push_back(item); } int long_key = -1; int short_key = -1; bool problem = (tmp_keys.size() > 2) || (name == ""); for (std::vector::iterator it = tmp_keys.begin(); it != tmp_keys.end(); ++it) { if(it->find("--") == 0) { problem |= (long_key != -1); long_key = it - tmp_keys.begin(); } else if(it->find("-") == 0) { problem |= (long_key != -1); short_key = it - tmp_keys.begin(); } } problem |= ((tmp_keys.size() == 2) && (long_key == -1 || short_key == -1)); if(problem) { throw std::invalid_argument("Synthax of name or flags is not correct. Possible synthax are \'-f\', \'-f;--foo\', \'--foo\', \'bar\'"); } if(long_key != -1) { arg->name = tmp_keys[long_key]; arg->name.erase(0, 2); } else if(short_key != -1) { arg->name = tmp_keys[short_key]; arg->name.erase(0, 1); } else { arg->name = tmp_keys[0]; pos_args.push_back(arg); arg->required = true; arg->is_positional = true; } arguments[arg->name] = arg; if(!arg->is_positional) { if(short_key != -1) { std::string key = tmp_keys[short_key]; key_args[key] = arg; arg->keys.push_back(key); } if(long_key != -1) { std::string key = tmp_keys[long_key]; key_args[key] = arg; arg->keys.push_back(key); } } return *arg; } /* -------------------------------------------------------------------------- */ void ArgumentParser::parse(int& argc, char**& argv, int flags, bool parse_help) { bool stop_in_not_parsed = flags & _stop_on_not_parsed; bool remove_parsed = flags & _remove_parsed; std::vector argvs; for (int i = 0; i < argc; ++i) { argvs.push_back(std::string(argv[i])); } unsigned int current_position = 0; - if(this->program_name == "") { + if(this->program_name == "" && argc > 0) { std::string prog = argvs[current_position]; const char * c_prog = prog.c_str(); char * c_prog_tmp = strdup(c_prog); std::string base_prog(basename(c_prog_tmp)); this->program_name = base_prog; std::free(c_prog_tmp); } std::queue<_Argument *> positional_queue; for(PositionalArgument::iterator it = pos_args.begin(); it != pos_args.end(); ++it) positional_queue.push(*it); std::vector argvs_to_remove; ++current_position; // consume argv[0] while(current_position < argvs.size()) { std::string arg = argvs[current_position]; ++current_position; ArgumentKeyMap::iterator key_it = key_args.find(arg); bool is_positional = false; _Argument * argument_ptr = NULL; if(key_it == key_args.end()) { if(positional_queue.empty()) { if(stop_in_not_parsed) this->_exit("Argument " + arg + " not recognized", EXIT_FAILURE); continue; } else { argument_ptr = positional_queue.front(); is_positional = true; --current_position; } } else { argument_ptr = key_it->second; } if(remove_parsed && ! is_positional && argument_ptr->name != "help") { argvs_to_remove.push_back(current_position - 1); } _Argument & argument = *argument_ptr; unsigned int min_nb_val = 0, max_nb_val = 0; switch(argument.nargs) { case _one_if_possible: max_nb_val = 1; break; // "?" case _at_least_one: min_nb_val = 1; // "+" case _any: max_nb_val = argc - current_position; break; // "*" default: min_nb_val = max_nb_val = argument.nargs; // "N" } std::vector values; unsigned int arg_consumed = 0; if(max_nb_val <= (argc - current_position)) { for(; arg_consumed < max_nb_val; ++arg_consumed) { std::string v = argvs[current_position]; ++current_position; bool is_key = key_args.find(v) != key_args.end(); bool is_good_type = checkType(argument.type, v); if(!is_key && is_good_type) { values.push_back(v); if(remove_parsed) argvs_to_remove.push_back(current_position - 1); } else { // unconsume not parsed argument for optional if(!is_positional || (is_positional && is_key)) --current_position; break; } } } if(arg_consumed < min_nb_val) { if(!is_positional) { this->_exit("Not enought values for the argument " + argument.name + " where provided", EXIT_FAILURE); } else { if(stop_in_not_parsed) this->_exit("Argument " + arg + " not recognized", EXIT_FAILURE); } } else { if (is_positional) positional_queue.pop(); if(!argument.parsed) { success_parsed[argument.name] = &argument; argument.parsed = true; if((argument.nargs == _one_if_possible || argument.nargs == 0) && arg_consumed == 0) { if(argument.has_const) argument.setToConst(); else if(argument.has_default) argument.setToDefault(); } else { argument.setValues(values); } } else { this->_exit("Argument " + argument.name + " already present in the list of argument", EXIT_FAILURE); } } } for (_Arguments::iterator ait = arguments.begin(); ait != arguments.end(); ++ait) { _Argument & argument = *(ait->second); if(!argument.parsed) { if(argument.has_default) { argument.setToDefault(); success_parsed[argument.name] = &argument; } if(argument.required) { this->_exit("Argument " + argument.name + " required but not given!", EXIT_FAILURE); } } } // removing the parsed argument if remove_parsed is true if(argvs_to_remove.size()) { std::vector::const_iterator next_to_remove = argvs_to_remove.begin(); for (int i = 0, c = 0; i < argc; ++i) { if(next_to_remove == argvs_to_remove.end() || i != *next_to_remove) { argv[c] = argv[i]; ++c; } else { if (next_to_remove != argvs_to_remove.end()) ++next_to_remove; } } argc -= argvs_to_remove.size(); } if(this->arguments["help"]->parsed && parse_help) { this->_exit(); } } /* -------------------------------------------------------------------------- */ bool ArgumentParser::checkType(ArgumentType type, const std::string & value) const { std::stringstream sstr(value); switch(type) { case _string: { std::string s; sstr >> s; break; } case _float: { double d; sstr >> d; break; } case _integer: { int i; sstr >> i; break; } case _boolean: { bool b; sstr >> b; break; } } return (sstr.fail() == false); } /* -------------------------------------------------------------------------- */ void ArgumentParser::printself(std::ostream & stream) const { for(Arguments::const_iterator it = success_parsed.begin(); it != success_parsed.end(); ++it) { const Argument & argument = *(it->second); argument.printself(stream); stream << std::endl; } } /* -------------------------------------------------------------------------- */ void ArgumentParser::print_usage(std::ostream & stream) const { stream << "Usage: " << this->program_name; std::stringstream sstr_opt; // print shorten usage for(_Arguments::const_iterator it = arguments.begin(); it != arguments.end(); ++it) { const _Argument & argument = *(it->second); if(!argument.is_positional) { if(!argument.required) stream << " ["; stream << argument.keys[0]; this->print_usage_nargs(stream, argument); if(!argument.required) stream << "]"; } } for(PositionalArgument::const_iterator it = pos_args.begin(); it != pos_args.end(); ++it) { const _Argument & argument = **it; this->print_usage_nargs(stream, argument); } stream << std::endl; } /* -------------------------------------------------------------------------- */ void ArgumentParser::print_usage_nargs(std::ostream & stream, const _Argument & argument) const { std::string u_name = to_upper(argument.name); switch(argument.nargs) { case _one_if_possible: stream << " [" << u_name << "]"; break; case _at_least_one: stream << " " << u_name; case _any: stream << " [" << u_name << " ...]"; break; default: for(int i = 0; i < argument.nargs; ++i) { stream << " " << u_name; } } } void ArgumentParser::print_help(std::ostream & stream) const { this->print_usage(stream); if(!pos_args.empty()) { stream << std::endl; stream << "positional arguments:" << std::endl; for(PositionalArgument::const_iterator it = pos_args.begin(); it != pos_args.end(); ++it) { const _Argument & argument = **it; this->print_help_argument(stream, argument); } } if(!key_args.empty()) { stream << std::endl; stream << "optional arguments:" << std::endl; for(_Arguments::const_iterator it = arguments.begin(); it != arguments.end(); ++it) { const _Argument & argument = *(it->second); if(!argument.is_positional) { this->print_help_argument(stream, argument); } } } } void ArgumentParser::print_help_argument(std::ostream & stream, const _Argument & argument) const { std::string key(""); if(argument.is_positional) key = argument.name; else { std::stringstream sstr; for(unsigned int i = 0; i < argument.keys.size(); ++i) { if(i != 0) sstr << ", "; sstr << argument.keys[i]; this->print_usage_nargs(sstr, argument); } key = sstr.str(); } stream << " " << std::left << std::setw(15) << key << " " << argument.help; argument.printDefault(stream); stream << std::endl; } } diff --git a/cppargparse_tmpl.hh b/cppargparse_tmpl.hh index 51338fc..061052b 100644 --- a/cppargparse_tmpl.hh +++ b/cppargparse_tmpl.hh @@ -1,261 +1,266 @@ /** * @file cppargparse_tmpl.hh * * @author Nicolas Richart * * @date creation: Thu Apr 03 2014 * @date last modification: Fri Sep 19 2014 * * @brief Implementation of the templated part of the commandline argument parser * * @section LICENSE * * Copyright (©) 2014 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * Akantu is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Akantu is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Akantu. If not, see . * */ /* -------------------------------------------------------------------------- */ #include #ifndef __CPPARGPARSE_TMPL_HH__ #define __CPPARGPARSE_TMPL_HH__ namespace cppargparse { /* -------------------------------------------------------------------------- */ /* Argument */ /* -------------------------------------------------------------------------- */ struct ArgumentParser::_Argument : public Argument { _Argument() : Argument(), help(std::string()), nargs(1), type(_string), required(false), parsed(false), has_default(false), has_const(false), is_positional(false) {} virtual ~_Argument() {} void setValues(std::vector & values) { for (std::vector::iterator it = values.begin(); it != values.end(); ++it) { this->addValue(*it); } } virtual void addValue(std::string & value) = 0; virtual void setToDefault() = 0; virtual void setToConst() = 0; std::ostream & printDefault(std::ostream & stream) const { stream << std::boolalpha; if(has_default) { stream << " (default: "; this->_printDefault(stream); stream << ")"; } if(has_const) { stream << " (const: "; this->_printConst(stream); stream << ")"; } return stream; } virtual std::ostream & _printDefault(std::ostream & stream) const = 0; virtual std::ostream & _printConst(std::ostream & stream) const = 0; std::string help; int nargs; ArgumentType type; bool required; bool parsed; bool has_default; bool has_const; std::vector keys; bool is_positional; }; /* -------------------------------------------------------------------------- */ template class ArgumentParser::ArgumentStorage : public ArgumentParser::_Argument { public: ArgumentStorage() : _default(T()), _const(T()), values(std::vector()) {} virtual void addValue(std::string & value) { std::stringstream sstr(value); T t; sstr >> t; values.push_back(t); } virtual void setToDefault() { values.clear(); values.push_back(_default); } virtual void setToConst() { values.clear(); values.push_back(_const); } void printself(std::ostream & stream) const { stream << this->name << " ="; stream << std::boolalpha; // for boolean for(typename std::vector::const_iterator vit = this->values.begin(); vit != this->values.end(); ++vit) { stream << " " << *vit; } } virtual std::ostream & _printDefault(std::ostream & stream) const { stream << _default; return stream; } virtual std::ostream & _printConst(std::ostream & stream) const { stream << _const; return stream; } T _default; T _const; std::vector values; }; /* -------------------------------------------------------------------------- */ template<> inline void ArgumentParser::ArgumentStorage::addValue(std::string & value) { values.push_back(value); } template struct is_vector { enum { value = false }; }; template struct is_vector< std::vector > { enum { value = true }; }; /* -------------------------------------------------------------------------- */ template::value> struct cast_helper { static T cast(const ArgumentParser::Argument & arg) { const ArgumentParser::ArgumentStorage & _arg = dynamic_cast &>(arg); if(_arg.values.size() == 1) { return _arg.values[0]; } else { throw std::length_error("Not enougth or too many argument where passed for the command line argument: " + arg.name); } } }; template struct cast_helper { static T cast(const ArgumentParser::Argument & arg) { const ArgumentParser::ArgumentStorage & _arg = dynamic_cast &>(arg); return _arg.values; } }; /* -------------------------------------------------------------------------- */ template ArgumentParser::Argument::operator T() const{ return cast_helper::cast(*this); } +template<> +inline ArgumentParser::Argument::operator const char*() const{ + return cast_helper::cast(*this).c_str(); +} + template<> inline ArgumentParser::Argument::operator unsigned int() const{ return cast_helper::cast(*this); } template void ArgumentParser::addArgument(const std::string & name_or_flag, const std::string & help, int nargs, ArgumentType type, T def) { _Argument & arg = _addArgument(name_or_flag, help, nargs, type); dynamic_cast &>(arg)._default = def; arg.has_default = true; } template void ArgumentParser::addArgument(const std::string & name_or_flag, const std::string & help, int nargs, ArgumentType type, T def, T cons) { _Argument & arg = _addArgument(name_or_flag, help, nargs, type); dynamic_cast &>(arg)._default = def; arg.has_default = true; dynamic_cast &>(arg)._const = cons; arg.has_const = true; } /* -------------------------------------------------------------------------- */ template<> inline void ArgumentParser::addArgument(const std::string & name_or_flag, const std::string & help, int nargs, ArgumentType type, const char * def) { this->addArgument(name_or_flag, help, nargs, type, def); } template<> inline void ArgumentParser::addArgument(const std::string & name_or_flag, const std::string & help, int nargs, ArgumentType type, unsigned int def) { this->addArgument(name_or_flag, help, nargs, type, def); } /* -------------------------------------------------------------------------- */ template<> inline void ArgumentParser::addArgument(const std::string & name_or_flag, const std::string & help, int nargs, ArgumentType type, const char * def, const char * cons) { this->addArgument(name_or_flag, help, nargs, type, def, cons); } template<> inline void ArgumentParser::addArgument(const std::string & name_or_flag, const std::string & help, int nargs, ArgumentType type, unsigned int def, unsigned int cons) { this->addArgument(name_or_flag, help, nargs, type, def, cons); } } #endif /* __AKANTU_CPPARGPARSE_TMPL_HH__ */