From f3077ef8e3b65cf0c513b9fd48c8bd226b1df57c Mon Sep 17 00:00:00 2001 From: Martchus Date: Wed, 25 May 2016 01:24:17 +0200 Subject: [PATCH] Use C-strings for argument definitions Argument names and abbreviations are always C-string literals and hence using std::string has no advantage. --- application/argumentparser.cpp | 92 ++++++++++++++-------------------- application/argumentparser.h | 71 ++++++++++++++++---------- 2 files changed, 83 insertions(+), 80 deletions(-) diff --git a/application/argumentparser.cpp b/application/argumentparser.cpp index fec88d6..5715c39 100644 --- a/application/argumentparser.cpp +++ b/application/argumentparser.cpp @@ -25,6 +25,11 @@ const char *applicationAuthor = nullptr; const char *applicationVersion = nullptr; const char *applicationUrl = nullptr; +inline bool notEmpty(const char *str) +{ + return str && *str; +} + /*! * \class ApplicationUtilities::Argument * \brief The Argument class is a wrapper for command line argument information. @@ -41,7 +46,11 @@ const char *applicationUrl = nullptr; * The \a name and the abbreviation mustn't contain any whitespaces. * The \a name mustn't be empty. The \a abbreviation and the \a description might be empty. */ -Argument::Argument(const std::string &name, const std::string abbreviation, const std::string &description) : +Argument::Argument(const char *name, const char *abbreviation, const char *description, const char *example) : + m_name(name), + m_abbreviation(abbreviation), + m_description(description), + m_example(example), m_required(false), m_combinable(false), m_implicit(false), @@ -50,40 +59,7 @@ Argument::Argument(const std::string &name, const std::string abbreviation, cons m_default(false), m_present(false), m_isMainArg(false) -{ - setName(name); - setAbbreviation(abbreviation); - setDescription(description); -} - -/*! - * \brief Constructs an Argument with the given \a name, \a abbreviation and \a description. - * - * The \a name and the abbreviation mustn't contain any whitespaces. - * The \a name mustn't be empty. The \a abbreviation and the \a description might be empty. - */ -Argument::Argument(const char *name, const char *abbreviation, const char *description) : - m_required(false), - m_combinable(false), - m_implicit(false), - m_denotesOperation(false), - m_requiredValueCount(0), - m_default(false), - m_present(false), - m_isMainArg(false) -{ - if(name) { - setName(name); - } else { - setName(string()); - } - if(abbreviation) { - setAbbreviation(abbreviation); - } - if(description) { - setDescription(description); - } -} +{} /*! * \brief Destroys the Argument. @@ -126,13 +102,13 @@ Argument::~Argument() void Argument::printInfo(ostream &os, unsigned char indentionLevel) const { for(unsigned char i = 0; i < indentionLevel; ++i) os << " "; - if(!name().empty()) { + if(notEmpty(name())) { os << "--" << name(); } - if(!name().empty() && !abbreviation().empty()) { + if(notEmpty(name()) && notEmpty(abbreviation())) { os << ", "; } - if(!abbreviation().empty()) { + if(notEmpty(abbreviation())) { os << "-" << abbreviation(); } if(requiredValueCount() > 0) { @@ -151,7 +127,7 @@ void Argument::printInfo(ostream &os, unsigned char indentionLevel) const os << " ..."; } ++indentionLevel; - if(!description().empty()) { + if(notEmpty(description())) { os << endl; for(unsigned char i = 0; i < indentionLevel; ++i) os << " "; os << description(); @@ -161,7 +137,7 @@ void Argument::printInfo(ostream &os, unsigned char indentionLevel) const for(unsigned char i = 0; i < indentionLevel; ++i) os << " "; os << "This argument is required."; } - if(!example().empty()) { + if(notEmpty(example())) { for(unsigned char i = 0; i < indentionLevel; ++i) os << " "; os << endl << "Usage: " << example(); } @@ -391,6 +367,7 @@ Argument *ArgumentParser::findArg(const ArgumentVector &arguments, const Argumen return nullptr; // no argument matches } +#ifdef DEBUG_BUILD /*! * \brief This method is used to verify the setup of the command line parser before parsing. * @@ -416,32 +393,37 @@ void ArgumentParser::verifySetup() const continue; // do not verify the same argument twice } if(arg->isMainArgument() && arg->parents().size()) { - throw invalid_argument("Argument \"" + arg->name() + "\" can not be used as main argument and sub argument at the same time."); + throw invalid_argument("Argument \"" + string(arg->name()) + "\" can not be used as main argument and sub argument at the same time."); } - if(!arg->abbreviation().empty() && find(abbreviations.cbegin(), abbreviations.cend(), arg->abbreviation()) != abbreviations.cend()) { - throw invalid_argument("Abbreviation \"" + arg->abbreviation() + "\" has been used more then once."); + if(notEmpty(arg->abbreviation()) && find(abbreviations.cbegin(), abbreviations.cend(), arg->abbreviation()) != abbreviations.cend()) { + throw invalid_argument("Abbreviation \"" + string(arg->abbreviation()) + "\" has been used more then once."); } if(find(names.cbegin(), names.cend(), arg->name()) != names.cend()) { - throw invalid_argument("Name \"" + arg->name() + "\" has been used more then once."); + throw invalid_argument("Name \"" + string(arg->name()) + "\" has been used more then once."); } if(arg->isDefault() && arg->requiredValueCount() > 0 && arg->defaultValues().size() < static_cast(arg->requiredValueCount())) { - throw invalid_argument("Default argument \"" + arg->name() + "\" doesn't provide the required number of default values."); + throw invalid_argument("Default argument \"" + string(arg->name()) + "\" doesn't provide the required number of default values."); } if(arg->isImplicit()) { if(implicitArg) { - throw invalid_argument("The arguments \"" + implicitArg->name() + "\" and \"" + arg->name() + "\" can not be both implicit."); + throw invalid_argument("The arguments \"" + string(implicitArg->name()) + "\" and \"" + string(arg->name()) + "\" can not be both implicit."); } else { implicitArg = arg; } } - abbreviations.push_back(arg->abbreviation()); - names.push_back(arg->name()); + if(arg->abbreviation()) { + abbreviations.push_back(arg->abbreviation()); + } + if(arg->name()) { + names.push_back(arg->name()); + } verifiedArgs.push_back(arg); checkArguments(arg->secondaryArguments()); } }; checkArguments(m_mainArgs); } +#endif /*! * \brief This method invokes verifySetup() before parsing. See its do documentation for more @@ -459,7 +441,9 @@ void ArgumentParser::verifySetup() const void ArgumentParser::parseArgs(int argc, char *argv[]) { // initiate parser +#ifdef DEBUG_BUILD verifySetup(); +#endif m_actualArgc = 0; // reset actual agument count unsigned int actualArgc = 0; int valuesToRead = 0; @@ -511,7 +495,7 @@ void ArgumentParser::parseArgs(int argc, char *argv[]) // the corresponding instance of Argument class has been found if(currentArg->m_present) { // the argument has been provided more then once - throw Failure("The argument \"" + currentArg->name() + "\" has been specified more than one time."); + throw Failure("The argument \"" + string(currentArg->name()) + "\" has been specified more than one time."); } else { // set present flag of argument currentArg->m_present = true; @@ -615,7 +599,7 @@ void ArgumentParser::parseArgs(int argc, char *argv[]) } // throw an error if mandatory argument is not present if(!arg->isPresent() && (arg->isRequired() && (arg->isMainArgument() || (parent && parent->isPresent())))) { - throw Failure("The argument \"" + arg->name() + "\" is required but not given."); + throw Failure("The argument \"" + string(arg->name()) + "\" is required but not given."); } } }); @@ -623,9 +607,9 @@ void ArgumentParser::parseArgs(int argc, char *argv[]) if(arg->isPresent()) { if(!arg->isMainArgument() && arg->parents().size() && !arg->isParentPresent()) { if(arg->parents().size() > 1) { - throw Failure("The argument \"" + arg->name() + "\" needs to be used together with one the following arguments: " + arg->parentNames()); + throw Failure("The argument \"" + string(arg->name()) + "\" needs to be used together with one the following arguments: " + arg->parentNames()); } else { - throw Failure("The argument \"" + arg->name() + "\" needs to be used together with the argument \"" + arg->parents().front()->name() + "\"."); + throw Failure("The argument \"" + string(arg->name()) + "\" needs to be used together with the argument \"" + arg->parents().front()->name() + "\"."); } } Argument *conflictingArgument = nullptr; @@ -637,11 +621,11 @@ void ArgumentParser::parseArgs(int argc, char *argv[]) conflictingArgument = arg->conflictsWithArgument(); } if(conflictingArgument) { - throw Failure("The argument \"" + conflictingArgument->name() + "\" can not be combined with \"" + arg->name() + "\"."); + throw Failure("The argument \"" + string(conflictingArgument->name()) + "\" can not be combined with \"" + arg->name() + "\"."); } if(!arg->allRequiredValuesPresent()) { stringstream ss(stringstream::in | stringstream::out); - ss << "Not all required information for the given argument \"" << arg->name() << "\" provided. You have to give the following information:"; + ss << "Not all required information for the given argument \"" << string(arg->name()) << "\" provided. You have to give the following information:"; int valueNamesPrint = 0; for(const auto &name : arg->m_valueNames) { ss << "\n" << name; diff --git a/application/argumentparser.h b/application/argumentparser.h index 2428aa1..ae39e89 100644 --- a/application/argumentparser.h +++ b/application/argumentparser.h @@ -41,19 +41,18 @@ class LIB_EXPORT Argument public: typedef std::function CallbackFunction; - Argument(const std::string &name, const std::string abbreviation = std::string(), const std::string &description = std::string()); - Argument(const char *name, const char *abbreviation = nullptr, const char *description = nullptr); + Argument(const char *name, const char *abbreviation = nullptr, const char *description = nullptr, const char *example = nullptr); ~Argument(); - const std::string &name() const; - void setName(const std::string &name); - const std::string &abbreviation() const; - void setAbbreviation(const std::string &abbreviation); + const char *name() const; + void setName(const char *name); + const char *abbreviation() const; + void setAbbreviation(const char *abbreviation); //unsigned char isAmbiguous(const ArgumentParser &parser) const; - const std::string &description() const; - void setDescription(const std::string &description); - const std::string &example() const; - void setExample(const std::string &example); + const char *description() const; + void setDescription(const char *description); + const char *example() const; + void setExample(const char *example); const StringVector &values() const; const std::string &value(StringVector::size_type index) const; StringVector::size_type valueCount() const; @@ -90,10 +89,10 @@ public: Argument *conflictsWithArgument() const; private: - std::string m_name; - std::string m_abbreviation; - std::string m_description; - std::string m_example; + const char *m_name; + const char *m_abbreviation; + const char *m_description; + const char *m_example; bool m_required; bool m_combinable; bool m_implicit; @@ -116,7 +115,7 @@ private: * The parser compares the name with the characters following a "--" prefix to * identify arguments. */ -inline const std::string &Argument::name() const +inline const char *Argument::name() const { return m_name; } @@ -129,11 +128,20 @@ inline const std::string &Argument::name() const * The parser compares the name with the characters following a "--" prefix to * identify arguments. */ -inline void Argument::setName(const std::string &name) +inline void Argument::setName(const char *name) { - if(name.empty() || name.find(' ') != std::string::npos || name.find('=') != std::string::npos) { - throw std::invalid_argument("name mustn't be empty or contain white spaces or equation chars"); +#ifdef DEBUG_BUILD + if(name && *name) { + for(const char *c = name; *c; ++c) { + switch(*c) { + case ' ': case '=': + throw std::invalid_argument("name mustn't contain white spaces or equation chars"); + default: + ; + } + } } +#endif m_name = name; } @@ -143,7 +151,7 @@ inline void Argument::setName(const std::string &name) * The parser compares the abbreviation with the characters following a "-" prefix to * identify arguments. */ -inline const std::string &Argument::abbreviation() const +inline const char *Argument::abbreviation() const { return m_abbreviation; } @@ -157,11 +165,20 @@ inline const std::string &Argument::abbreviation() const * The parser compares the abbreviation with the characters following a "-" prefix to * identify arguments. */ -inline void Argument::setAbbreviation(const std::string &abbreviation) +inline void Argument::setAbbreviation(const char *abbreviation) { - if(!abbreviation.empty() && (abbreviation.find(' ') != std::string::npos || abbreviation.find('=') != std::string::npos)) { - throw std::invalid_argument("abbreviation mustn't contain white spaces or equation chars"); +#ifdef DEBUG_BUILD + if(abbreviation && *abbreviation) { + for(const char *c = abbreviation; *c; ++c) { + switch(*c) { + case ' ': case '=': + throw std::invalid_argument("abbreviation mustn't contain white spaces or equation chars"); + default: + ; + } + } } +#endif m_abbreviation = abbreviation; } @@ -170,7 +187,7 @@ inline void Argument::setAbbreviation(const std::string &abbreviation) * * The parser uses the description when printing help information. */ -inline const std::string &Argument::description() const +inline const char *Argument::description() const { return m_description; } @@ -180,7 +197,7 @@ inline const std::string &Argument::description() const * * The parser uses the description when printing help information. */ -inline void Argument::setDescription(const std::string &description) +inline void Argument::setDescription(const char *description) { m_description = description; } @@ -190,7 +207,7 @@ inline void Argument::setDescription(const std::string &description) * * The parser uses the description when printing help information. */ -inline const std::string &Argument::example() const +inline const char *Argument::example() const { return m_example; } @@ -200,7 +217,7 @@ inline const std::string &Argument::example() const * * The parser uses the description when printing help information. */ -inline void Argument::setExample(const std::string &example) +inline void Argument::setExample(const char *example) { m_example = example; } @@ -564,7 +581,9 @@ public: void printHelp(std::ostream &os) const; Argument *findArg(const ArgumentPredicate &predicate) const; static Argument *findArg(const ArgumentVector &arguments, const ArgumentPredicate &predicate); +#ifdef DEBUG_BUILD void verifySetup() const; +#endif void parseArgs(int argc, char *argv[]); unsigned int actualArgumentCount() const; const std::string ¤tDirectory() const;