Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F92813548
cppargparse.cc
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Sat, Nov 23, 22:00
Size
13 KB
Mime Type
text/x-c
Expires
Mon, Nov 25, 22:00 (1 d, 21 h)
Engine
blob
Format
Raw Data
Handle
22518945
Attached To
rAKA akantu
cppargparse.cc
View Options
/**
* @file cppargparse.cc
*
* @author Nicolas Richart <nicolas.richart@epfl.ch>
*
* @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 <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "cppargparse.hh"
#include <cstdlib>
#include <cstring>
#include <libgen.h>
#include <iostream>
#include <iomanip>
#include <string>
#include <queue>
#include <sstream>
#include <algorithm>
#include <exception>
#include <stdexcept>
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) {
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<std::string>(); break; }
case _float: { arg = new ArgumentStorage<double>(); break; }
case _integer: { arg = new ArgumentStorage<int>(); break; }
case _boolean: { arg = new ArgumentStorage<bool>(); break; }
}
arg->help = help;
arg->nargs = nargs;
arg->type = type;
std::stringstream sstr(name);
std::string item;
std::vector<std::string> 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<std::string>::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<std::string> argvs;
for (int i = 0; i < argc; ++i) {
argvs.push_back(std::string(argv[i]));
}
unsigned int current_position = 0;
if(this->program_name == "") {
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<int> 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<std::string> 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<int>::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;
}
}
Event Timeline
Log In to Comment