cpp-utilities/application/argumentparser.cpp

683 lines
24 KiB
C++
Raw Normal View History

2015-09-06 20:19:09 +02:00
#include "./argumentparser.h"
#include "./commandlineutils.h"
#include "./failure.h"
2015-05-08 23:20:47 +02:00
2015-09-06 20:19:09 +02:00
#include "../conversion/stringconversion.h"
#include "../misc/random.h"
2015-04-22 18:36:40 +02:00
#include <algorithm>
#include <iostream>
2016-06-12 01:56:57 +02:00
#include <string>
2015-04-22 18:36:40 +02:00
#include <sstream>
2016-06-12 01:56:57 +02:00
#include <cstring>
2015-04-22 18:36:40 +02:00
using namespace std;
using namespace std::placeholders;
2016-06-12 01:56:57 +02:00
using namespace ConversionUtilities;
2015-04-22 18:36:40 +02:00
/*!
2016-06-12 01:56:57 +02:00
* \namespace ApplicationUtilities
* \brief Contains currently only ArgumentParser and related classes.
*/
2015-04-22 18:36:40 +02:00
namespace ApplicationUtilities {
2016-06-12 01:56:57 +02:00
/// \cond
2015-08-25 19:12:05 +02:00
const char *applicationName = nullptr;
const char *applicationAuthor = nullptr;
const char *applicationVersion = nullptr;
const char *applicationUrl = nullptr;
inline bool notEmpty(const char *str)
{
return str && *str;
}
2016-06-12 01:56:57 +02:00
/// \endcond
2016-06-23 00:58:43 +02:00
/*!
* \brief The ArgumentDenotationType enum specifies the type of a given argument denotation.
*/
enum ArgumentDenotationType : unsigned char {
Value = 0, /**< parameter value */
Abbreviation = 1, /**< argument abbreviation */
FullName = 2 /**< full argument name */
};
2015-04-22 18:36:40 +02:00
/*!
* \class ApplicationUtilities::Argument
* \brief The Argument class is a wrapper for command line argument information.
*
* Instaces of the Argument class are used as definition when parsing command line
* arguments. Arguments can be assigned to an ArgumentParser using
* ArgumentParser::setMainArguments() and to another Argument instance using
* Argument::setSecondaryArguments().
*/
/*!
2016-02-27 01:19:16 +01:00
* \brief Constructs an Argument with the given \a name, \a abbreviation and \a description.
2015-04-22 18:36:40 +02:00
*
* 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.
*/
2016-06-12 01:56:57 +02:00
Argument::Argument(const char *name, char abbreviation, const char *description, const char *example) :
m_name(name),
m_abbreviation(abbreviation),
m_description(description),
m_example(example),
2016-06-12 01:56:57 +02:00
m_minOccurrences(0),
m_maxOccurrences(1),
2015-04-22 18:36:40 +02:00
m_combinable(false),
2015-05-08 23:20:47 +02:00
m_denotesOperation(false),
2015-04-22 18:36:40 +02:00
m_requiredValueCount(0),
m_implicit(false),
2015-04-22 18:36:40 +02:00
m_isMainArg(false)
{}
2015-04-22 18:36:40 +02:00
/*!
2016-02-27 01:19:16 +01:00
* \brief Destroys the Argument.
2015-04-22 18:36:40 +02:00
*/
Argument::~Argument()
{}
/*!
2016-02-27 01:19:16 +01:00
* \brief Appends the name, the abbreviation and the description of the Argument to the give ostream.
2015-04-22 18:36:40 +02:00
*/
void Argument::printInfo(ostream &os, unsigned char indentionLevel) const
{
for(unsigned char i = 0; i < indentionLevel; ++i) os << " ";
if(notEmpty(name())) {
2016-06-12 01:56:57 +02:00
os << '-' << '-' << name();
2015-04-22 18:36:40 +02:00
}
2016-06-12 01:56:57 +02:00
if(notEmpty(name()) && abbreviation()) {
os << ',' << ' ';
2015-04-22 18:36:40 +02:00
}
2016-06-12 01:56:57 +02:00
if(abbreviation()) {
os << '-' << abbreviation();
2015-04-22 18:36:40 +02:00
}
2016-06-12 01:56:57 +02:00
if(requiredValueCount() != 0) {
unsigned int valueNamesPrint = 0;
2015-04-22 18:36:40 +02:00
for(auto i = valueNames().cbegin(), end = valueNames().cend(); i != end && valueNamesPrint < requiredValueCount(); ++i) {
2016-06-12 01:56:57 +02:00
os << ' ' << '[' << *i << ']';
2015-04-22 18:36:40 +02:00
++valueNamesPrint;
}
2016-06-12 01:56:57 +02:00
if(requiredValueCount() == static_cast<size_t>(-1)) {
os << " ...";
} else {
for(; valueNamesPrint < requiredValueCount(); ++valueNamesPrint) {
os << " [value " << (valueNamesPrint + 1) << ']';
}
2015-04-22 18:36:40 +02:00
}
}
++indentionLevel;
if(notEmpty(description())) {
2015-04-22 18:36:40 +02:00
os << endl;
2016-06-12 01:56:57 +02:00
for(unsigned char i = 0; i < indentionLevel; ++i) os << ' ' << ' ';
2015-04-22 18:36:40 +02:00
os << description();
}
if(isRequired()) {
os << endl;
2016-06-12 01:56:57 +02:00
for(unsigned char i = 0; i < indentionLevel; ++i) os << ' ' << ' ';
2015-04-22 18:36:40 +02:00
os << "This argument is required.";
}
if(notEmpty(example())) {
2016-06-12 01:56:57 +02:00
for(unsigned char i = 0; i < indentionLevel; ++i) os << ' ' << ' ';
2015-10-06 22:27:16 +02:00
os << endl << "Usage: " << example();
}
2015-04-22 18:36:40 +02:00
os << endl;
for(const auto *arg : subArguments()) {
2015-04-22 18:36:40 +02:00
arg->printInfo(os, indentionLevel + 1);
}
}
/*!
2016-02-27 01:19:16 +01:00
* \brief This function return the first present and uncombinable argument of the given list of arguments.
*
2015-04-22 18:36:40 +02:00
* The Argument \a except will be ignored.
*/
Argument *firstPresentUncombinableArg(const ArgumentVector &args, const Argument *except)
{
for(Argument *arg : args) {
if(arg != except && arg->isPresent() && !arg->isCombinable()) {
return arg;
}
}
return nullptr;
}
/*!
2016-02-27 01:19:16 +01:00
* \brief Sets the secondary arguments for this arguments.
*
* The given arguments will be considered as secondary arguments of this argument by the argument parser.
* This means that the parser will complain if these arguments are given, but not this argument.
* If secondary arguments are labeled as mandatory their parent is also mandatory.
2015-04-22 18:36:40 +02:00
*
* The Argument does not take ownership. Do not destroy the given arguments as long as they are
* used as secondary arguments.
*
* \sa secondaryArguments()
* \sa addSecondaryArgument()
2015-04-22 18:36:40 +02:00
* \sa hasSecondaryArguments()
*/
void Argument::setSubArguments(const ArgumentInitializerList &secondaryArguments)
2015-04-22 18:36:40 +02:00
{
// remove this argument from the parents list of the previous secondary arguments
for(Argument *arg : m_subArgs) {
2015-04-22 18:36:40 +02:00
arg->m_parents.erase(remove(arg->m_parents.begin(), arg->m_parents.end(), this), arg->m_parents.end());
}
// assign secondary arguments
m_subArgs.assign(secondaryArguments);
2015-04-22 18:36:40 +02:00
// add this argument to the parents list of the assigned secondary arguments
// and set the parser
for(Argument *arg : m_subArgs) {
2015-04-22 18:36:40 +02:00
if(find(arg->m_parents.cbegin(), arg->m_parents.cend(), this) == arg->m_parents.cend()) {
arg->m_parents.push_back(this);
}
}
}
/*!
2016-02-27 01:19:16 +01:00
* \brief Adds \a arg as a secondary argument for this argument.
*
* \sa secondaryArguments()
* \sa setSecondaryArguments()
* \sa hasSecondaryArguments()
*/
void Argument::addSubArgument(Argument *arg)
{
if(find(m_subArgs.cbegin(), m_subArgs.cend(), arg) == m_subArgs.cend()) {
m_subArgs.push_back(arg);
if(find(arg->m_parents.cbegin(), arg->m_parents.cend(), this) == arg->m_parents.cend()) {
arg->m_parents.push_back(this);
}
}
}
2015-04-22 18:36:40 +02:00
/*!
2016-06-12 01:56:57 +02:00
* \brief Returns whether at least one parent argument is present.
* \remarks Returns always true for main arguments.
2015-04-22 18:36:40 +02:00
*/
bool Argument::isParentPresent() const
{
2016-06-12 01:56:57 +02:00
if(isMainArgument()) {
return true;
}
for(const Argument *parent : m_parents) {
2015-04-22 18:36:40 +02:00
if(parent->isPresent()) {
return true;
}
}
return false;
}
/*!
2016-02-27 01:19:16 +01:00
* \brief Checks if this arguments conflicts with other arguments.
*
* If the argument is in conflict with an other argument this argument will be returned.
* Otherwise nullptr will be returned.
2015-04-22 18:36:40 +02:00
*/
Argument *Argument::conflictsWithArgument() const
{
if(!isCombinable() && isPresent()) {
for(Argument *parent : m_parents) {
for(Argument *sibling : parent->subArguments()) {
2015-04-22 18:36:40 +02:00
if(sibling != this && sibling->isPresent() && !sibling->isCombinable()) {
return sibling;
}
}
}
}
return nullptr;
}
2016-06-12 01:56:57 +02:00
/*!
* \brief Resets occurrences and values.
*/
void Argument::reset()
{
m_indices.clear();
m_values.clear();
}
2015-04-22 18:36:40 +02:00
/*!
* \class ApplicationUtilities::ArgumentParser
* \brief The ArgumentParser class provides a means for handling command line arguments.
*
* To setup the parser create instances of ApplicationUtilities::Argument to define a
* set of known arguments and assign these to the parser using setMainArguments().
*
* To invoke parsing call parseArgs(). The parser will verify the previously
* assigned definitions (and might throw std::invalid_argument) and then parse the
* given command line arguments according the definitions (and might throw
* ApplicationUtilities::Failure).
*/
/*!
2016-02-27 01:19:16 +01:00
* \brief Constructs a new ArgumentParser.
2015-04-22 18:36:40 +02:00
*/
ArgumentParser::ArgumentParser() :
m_actualArgc(0),
m_executable(nullptr),
m_ignoreUnknownArgs(false),
m_defaultArg(nullptr)
2015-04-22 18:36:40 +02:00
{}
/*!
2016-02-27 01:19:16 +01:00
* \brief Sets the main arguments for the parser. The parser will use these argument definitions
* to when parsing the command line arguments and when printing help information.
* \remarks
* - The parser does not take ownership. Do not destroy the arguments as long as they are used as
* main arguments.
* - Sets the first specified argument as default argument if none is assigned yet and the
* first argument has no mandatory sub arguments.
2015-04-22 18:36:40 +02:00
*/
void ArgumentParser::setMainArguments(const ArgumentInitializerList &mainArguments)
{
if(mainArguments.size()) {
for(Argument *arg : mainArguments) {
arg->m_isMainArg = true;
}
m_mainArgs.assign(mainArguments);
if(!m_defaultArg) {
if(!(*mainArguments.begin())->requiredValueCount()) {
bool subArgsRequired = false;
for(Argument *subArg : (*mainArguments.begin())->subArguments()) {
if(subArg->isRequired()) {
subArgsRequired = true;
break;
}
}
if(!subArgsRequired) {
m_defaultArg = *mainArguments.begin();
}
}
}
} else {
m_mainArgs.clear();
2015-04-22 18:36:40 +02:00
}
}
/*!
2016-02-27 01:19:16 +01:00
* \brief Adds the specified \a argument to the main argument.
* \remarks
* The parser does not take ownership. Do not destroy the argument as long as it is used as
* main argument.
*/
void ArgumentParser::addMainArgument(Argument *argument)
{
argument->m_isMainArg = true;
m_mainArgs.push_back(argument);
}
/*!
* \brief Prints help information for all main arguments which have been set using setMainArguments().
2015-04-22 18:36:40 +02:00
*/
void ArgumentParser::printHelp(ostream &os) const
{
2015-08-25 19:12:05 +02:00
if(applicationName && *applicationName) {
os << applicationName;
if(applicationVersion && *applicationVersion) {
os << ',' << ' ';
}
}
if(applicationVersion && *applicationVersion) {
os << "version " << applicationVersion;
}
if((applicationName && *applicationName) || (applicationVersion && *applicationVersion)) {
os << '\n' << '\n';
}
if(!m_mainArgs.empty()) {
os << "Available arguments:\n";
2015-10-06 22:27:16 +02:00
for(const auto *arg : m_mainArgs) {
2015-08-25 19:12:05 +02:00
arg->printInfo(os);
}
2015-04-22 18:36:40 +02:00
}
2015-08-25 19:12:05 +02:00
if(applicationUrl && *applicationUrl) {
os << "\nProject website: " << applicationUrl << endl;
2015-04-22 18:36:40 +02:00
}
}
/*!
2016-02-27 01:19:16 +01:00
* \brief Returns the first argument definition which matches the predicate.
*
2015-04-22 18:36:40 +02:00
* The search includes all assigned main argument definitions and their sub arguments.
*/
Argument *ArgumentParser::findArg(const ArgumentPredicate &predicate) const
{
return findArg(m_mainArgs, predicate);
}
/*!
2016-02-27 01:19:16 +01:00
* \brief Returns the first argument definition which matches the predicate.
*
2015-04-22 18:36:40 +02:00
* The search includes all provided \a arguments and their sub arguments.
*/
Argument *ArgumentParser::findArg(const ArgumentVector &arguments, const ArgumentPredicate &predicate)
{
for(Argument *arg : arguments) {
if(predicate(arg)) {
return arg; // argument matches
} else if(Argument *subarg = findArg(arg->subArguments(), predicate)) {
2015-04-22 18:36:40 +02:00
return subarg; // a secondary argument matches
}
}
return nullptr; // no argument matches
}
2016-06-12 01:56:57 +02:00
/*!
* \brief Parses the specified command line arguments.
* \remarks
* - The results are stored in the Argument instances assigned as main arguments and sub arguments.
* - Calls the assigned callbacks if no constraints are violated.
* \throws Throws Failure if the specified arguments violate the constraints defined
* by the Argument instances.
*/
2016-06-17 17:55:09 +02:00
void ArgumentParser::parseArgs(int argc, const char *const *argv)
2016-06-12 01:56:57 +02:00
{
IF_DEBUG_BUILD(verifyArgs(m_mainArgs);)
2016-06-23 00:58:43 +02:00
m_actualArgc = 0;
if(argc) {
m_executable = *argv;
if(--argc) {
size_t index = 0;
++argv;
2016-06-23 00:58:43 +02:00
vector<Argument *> path;
path.reserve(4);
readSpecifiedArgs(m_mainArgs, index, argv, argv + argc, path);
} else {
// no arguments specified -> set default argument as present
if(m_defaultArg) {
m_defaultArg->m_indices.push_back(0);
m_defaultArg->m_values.emplace_back();
}
}
2016-06-12 01:56:57 +02:00
checkConstraints(m_mainArgs);
invokeCallbacks(m_mainArgs);
} else {
m_executable = nullptr;
2016-06-12 01:56:57 +02:00
}
}
#ifdef DEBUG_BUILD
2015-04-22 18:36:40 +02:00
/*!
2016-06-12 01:56:57 +02:00
* \brief Verifies the specified \a argument definitions.
2015-04-22 18:36:40 +02:00
*
2016-06-12 01:56:57 +02:00
* Asserts that
* - The same argument has not been added twice to the same parent.
* - Only one argument within a parent is default or implicit.
* - Only main arguments denote operations.
* - Argument abbreviations are unique within one parent.
* - Argument names are unique within one parent.
2015-04-22 18:36:40 +02:00
*
2016-06-12 01:56:57 +02:00
* \remarks
* - Verifies the sub arguments, too.
* - For debugging purposes only; hence only available in debug builds.
2015-04-22 18:36:40 +02:00
*/
2016-06-12 01:56:57 +02:00
void ApplicationUtilities::ArgumentParser::verifyArgs(const ArgumentVector &args)
2015-04-22 18:36:40 +02:00
{
2016-06-12 01:56:57 +02:00
vector<const Argument *> verifiedArgs;
verifiedArgs.reserve(args.size());
vector<char> abbreviations;
abbreviations.reserve(args.size());
2015-04-22 18:36:40 +02:00
vector<string> names;
2016-06-12 01:56:57 +02:00
names.reserve(args.size());
bool hasImplicit = false;
2016-06-12 01:56:57 +02:00
for(const Argument *arg : args) {
assert(find(verifiedArgs.cbegin(), verifiedArgs.cend(), arg) == verifiedArgs.cend());
verifiedArgs.push_back(arg);
assert(arg->isMainArgument() || !arg->denotesOperation());
assert(!arg->isImplicit() || !hasImplicit);
hasImplicit |= arg->isImplicit();
2016-06-12 01:56:57 +02:00
assert(!arg->abbreviation() || find(abbreviations.cbegin(), abbreviations.cend(), arg->abbreviation()) == abbreviations.cend());
abbreviations.push_back(arg->abbreviation());
assert(!arg->name() || find(names.cbegin(), names.cend(), arg->name()) == names.cend());
assert(arg->requiredValueCount() == 0 || arg->subArguments().size() == 0);
names.emplace_back(arg->name());
verifyArgs(arg->subArguments());
}
2015-04-22 18:36:40 +02:00
}
#endif
2015-04-22 18:36:40 +02:00
/*!
2016-06-12 01:56:57 +02:00
* \brief Reads the specified commands line arguments.
* \remarks Results are stored in Argument instances added as main arguments and sub arguments.
2015-04-22 18:36:40 +02:00
*/
2016-06-23 00:58:43 +02:00
void ArgumentParser::readSpecifiedArgs(ArgumentVector &args, std::size_t &index, const char *const *&argv, const char *const *end, std::vector<Argument *> &currentPath)
2015-04-22 18:36:40 +02:00
{
2016-06-12 01:56:57 +02:00
Argument *lastArg = nullptr;
vector<const char *> *values = nullptr;
while(argv != end) {
if(values && lastArg->requiredValueCount() != static_cast<size_t>(-1) && values->size() < lastArg->requiredValueCount()) {
// there are still values to read
values->emplace_back(*argv);
++index, ++argv;
} else {
// determine denotation type
const char *argDenotation = *argv;
2016-06-23 22:06:59 +02:00
if(!*argDenotation && (!lastArg || values->size() >= lastArg->requiredValueCount())) {
// skip empty arguments
++index, ++argv;
continue;
}
2016-06-12 01:56:57 +02:00
bool abbreviationFound = false;
unsigned char argDenotationType = Value;
*argDenotation == '-' && (++argDenotation, ++argDenotationType)
&& *argDenotation == '-' && (++argDenotation, ++argDenotationType);
// try to find matching Argument instance
Argument *matchingArg = nullptr;
2016-06-23 00:58:43 +02:00
size_t argDenLen;
2016-06-12 01:56:57 +02:00
if(argDenotationType != Value) {
const char *const equationPos = strchr(argDenotation, '=');
2016-06-23 00:58:43 +02:00
for(argDenLen = equationPos ? static_cast<size_t>(equationPos - argDenotation) : strlen(argDenotation); ; matchingArg = nullptr) {
2016-06-12 01:56:57 +02:00
// search for arguments by abbreviation or name depending on the denotation type
if(argDenotationType == Abbreviation) {
for(Argument *arg : args) {
if(arg->abbreviation() && arg->abbreviation() == *argDenotation) {
matchingArg = arg;
abbreviationFound = true;
break;
2015-04-22 18:36:40 +02:00
}
}
} else {
2016-06-12 01:56:57 +02:00
for(Argument *arg : args) {
if(arg->name() && !strncmp(arg->name(), argDenotation, argDenLen)) {
matchingArg = arg;
break;
}
}
}
if(matchingArg) {
// an argument matched the specified denotation
matchingArg->m_indices.push_back(index);
// prepare reading parameter values
matchingArg->m_values.emplace_back();
values = &matchingArg->m_values.back();
if(equationPos) {
values->push_back(equationPos + 1);
2015-04-22 18:36:40 +02:00
}
2016-06-12 01:56:57 +02:00
// read sub arguments if no abbreviated argument follows
++index, ++m_actualArgc, lastArg = matchingArg;
if(argDenotationType != Abbreviation || (!*++argDenotation && argDenotation != equationPos)) {
2016-06-23 00:58:43 +02:00
currentPath.push_back(matchingArg);
readSpecifiedArgs(matchingArg->m_subArgs, index, ++argv, end, currentPath);
currentPath.pop_back();
2016-06-12 01:56:57 +02:00
break;
} // else: another abbreviated argument follows
} else {
break;
2015-04-22 18:36:40 +02:00
}
2016-06-12 01:56:57 +02:00
}
}
if(!matchingArg) {
2016-06-23 00:58:43 +02:00
if(argDenotationType != Value) {
// unknown argument might be a sibling of the parent element
for(auto parentArgument = currentPath.crbegin(), end = currentPath.crend(); parentArgument != end; ) {
for(Argument *sibling : (++parentArgument != end ? (*parentArgument)->subArguments() : m_mainArgs)) {
if(sibling->occurrences() < sibling->maxOccurrences()) {
if((argDenotationType == Abbreviation && (sibling->abbreviation() && sibling->abbreviation() == *argDenotation))
|| (sibling->name() && !strncmp(sibling->name(), argDenotation, argDenLen))) {
return;
}
}
}
}
}
2016-06-12 01:56:57 +02:00
if(lastArg && values->size() < lastArg->requiredValueCount()) {
2016-06-23 00:58:43 +02:00
// unknown argument might just be a parameter of the last argument
2016-06-12 01:56:57 +02:00
values->emplace_back(abbreviationFound ? argDenotation : *argv);
++index, ++argv;
continue;
2016-06-23 00:58:43 +02:00
}
// first value might denote "operation"
if(currentPath.empty()) {
for(Argument *arg : args) {
if(arg->denotesOperation() && arg->name() && !strcmp(arg->name(), *argv)) {
(matchingArg = arg)->m_indices.push_back(index);
++index, ++argv;
break;
2015-04-22 18:36:40 +02:00
}
}
2016-06-23 00:58:43 +02:00
}
2016-06-12 01:56:57 +02:00
2016-06-23 00:58:43 +02:00
if(!matchingArg) {
// use the first default argument which is not already present
for(Argument *arg : args) {
if(arg->isImplicit() && !arg->isPresent()) {
(matchingArg = arg)->m_indices.push_back(index);
break;
2015-04-22 18:36:40 +02:00
}
2016-06-12 01:56:57 +02:00
}
2016-06-23 00:58:43 +02:00
}
2016-06-12 01:56:57 +02:00
2016-06-23 00:58:43 +02:00
if(matchingArg) {
// an argument matched the specified denotation
if(lastArg == matchingArg) {
break;
2016-06-12 01:56:57 +02:00
}
2016-06-23 00:58:43 +02:00
// prepare reading parameter values
matchingArg->m_values.emplace_back();
values = &matchingArg->m_values.back();
// read sub arguments
++m_actualArgc, lastArg = matchingArg;
currentPath.push_back(matchingArg);
readSpecifiedArgs(matchingArg->m_subArgs, index, argv, end, currentPath);
currentPath.pop_back();
continue;
2016-06-12 01:56:57 +02:00
}
2016-06-23 00:58:43 +02:00
if(currentPath.empty()) {
2016-06-12 01:56:57 +02:00
if(m_ignoreUnknownArgs) {
cerr << "The specified argument \"" << *argv << "\" is unknown and will be ignored." << endl;
++index, ++argv;
2015-04-22 18:36:40 +02:00
} else {
2016-06-12 01:56:57 +02:00
throw Failure("The specified argument \"" + string(*argv) + "\" is unknown and will be ignored.");
2015-04-22 18:36:40 +02:00
}
2016-06-12 01:56:57 +02:00
} else {
return; // unknown argument name or abbreviation found -> continue with parent level
2015-04-22 18:36:40 +02:00
}
}
}
}
2016-06-12 01:56:57 +02:00
}
/*!
* \brief Checks the constrains of the specified \a args.
* \remarks Checks the contraints of sub arguments, too.
*/
void ArgumentParser::checkConstraints(const ArgumentVector &args)
{
for(const Argument *arg : args) {
const auto occurrences = arg->occurrences();
if(arg->isParentPresent() && occurrences > arg->maxOccurrences()) {
throw Failure("The argument \"" + string(arg->name()) + "\" mustn't be specified more than " + numberToString(arg->maxOccurrences()) + (arg->maxOccurrences() == 1 ? " time." : " times."));
}
2016-06-12 01:56:57 +02:00
if(arg->isParentPresent() && occurrences < arg->minOccurrences()) {
throw Failure("The argument \"" + string(arg->name()) + "\" must be specified at least " + numberToString(arg->minOccurrences()) + (arg->minOccurrences() == 1 ? " time." : " times."));
}
Argument *conflictingArgument = nullptr;
if(arg->isMainArgument()) {
if(!arg->isCombinable() && arg->isPresent()) {
conflictingArgument = firstPresentUncombinableArg(m_mainArgs, arg);
2015-04-22 18:36:40 +02:00
}
2016-06-12 01:56:57 +02:00
} else {
conflictingArgument = arg->conflictsWithArgument();
}
if(conflictingArgument) {
throw Failure("The argument \"" + string(conflictingArgument->name()) + "\" can not be combined with \"" + arg->name() + "\".");
}
for(size_t i = 0; i != occurrences; ++i) {
if(!arg->allRequiredValuesPresent(i)) {
2015-04-22 18:36:40 +02:00
stringstream ss(stringstream::in | stringstream::out);
2016-06-12 01:56:57 +02:00
ss << "Not all parameter for argument \"" << arg->name() << "\" ";
if(i) {
ss << " (" << (i + 1) << " occurrence) ";
}
ss << "provided. You have to provide the following parameter:";
size_t valueNamesPrint = 0;
2015-04-22 18:36:40 +02:00
for(const auto &name : arg->m_valueNames) {
ss << ' ' << name, ++valueNamesPrint;
2015-04-22 18:36:40 +02:00
}
2016-06-12 01:56:57 +02:00
if(arg->m_requiredValueCount != static_cast<size_t>(-1)) {
while(valueNamesPrint < arg->m_requiredValueCount) {
ss << "\nvalue " << (++valueNamesPrint);
}
2015-04-22 18:36:40 +02:00
}
throw Failure(ss.str());
}
}
2016-06-12 01:56:57 +02:00
// check contraints of sub arguments recursively
checkConstraints(arg->m_subArgs);
}
}
/*!
* \brief Invokes the callbacks for the specified \a args.
* \remarks
* - Checks the callbacks for sub arguments, too.
* - Invokes the assigned callback methods for each occurance of
* the argument.
*/
void ArgumentParser::invokeCallbacks(const ArgumentVector &args)
{
for(const Argument *arg : args) {
// invoke the callback for each occurance of the argument
2015-04-22 18:36:40 +02:00
if(arg->m_callbackFunction) {
2016-06-12 01:56:57 +02:00
for(const auto &valuesOfOccurance : arg->m_values) {
arg->m_callbackFunction(valuesOfOccurance);
2015-04-22 18:36:40 +02:00
}
}
2016-06-12 01:56:57 +02:00
// invoke the callbacks for sub arguments recursively
invokeCallbacks(arg->m_subArgs);
}
2015-04-22 18:36:40 +02:00
}
/*!
* \class HelpArgument
2015-04-22 18:36:40 +02:00
* \brief The HelpArgument class prints help information for an argument parser
* when present (--help, -h).
*/
/*!
* \brief Constructs a new help argument for the specified parser.
*/
HelpArgument::HelpArgument(ArgumentParser &parser) :
2016-06-12 01:56:57 +02:00
Argument("help", 'h', "shows this information")
2015-04-22 18:36:40 +02:00
{
2016-06-12 01:56:57 +02:00
setCallback([&parser] (const std::vector<const char *> &) {
CMD_UTILS_START_CONSOLE;
parser.printHelp(cout);
});
2015-04-22 18:36:40 +02:00
}
}