From 8a294bd0e750389d4384e3292bb11e723943b25c Mon Sep 17 00:00:00 2001 From: Denes Matetelki Date: Sat, 15 Oct 2011 22:17:45 +0200 Subject: [PATCH] python argParse like ArgParse class added --- include/ArgParse.hpp | 105 +++++++++++++++++++ src/ArgParse.cpp | 243 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 348 insertions(+) create mode 100644 include/ArgParse.hpp create mode 100644 src/ArgParse.cpp diff --git a/include/ArgParse.hpp b/include/ArgParse.hpp new file mode 100644 index 0000000..65a0e92 --- /dev/null +++ b/include/ArgParse.hpp @@ -0,0 +1,105 @@ +#ifndef ARGPARSE_HPP +#define ARGPARSE_HPP + +#include +#include +#include +#include + +// Aims for the functionality of Python argparse +// http://docs.python.org/library/argparse.html#module-argparse +class ArgParse +{ + +public: + + enum ValueType { + TYPE_NONE, + TYPE_STRING, + TYPE_INT, + TYPE_DOUBLE, + TYPE_BOOL + }; + + enum ValueRequired { + OPTIONAL, + REQUIRED + }; + + ArgParse(const std::string description, + const std::string epilog = std::string(""), + const bool add_Help = true); + + void addArgument(const std::string name, // "-f,--foo" + const std::string help, + const enum ValueType type = TYPE_NONE, + const enum ValueRequired valueRequired = REQUIRED, + const std::string valueName = std::string(""), + const std::string choices = std::string("")); // "yes,no" or range: [1..100] + + + bool parseArgs(const int argc, + const char* argv[]) throw(std::runtime_error); + + bool parseArgs(const std::list argList) throw(std::runtime_error); + + // is this arg in the map of understood arguments? + bool isArg(const std::string arg) const; + // is this argument passed as a command line parameter? + bool foundArg(const std::string arg) const; + // argument is passed as a command line parameter, but has a value? + bool argHasValue(const std::string arg) const; + + // return true is arg exists and the arg in &value + // arg need to be the same string as in addArgument ( "-h,--help" ) + bool argAsString(const std::string arg, std::string &value) const; + bool argAsInt(const std::string arg, int &value) const; + bool argAsDouble(const std::string arg, double &value) const; + bool argAsBool(const std::string arg, bool &value) const; + + std::string usage() const; + + +private: + + struct Argument { + const std::string m_help; + const enum ValueType m_type; + const enum ValueRequired m_valueRequired; + const std::string m_valueName; + const std::string m_choices; + std::string m_value; + bool m_found; + bool m_valueHasBeenSet; + + Argument (const std::string help, + const enum ValueType type = TYPE_NONE, + const enum ValueRequired valueRequired = REQUIRED, + const std::string valueName = std::string(""), + const std::string choices = std::string(""), + const std::string value = std::string("")) + : m_help(help) + , m_type(type) + , m_valueRequired(valueRequired) + , m_valueName(valueName) + , m_choices(choices) + , m_value(value) + , m_found(false) + , m_valueHasBeenSet(false) {}; + }; + + // arg is just the shor or long form: "-h" or "--help" + std::map::iterator findElement(const std::string param); + + + std::string m_description; + std::string m_epilog; + std::string m_programName; + + std::map m_params; + + +}; // class ArgParse + + +#endif // ARGPARSE_HPP diff --git a/src/ArgParse.cpp b/src/ArgParse.cpp new file mode 100644 index 0000000..1eb5033 --- /dev/null +++ b/src/ArgParse.cpp @@ -0,0 +1,243 @@ +#include "../include/ArgParse.hpp" + + +#include +#include + +#include +#include +#include + + +ArgParse::ArgParse(const std::string description, + const std::string epilog, + const bool add_Help) + : m_description(description) + , m_epilog(epilog) + , m_programName() + , m_params() +{ + if (add_Help) + addArgument("-h,--help", "Prints this help message"); +} + + +void ArgParse::addArgument(const std::string arg, + const std::string help, + const ValueType type, + const ValueRequired valueRequired, + const std::string valueName, + const std::string choices) +{ + Argument argument(help, type, valueRequired, valueName, choices, ""); + m_params.insert(std::pair(arg, argument)); +} + + +bool ArgParse::parseArgs(const int argc, + const char* argv[]) throw(std::runtime_error) +{ + std::list argList; + for (int i = 0; i < argc; ++i ) + argList.push_back(argv[i]); + + return parseArgs(argList); +} + + +bool ArgParse::parseArgs(const std::list argList) throw(std::runtime_error) +{ + m_programName = argList.front(); + + // the wrok. + std::list::const_iterator it = argList.begin(); + for (++it; it != argList.end(); ++it ) { + + if ( (*it).at(0) != '-' ) + throw std::runtime_error(std::string(*it).append(" shall start with a dash.")); + + // inspect each arument + std::map::iterator it2 = findElement(*it); + if ( it2 == m_params.end() ) + throw std::runtime_error(std::string(*it).append(" is not known.")); + + if ( (*it2).second.m_found ) + throw std::runtime_error(std::string(*it).append(" has been given before.")); + + (*it2).second.m_found = true; + + if ( (*it2).second.m_type == TYPE_NONE ) + continue; + + std::list::const_iterator next = it; + next++; + + if ( next == argList.end() ) { + if ( (*it2).second.m_valueRequired == REQUIRED ) + throw std::runtime_error(std::string(*it).append(" requires a parameter.")); + + if ( (*it2).second.m_valueRequired == OPTIONAL ) + continue; + } + + if ( (*it2).second.m_valueRequired == OPTIONAL && findElement( *next ) != m_params.end() ) + continue; + + switch ( (*it2).second.m_type ) { + case TYPE_INT : { + int temp; + if ( sscanf( next->c_str(), "%d", &temp ) == 0 ) + throw std::runtime_error(std::string( *next ). + append(" is not an integer, required by ").append(*it)); + break; + } + case TYPE_DOUBLE : { + double temp; + if ( sscanf( next->c_str(), "%f", &temp ) == 0 ) + throw std::runtime_error(std::string( *next ). + append(" is not a double, required by ").append(*it)); + break; + } + case TYPE_BOOL : { + std::string temp = *next; + std::transform(temp.begin(), temp.end(),temp.begin(), ::toupper); + if ( temp != "TRUE" && temp != "FALSE" ) + throw std::runtime_error(std::string( *next ). + append(" is not a boolean, required by ").append(*it)); + break; + } + default: + break; + } + + (*it2).second.m_value = *next; + (*it2).second.m_valueHasBeenSet = true; + ++it; + } + return true; +} + + +bool ArgParse::isArg(const std::string arg) const +{ + std::map::const_iterator it = m_params.find(arg); + return it != m_params.end(); +} + + +bool ArgParse::foundArg(const std::string arg) const +{ + std::map::const_iterator it = m_params.find(arg); + return it != m_params.end() && (*it).second.m_found == true; +} + +bool ArgParse::argHasValue(const std::string arg) const +{ + std::map::const_iterator it = m_params.find(arg); + return it != m_params.end() && + (*it).second.m_found == true && + (*it).second.m_valueHasBeenSet; +} + + +bool ArgParse::argAsString(const std::string arg, std::string &value) const +{ + if ( !argHasValue(arg) ) + return false; + + std::map::const_iterator it = m_params.find(arg); + value = (*it).second.m_value; + return true; +} + + +bool ArgParse::argAsInt(const std::string arg, int &value) const +{ + if ( !argHasValue(arg) ) + return false; + + std::map::const_iterator it = m_params.find(arg); + value = atoi((*it).second.m_value.c_str()); + return true; +} + + +bool ArgParse::argAsDouble(const std::string arg, double &value) const +{ + if ( !argHasValue(arg) ) + return false; + + std::map::const_iterator it = m_params.find(arg); + value = atof((*it).second.m_value.c_str()); + return true; +} + + +bool ArgParse::argAsBool(const std::string arg, bool &value) const +{ + if ( !argHasValue(arg) ) + return false; + + std::map::const_iterator it = m_params.find(arg); + + std::string temp = (*it).second.m_value; + std::transform(temp.begin(), temp.end(),temp.begin(), ::toupper); + + value = temp == "TRUE"; + return true; +} + + +std::string ArgParse::usage() const +{ + std::stringstream ss; + + ss << m_description << std::endl << std::endl; + + ss << "usage: " << m_programName; + if (!m_params.empty()) { + ss << " [OPTION]" << std::endl << std::endl; + ss << "Options:" << std::endl; + int length; + std::map::const_iterator it; + for ( it = m_params.begin(); it != m_params.end(); it++ ) { + ss << (*it).first; + length = (*it).first.length(); + if ( (*it).second.m_type != TYPE_NONE ) { + if ((*it).second.m_valueRequired == REQUIRED ) { + ss << "=" << (*it).second.m_valueName; + length += (*it).second.m_valueName.length()+1; + } else { + ss << "[=" << (*it).second.m_valueName << "]"; + length += (*it).second.m_valueName.length() + 3; + } + } + ss << std::string(30-length, ' '); + ss << (*it).second.m_help << std::endl; + } + } + ss << std::endl; + ss << m_epilog << std::endl; + + return ss.str();; +} + +std::map::iterator +ArgParse::findElement(const std::string param) +{ + std::map::iterator it; + for( it = m_params.begin(); it != m_params.end(); ++it) { + + // if it's the short param at the beginning + if ( (*it).first.find(param) == 0 ) + return it; + + // or is it the long after the comma? + size_t commaPos = (*it).first.find(","); + if ( commaPos != std::string::npos && + (*it).first.find( param, commaPos+1 ) != std::string::npos ) + return it; + } + + return m_params.end(); +} \ No newline at end of file