mirror of
https://github.com/KeqingMoe/argparse.git
synced 2025-07-04 07:04:39 +00:00
commit
16ef9d84e0
@ -49,29 +49,11 @@ struct is_specialization : std::false_type {};
|
|||||||
template<template<typename...> class Ref, typename... Args>
|
template<template<typename...> class Ref, typename... Args>
|
||||||
struct is_specialization<Ref<Args...>, Ref> : std::true_type {};
|
struct is_specialization<Ref<Args...>, Ref> : std::true_type {};
|
||||||
|
|
||||||
// Upsert into std::map
|
|
||||||
template <class KeyType, class ElementType>
|
|
||||||
bool upsert(std::map<KeyType, ElementType>& aMap, KeyType const& aKey, ElementType const& aNewValue) {
|
|
||||||
typedef typename std::map<KeyType, ElementType>::iterator Iterator;
|
|
||||||
typedef typename std::pair<Iterator, bool> Result;
|
|
||||||
Result tResult = aMap.insert(typename std::map<KeyType, ElementType>::value_type(aKey, aNewValue));
|
|
||||||
if (!tResult.second) {
|
|
||||||
if (!(tResult.first->second == aNewValue)) {
|
|
||||||
tResult.first->second = aNewValue;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return false; // it was the same
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return true; // changed cause not existing
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if string (haystack) starts with a substring (needle)
|
// Check if string (haystack) starts with a substring (needle)
|
||||||
bool starts_with(const std::string& haystack, const std::string& needle) {
|
bool starts_with(const std::string& haystack, const std::string& needle) {
|
||||||
return needle.length() <= haystack.length()
|
return needle.length() <= haystack.length()
|
||||||
&& std::equal(needle.begin(), needle.end(), haystack.begin());
|
&& std::equal(needle.begin(), needle.end(), haystack.begin());
|
||||||
};
|
}
|
||||||
|
|
||||||
// Get value at index from std::list
|
// Get value at index from std::list
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@ -87,16 +69,13 @@ T get_from_list(const std::list<T>& aList, size_t aIndex) {
|
|||||||
class Argument {
|
class Argument {
|
||||||
friend class ArgumentParser;
|
friend class ArgumentParser;
|
||||||
public:
|
public:
|
||||||
Argument() :
|
Argument() = default;
|
||||||
mNames({}),
|
|
||||||
mUsedName(""),
|
template <typename ...Args>
|
||||||
mHelp(""),
|
explicit Argument(Args... args)
|
||||||
mAction([](const std::string& aValue) { return aValue; }),
|
: mNames({std::move(args)...})
|
||||||
mValues({}),
|
, mIsOptional((is_optional(args) || ...))
|
||||||
mRawValues({}),
|
{}
|
||||||
mNumArgs(1),
|
|
||||||
mIsOptional(false),
|
|
||||||
mIsUsed(false) {}
|
|
||||||
|
|
||||||
Argument& help(const std::string& aHelp) {
|
Argument& help(const std::string& aHelp) {
|
||||||
mHelp = aHelp;
|
mHelp = aHelp;
|
||||||
@ -104,18 +83,18 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
Argument& default_value(std::any aDefaultValue) {
|
Argument& default_value(std::any aDefaultValue) {
|
||||||
mDefaultValue = aDefaultValue;
|
mDefaultValue = std::move(aDefaultValue);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Argument& implicit_value(std::any aImplicitValue) {
|
Argument& implicit_value(std::any aImplicitValue) {
|
||||||
mImplicitValue = aImplicitValue;
|
mImplicitValue = std::move(aImplicitValue);
|
||||||
mNumArgs = 0;
|
mNumArgs = 0;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Argument& action(std::function<std::any(const std::string&)> aAction) {
|
Argument& action(std::function<std::any(const std::string&)> aAction) {
|
||||||
mAction = aAction;
|
mAction = std::move(aAction);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,8 +110,8 @@ public:
|
|||||||
|
|
||||||
// Entry point for template types other than std::vector and std::list
|
// Entry point for template types other than std::vector and std::list
|
||||||
template <typename T>
|
template <typename T>
|
||||||
typename std::enable_if<is_specialization<T, std::vector>::value == false &&
|
typename std::enable_if<!is_specialization<T, std::vector>::value &&
|
||||||
is_specialization<T, std::list>::value == false, bool>::type
|
!is_specialization<T, std::list>::value, bool>::type
|
||||||
operator==(const T& aRhs) const {
|
operator==(const T& aRhs) const {
|
||||||
return get<T>() == aRhs;
|
return get<T>() == aRhs;
|
||||||
}
|
}
|
||||||
@ -172,11 +151,15 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// If an argument starts with "-" or "--", then it's optional
|
||||||
|
static bool is_optional(const std::string& aName) {
|
||||||
|
return (starts_with(aName, "--") || starts_with(aName, "-"));
|
||||||
|
}
|
||||||
|
|
||||||
// Getter for template types other than std::vector and std::list
|
// Getter for template types other than std::vector and std::list
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T get() const {
|
T get() const {
|
||||||
if (mValues.size() == 0) {
|
if (mValues.empty()) {
|
||||||
if (mDefaultValue.has_value()) {
|
if (mDefaultValue.has_value()) {
|
||||||
return std::any_cast<T>(mDefaultValue);
|
return std::any_cast<T>(mDefaultValue);
|
||||||
}
|
}
|
||||||
@ -184,7 +167,7 @@ public:
|
|||||||
return T();
|
return T();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (mRawValues.size() > 0)
|
if (!mRawValues.empty())
|
||||||
return std::any_cast<T>(mValues[0]);
|
return std::any_cast<T>(mValues[0]);
|
||||||
else {
|
else {
|
||||||
if (mDefaultValue.has_value())
|
if (mDefaultValue.has_value())
|
||||||
@ -199,11 +182,11 @@ public:
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
T get_vector() const {
|
T get_vector() const {
|
||||||
T tResult;
|
T tResult;
|
||||||
if (mValues.size() == 0) {
|
if (mValues.empty()) {
|
||||||
if (mDefaultValue.has_value()) {
|
if (mDefaultValue.has_value()) {
|
||||||
T tDefaultValues = std::any_cast<T>(mDefaultValue);
|
T tDefaultValues = std::any_cast<T>(mDefaultValue);
|
||||||
for (size_t i = 0; i < tDefaultValues.size(); i++) {
|
for (size_t i = 0; i < tDefaultValues.size(); i++) {
|
||||||
tResult.push_back(std::any_cast<typename T::value_type>(tDefaultValues[i]));
|
tResult.emplace_back(std::any_cast<typename T::value_type>(tDefaultValues[i]));
|
||||||
}
|
}
|
||||||
return tResult;
|
return tResult;
|
||||||
}
|
}
|
||||||
@ -211,9 +194,9 @@ public:
|
|||||||
return T();
|
return T();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (mRawValues.size() > 0) {
|
if (!mRawValues.empty()) {
|
||||||
for (size_t i = 0; i < mValues.size(); i++) {
|
for (const auto& mValue : mValues) {
|
||||||
tResult.push_back(std::any_cast<typename T::value_type>(mValues[i]));
|
tResult.emplace_back(std::any_cast<typename T::value_type>(mValue));
|
||||||
}
|
}
|
||||||
return tResult;
|
return tResult;
|
||||||
}
|
}
|
||||||
@ -221,7 +204,7 @@ public:
|
|||||||
if (mDefaultValue.has_value()) {
|
if (mDefaultValue.has_value()) {
|
||||||
std::vector<T> tDefaultValues = std::any_cast<std::vector<T>>(mDefaultValue);
|
std::vector<T> tDefaultValues = std::any_cast<std::vector<T>>(mDefaultValue);
|
||||||
for (size_t i = 0; i < tDefaultValues.size(); i++) {
|
for (size_t i = 0; i < tDefaultValues.size(); i++) {
|
||||||
tResult.push_back(std::any_cast<typename T::value_type>(tDefaultValues[i]));
|
tResult.emplace_back(std::any_cast<typename T::value_type>(tDefaultValues[i]));
|
||||||
}
|
}
|
||||||
return tResult;
|
return tResult;
|
||||||
}
|
}
|
||||||
@ -235,11 +218,11 @@ public:
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
T get_list() const {
|
T get_list() const {
|
||||||
T tResult;
|
T tResult;
|
||||||
if (mValues.size() == 0) {
|
if (mValues.empty()) {
|
||||||
if (mDefaultValue.has_value()) {
|
if (mDefaultValue.has_value()) {
|
||||||
T tDefaultValues = std::any_cast<T>(mDefaultValue);
|
T tDefaultValues = std::any_cast<T>(mDefaultValue);
|
||||||
for (size_t i = 0; i < tDefaultValues.size(); i++) {
|
for (size_t i = 0; i < tDefaultValues.size(); i++) {
|
||||||
tResult.push_back(std::any_cast<typename T::value_type>(get_from_list(tDefaultValues, i)));
|
tResult.emplace_back(std::any_cast<typename T::value_type>(get_from_list(tDefaultValues, i)));
|
||||||
}
|
}
|
||||||
return tResult;
|
return tResult;
|
||||||
}
|
}
|
||||||
@ -247,9 +230,9 @@ public:
|
|||||||
return T();
|
return T();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (mRawValues.size() > 0) {
|
if (!mRawValues.empty()) {
|
||||||
for (size_t i = 0; i < mValues.size(); i++) {
|
for (const auto& mValue : mValues) {
|
||||||
tResult.push_back(std::any_cast<typename T::value_type>(mValues[i]));
|
tResult.emplace_back(std::any_cast<typename T::value_type>(mValue));
|
||||||
}
|
}
|
||||||
return tResult;
|
return tResult;
|
||||||
}
|
}
|
||||||
@ -257,7 +240,7 @@ public:
|
|||||||
if (mDefaultValue.has_value()) {
|
if (mDefaultValue.has_value()) {
|
||||||
std::list<T> tDefaultValues = std::any_cast<std::list<T>>(mDefaultValue);
|
std::list<T> tDefaultValues = std::any_cast<std::list<T>>(mDefaultValue);
|
||||||
for (size_t i = 0; i < tDefaultValues.size(); i++) {
|
for (size_t i = 0; i < tDefaultValues.size(); i++) {
|
||||||
tResult.push_back(std::any_cast<typename T::value_type>(get_from_list(tDefaultValues, i)));
|
tResult.emplace_back(std::any_cast<typename T::value_type>(get_from_list(tDefaultValues, i)));
|
||||||
}
|
}
|
||||||
return tResult;
|
return tResult;
|
||||||
}
|
}
|
||||||
@ -272,70 +255,60 @@ public:
|
|||||||
std::string mHelp;
|
std::string mHelp;
|
||||||
std::any mDefaultValue;
|
std::any mDefaultValue;
|
||||||
std::any mImplicitValue;
|
std::any mImplicitValue;
|
||||||
std::function<std::any(const std::string&)> mAction;
|
std::function<std::any(const std::string&)> mAction = [](const std::string& aValue) { return aValue; };
|
||||||
std::vector<std::any> mValues;
|
std::vector<std::any> mValues;
|
||||||
std::vector<std::string> mRawValues;
|
std::vector<std::string> mRawValues;
|
||||||
size_t mNumArgs;
|
size_t mNumArgs = 1;
|
||||||
bool mIsOptional;
|
bool mIsOptional = false;
|
||||||
bool mIsUsed; // relevant for optional arguments. True if used by user
|
bool mIsUsed = false; // relevant for optional arguments. True if used by user
|
||||||
};
|
};
|
||||||
|
|
||||||
class ArgumentParser {
|
class ArgumentParser {
|
||||||
public:
|
public:
|
||||||
ArgumentParser(const std::string& aProgramName = "") :
|
explicit ArgumentParser(std::string aProgramName = {}) :
|
||||||
mProgramName(aProgramName),
|
mProgramName(std::move(aProgramName))
|
||||||
mNextPositionalArgument(0) {
|
{
|
||||||
std::shared_ptr<Argument> tArgument = std::make_shared<Argument>();
|
std::shared_ptr<Argument> tArgument = std::make_shared<Argument>("-h", "--help");
|
||||||
tArgument->mNames = { "-h", "--help" };
|
|
||||||
tArgument->mHelp = "show this help message and exit";
|
tArgument->mHelp = "show this help message and exit";
|
||||||
tArgument->mNumArgs = 0;
|
tArgument->mNumArgs = 0;
|
||||||
tArgument->mDefaultValue = false;
|
tArgument->mDefaultValue = false;
|
||||||
tArgument->mImplicitValue = true;
|
tArgument->mImplicitValue = true;
|
||||||
mOptionalArguments.push_back(tArgument);
|
mOptionalArguments.emplace_back(tArgument);
|
||||||
upsert(mArgumentMap, std::string("-h"), tArgument);
|
mArgumentMap.insert_or_assign(std::string("-h"), tArgument);
|
||||||
upsert(mArgumentMap, std::string("--help"), tArgument);
|
mArgumentMap.insert_or_assign(std::string("--help"), tArgument);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parameter packing
|
// Parameter packing
|
||||||
// Call add_argument with variadic number of string arguments
|
// Call add_argument with variadic number of string arguments
|
||||||
// TODO: enforce T to be std::string
|
template<typename... Targs>
|
||||||
template<typename T, typename... Targs>
|
Argument& add_argument(Targs... Fargs) {
|
||||||
Argument& add_argument(T value, Targs... Fargs) {
|
std::shared_ptr<Argument> tArgument = std::make_shared<Argument>(std::move(Fargs)...);
|
||||||
std::shared_ptr<Argument> tArgument = std::make_shared<Argument>();
|
|
||||||
tArgument->mNames.push_back(value);
|
|
||||||
add_argument_internal(tArgument, Fargs...);
|
|
||||||
|
|
||||||
for (auto& mName : tArgument->mNames) {
|
|
||||||
if (is_optional(mName))
|
|
||||||
tArgument->mIsOptional = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!tArgument->mIsOptional)
|
if (!tArgument->mIsOptional)
|
||||||
mPositionalArguments.push_back(tArgument);
|
mPositionalArguments.emplace_back(tArgument);
|
||||||
else
|
else
|
||||||
mOptionalArguments.push_back(tArgument);
|
mOptionalArguments.emplace_back(tArgument);
|
||||||
|
|
||||||
for (auto& mName : tArgument->mNames) {
|
for (auto& mName : tArgument->mNames) {
|
||||||
upsert(mArgumentMap, mName, tArgument);
|
mArgumentMap.insert_or_assign(mName, tArgument);
|
||||||
}
|
}
|
||||||
return *tArgument;
|
return *tArgument;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Base case for add_parents parameter packing
|
// Base case for add_parents parameter packing
|
||||||
void add_parents() {
|
void add_parents() {
|
||||||
for (size_t i = 0; i < mParentParsers.size(); i++) {
|
for (const auto& tParentParser : mParentParsers) {
|
||||||
auto tParentParser = mParentParsers[i];
|
|
||||||
auto tPositionalArguments = tParentParser.mPositionalArguments;
|
auto tPositionalArguments = tParentParser.mPositionalArguments;
|
||||||
for (auto& tArgument : tPositionalArguments) {
|
for (auto& tArgument : tPositionalArguments) {
|
||||||
mPositionalArguments.push_back(tArgument);
|
mPositionalArguments.emplace_back(tArgument);
|
||||||
}
|
}
|
||||||
auto tOptionalArguments = tParentParser.mOptionalArguments;
|
auto tOptionalArguments = tParentParser.mOptionalArguments;
|
||||||
for (auto& tArgument : tOptionalArguments) {
|
for (auto& tArgument : tOptionalArguments) {
|
||||||
mOptionalArguments.push_back(tArgument);
|
mOptionalArguments.emplace_back(tArgument);
|
||||||
}
|
}
|
||||||
auto tArgumentMap = tParentParser.mArgumentMap;
|
auto tArgumentMap = tParentParser.mArgumentMap;
|
||||||
for (auto&[tKey, tValue] : tArgumentMap) {
|
for (auto&[tKey, tValue] : tArgumentMap) {
|
||||||
upsert(mArgumentMap, tKey, tValue);
|
mArgumentMap.insert_or_assign(tKey, tValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -344,7 +317,7 @@ class ArgumentParser {
|
|||||||
// Accepts a variadic number of ArgumentParser objects
|
// Accepts a variadic number of ArgumentParser objects
|
||||||
template<typename T, typename... Targs>
|
template<typename T, typename... Targs>
|
||||||
void add_parents(T aArgumentParser, Targs... Fargs) {
|
void add_parents(T aArgumentParser, Targs... Fargs) {
|
||||||
mParentParsers.push_back(aArgumentParser);
|
mParentParsers.emplace_back(aArgumentParser);
|
||||||
add_parents(Fargs...);
|
add_parents(Fargs...);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -364,10 +337,10 @@ class ArgumentParser {
|
|||||||
|
|
||||||
// Getter enabled for all template types other than std::vector and std::list
|
// Getter enabled for all template types other than std::vector and std::list
|
||||||
template <typename T = std::string>
|
template <typename T = std::string>
|
||||||
typename std::enable_if<is_specialization<T, std::vector>::value == false &&
|
typename std::enable_if<!is_specialization<T, std::vector>::value &&
|
||||||
is_specialization<T, std::list>::value == false, T>::type
|
!is_specialization<T, std::list>::value, T>::type
|
||||||
get(const char * aArgumentName) {
|
get(const char * aArgumentName) {
|
||||||
std::map<std::string, std::shared_ptr<Argument>>::iterator tIterator = mArgumentMap.find(aArgumentName);
|
auto tIterator = mArgumentMap.find(aArgumentName);
|
||||||
if (tIterator != mArgumentMap.end()) {
|
if (tIterator != mArgumentMap.end()) {
|
||||||
return tIterator->second->get<T>();
|
return tIterator->second->get<T>();
|
||||||
}
|
}
|
||||||
@ -378,7 +351,7 @@ class ArgumentParser {
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
typename std::enable_if<is_specialization<T, std::vector>::value, T>::type
|
typename std::enable_if<is_specialization<T, std::vector>::value, T>::type
|
||||||
get(const char * aArgumentName) {
|
get(const char * aArgumentName) {
|
||||||
std::map<std::string, std::shared_ptr<Argument>>::iterator tIterator = mArgumentMap.find(aArgumentName);
|
auto tIterator = mArgumentMap.find(aArgumentName);
|
||||||
if (tIterator != mArgumentMap.end()) {
|
if (tIterator != mArgumentMap.end()) {
|
||||||
return tIterator->second->get_vector<T>();
|
return tIterator->second->get_vector<T>();
|
||||||
}
|
}
|
||||||
@ -389,7 +362,7 @@ class ArgumentParser {
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
typename std::enable_if<is_specialization<T, std::list>::value, T>::type
|
typename std::enable_if<is_specialization<T, std::list>::value, T>::type
|
||||||
get(const char * aArgumentName) {
|
get(const char * aArgumentName) {
|
||||||
std::map<std::string, std::shared_ptr<Argument>>::iterator tIterator = mArgumentMap.find(aArgumentName);
|
auto tIterator = mArgumentMap.find(aArgumentName);
|
||||||
if (tIterator != mArgumentMap.end()) {
|
if (tIterator != mArgumentMap.end()) {
|
||||||
return tIterator->second->get_list<T>();
|
return tIterator->second->get_list<T>();
|
||||||
}
|
}
|
||||||
@ -399,7 +372,7 @@ class ArgumentParser {
|
|||||||
// Indexing operator. Return a reference to an Argument object
|
// Indexing operator. Return a reference to an Argument object
|
||||||
// Used in conjuction with Argument.operator== e.g., parser["foo"] == true
|
// Used in conjuction with Argument.operator== e.g., parser["foo"] == true
|
||||||
Argument& operator[](const std::string& aArgumentName) {
|
Argument& operator[](const std::string& aArgumentName) {
|
||||||
std::map<std::string, std::shared_ptr<Argument>>::iterator tIterator = mArgumentMap.find(aArgumentName);
|
auto tIterator = mArgumentMap.find(aArgumentName);
|
||||||
if (tIterator != mArgumentMap.end()) {
|
if (tIterator != mArgumentMap.end()) {
|
||||||
return *(tIterator->second);
|
return *(tIterator->second);
|
||||||
}
|
}
|
||||||
@ -422,11 +395,11 @@ class ArgumentParser {
|
|||||||
}
|
}
|
||||||
stream << "\n\n";
|
stream << "\n\n";
|
||||||
|
|
||||||
if (mPositionalArguments.size() > 0)
|
if (!mPositionalArguments.empty())
|
||||||
stream << "Positional arguments:\n";
|
stream << "Positional arguments:\n";
|
||||||
for (size_t i = 0; i < mPositionalArguments.size(); i++) {
|
for (const auto& mPositionalArgument : mPositionalArguments) {
|
||||||
size_t tCurrentLength = 0;
|
size_t tCurrentLength = 0;
|
||||||
auto tNames = mPositionalArguments[i]->mNames;
|
auto tNames = mPositionalArgument->mNames;
|
||||||
for (size_t j = 0; j < tNames.size() - 1; j++) {
|
for (size_t j = 0; j < tNames.size() - 1; j++) {
|
||||||
auto tCurrentName = tNames[j];
|
auto tCurrentName = tNames[j];
|
||||||
stream << tCurrentName;
|
stream << tCurrentName;
|
||||||
@ -442,16 +415,16 @@ class ArgumentParser {
|
|||||||
else
|
else
|
||||||
stream << std::string((tCurrentLength - tLongestArgumentLength) + 2, ' ');
|
stream << std::string((tCurrentLength - tLongestArgumentLength) + 2, ' ');
|
||||||
|
|
||||||
stream << mPositionalArguments[i]->mHelp << "\n";
|
stream << mPositionalArgument->mHelp << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mOptionalArguments.size() > 0 && mPositionalArguments.size() > 0)
|
if (!mOptionalArguments.empty() && !mPositionalArguments.empty())
|
||||||
stream << "\nOptional arguments:\n";
|
stream << "\nOptional arguments:\n";
|
||||||
else if (mOptionalArguments.size() > 0)
|
else if (!mOptionalArguments.empty())
|
||||||
stream << "Optional arguments:\n";
|
stream << "Optional arguments:\n";
|
||||||
for (size_t i = 0; i < mOptionalArguments.size(); i++) {
|
for (const auto & mOptionalArgument : mOptionalArguments) {
|
||||||
size_t tCurrentLength = 0;
|
size_t tCurrentLength = 0;
|
||||||
auto tNames = mOptionalArguments[i]->mNames;
|
auto tNames = mOptionalArgument->mNames;
|
||||||
std::sort(tNames.begin(), tNames.end(),
|
std::sort(tNames.begin(), tNames.end(),
|
||||||
[](const std::string& lhs, const std::string& rhs) {
|
[](const std::string& lhs, const std::string& rhs) {
|
||||||
return lhs.size() == rhs.size() ? lhs < rhs : lhs.size() < rhs.size();
|
return lhs.size() == rhs.size() ? lhs < rhs : lhs.size() < rhs.size();
|
||||||
@ -471,7 +444,7 @@ class ArgumentParser {
|
|||||||
else
|
else
|
||||||
stream << std::string((tCurrentLength - tLongestArgumentLength) + 2, ' ');
|
stream << std::string((tCurrentLength - tLongestArgumentLength) + 2, ' ');
|
||||||
|
|
||||||
stream << mOptionalArguments[i]->mHelp << "\n";
|
stream << mOptionalArgument->mHelp << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << stream.str();
|
std::cout << stream.str();
|
||||||
@ -479,38 +452,22 @@ class ArgumentParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Argument& add_argument_internal(std::shared_ptr<Argument> aArgument) {
|
|
||||||
return *aArgument;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T, typename... Targs>
|
|
||||||
Argument& add_argument_internal(std::shared_ptr<Argument> aArgument, T aArgumentName, Targs... Fargs) {
|
|
||||||
aArgument->mNames.push_back(aArgumentName);
|
|
||||||
add_argument_internal(aArgument, Fargs...);
|
|
||||||
return *aArgument;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If an argument starts with "-" or "--", then it's optional
|
|
||||||
bool is_optional(const std::string& aName) {
|
|
||||||
return (starts_with(aName, "--") || starts_with(aName, "-"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the argument was defined by the user and can be found in mArgumentMap, then it's valid
|
// If the argument was defined by the user and can be found in mArgumentMap, then it's valid
|
||||||
bool is_valid_argument(const std::string& aName) {
|
bool is_valid_argument(const std::string& aName) {
|
||||||
std::map<std::string, std::shared_ptr<Argument>>::iterator tIterator = mArgumentMap.find(aName);
|
auto tIterator = mArgumentMap.find(aName);
|
||||||
return (tIterator != mArgumentMap.end());
|
return (tIterator != mArgumentMap.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
void parse_args_internal(const std::vector<std::string>& aArguments) {
|
void parse_args_internal(const std::vector<std::string>& aArguments) {
|
||||||
std::vector<char*> argv;
|
std::vector<char*> argv;
|
||||||
for (const auto& arg : aArguments)
|
for (const auto& arg : aArguments)
|
||||||
argv.push_back(const_cast<char*>(arg.data()));
|
argv.emplace_back(const_cast<char*>(arg.data()));
|
||||||
argv.push_back(nullptr);
|
argv.emplace_back(nullptr);
|
||||||
return parse_args_internal(int(argv.size()) - 1, argv.data());
|
return parse_args_internal(int(argv.size()) - 1, argv.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
void parse_args_internal(int argc, char * argv[]) {
|
void parse_args_internal(int argc, char * argv[]) {
|
||||||
if (mProgramName == "" && argc > 0)
|
if (mProgramName.empty() && argc > 0)
|
||||||
mProgramName = argv[0];
|
mProgramName = argv[0];
|
||||||
for (int i = 1; i < argc; i++) {
|
for (int i = 1; i < argc; i++) {
|
||||||
auto tCurrentArgument = std::string(argv[i]);
|
auto tCurrentArgument = std::string(argv[i]);
|
||||||
@ -518,12 +475,11 @@ class ArgumentParser {
|
|||||||
print_help();
|
print_help();
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
std::map<std::string, std::shared_ptr<Argument>>::iterator tIterator =
|
auto tIterator = mArgumentMap.find(argv[i]);
|
||||||
mArgumentMap.find(argv[i]);
|
|
||||||
if (tIterator != mArgumentMap.end()) {
|
if (tIterator != mArgumentMap.end()) {
|
||||||
// Start parsing optional argument
|
// Start parsing optional argument
|
||||||
auto tArgument = tIterator->second;
|
auto tArgument = tIterator->second;
|
||||||
tArgument->mUsedName = tCurrentArgument;
|
tArgument->mUsedName = tCurrentArgument;
|
||||||
tArgument->mIsUsed = true;
|
tArgument->mIsUsed = true;
|
||||||
auto tCount = tArgument->mNumArgs;
|
auto tCount = tArgument->mNumArgs;
|
||||||
|
|
||||||
@ -533,29 +489,29 @@ class ArgumentParser {
|
|||||||
// (2) User has provided an implicit value, which also sets nargs to 0
|
// (2) User has provided an implicit value, which also sets nargs to 0
|
||||||
if (tCount == 0) {
|
if (tCount == 0) {
|
||||||
// Use implicit value for this optional argument
|
// Use implicit value for this optional argument
|
||||||
tArgument->mValues.push_back(tArgument->mImplicitValue);
|
tArgument->mValues.emplace_back(tArgument->mImplicitValue);
|
||||||
tArgument->mRawValues.push_back("");
|
tArgument->mRawValues.emplace_back();
|
||||||
tCount = 0;
|
tCount = 0;
|
||||||
}
|
}
|
||||||
while (tCount > 0) {
|
while (tCount > 0) {
|
||||||
i = i + 1;
|
i = i + 1;
|
||||||
if (i < argc) {
|
if (i < argc) {
|
||||||
tArgument->mUsedName = tCurrentArgument;
|
tArgument->mUsedName = tCurrentArgument;
|
||||||
tArgument->mRawValues.push_back(argv[i]);
|
tArgument->mRawValues.emplace_back(argv[i]);
|
||||||
if (tArgument->mAction != nullptr)
|
if (tArgument->mAction != nullptr)
|
||||||
tArgument->mValues.push_back(tArgument->mAction(argv[i]));
|
tArgument->mValues.emplace_back(tArgument->mAction(argv[i]));
|
||||||
else {
|
else {
|
||||||
if (tArgument->mDefaultValue.has_value())
|
if (tArgument->mDefaultValue.has_value())
|
||||||
tArgument->mValues.push_back(tArgument->mDefaultValue);
|
tArgument->mValues.emplace_back(tArgument->mDefaultValue);
|
||||||
else
|
else
|
||||||
tArgument->mValues.push_back(std::string(argv[i]));
|
tArgument->mValues.emplace_back(std::string(argv[i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tCount -= 1;
|
tCount -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (is_optional(argv[i])) {
|
if (Argument::is_optional(argv[i])) {
|
||||||
// This is possibly a compound optional argument
|
// This is possibly a compound optional argument
|
||||||
// Example: We have three optional arguments -a, -u and -x
|
// Example: We have three optional arguments -a, -u and -x
|
||||||
// The user provides ./main -aux ...
|
// The user provides ./main -aux ...
|
||||||
@ -569,24 +525,24 @@ class ArgumentParser {
|
|||||||
if (tIterator != mArgumentMap.end()) {
|
if (tIterator != mArgumentMap.end()) {
|
||||||
auto tArgumentObject = tIterator->second;
|
auto tArgumentObject = tIterator->second;
|
||||||
tNumArgs = tArgumentObject->mNumArgs;
|
tNumArgs = tArgumentObject->mNumArgs;
|
||||||
std::vector<std::string> tArgumentsForRecursiveParsing = { "", "-" + tArgument };
|
std::vector<std::string> tArgumentsForRecursiveParsing = {"", "-" + tArgument};
|
||||||
while (tNumArgs > 0 && i < argc) {
|
while (tNumArgs > 0 && i < argc) {
|
||||||
i += 1;
|
i += 1;
|
||||||
if (i < argc) {
|
if (i < argc) {
|
||||||
tArgumentsForRecursiveParsing.push_back(argv[i]);
|
tArgumentsForRecursiveParsing.emplace_back(argv[i]);
|
||||||
tNumArgs -= 1;
|
tNumArgs -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
parse_args_internal(tArgumentsForRecursiveParsing);
|
parse_args_internal(tArgumentsForRecursiveParsing);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!tArgument.empty() && tArgument[0] == '-')
|
||||||
|
std::cout << "warning: unrecognized optional argument " << tArgument
|
||||||
|
<< std::endl;
|
||||||
|
else
|
||||||
|
std::cout << "warning: unrecognized optional argument -" << tArgument
|
||||||
|
<< std::endl;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
if (tArgument.size() > 0 && tArgument[0] == '-')
|
|
||||||
std::cout << "warning: unrecognized optional argument " << tArgument
|
|
||||||
<< std::endl;
|
|
||||||
else
|
|
||||||
std::cout << "warning: unrecognized optional argument -" << tArgument
|
|
||||||
<< std::endl;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -605,20 +561,20 @@ class ArgumentParser {
|
|||||||
auto tCount = tArgument->mNumArgs - tArgument->mRawValues.size();
|
auto tCount = tArgument->mNumArgs - tArgument->mRawValues.size();
|
||||||
while (tCount > 0) {
|
while (tCount > 0) {
|
||||||
tIterator = mArgumentMap.find(argv[i]);
|
tIterator = mArgumentMap.find(argv[i]);
|
||||||
if (tIterator != mArgumentMap.end() || is_optional(argv[i])) {
|
if (tIterator != mArgumentMap.end() || Argument::is_optional(argv[i])) {
|
||||||
i = i - 1;
|
i = i - 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (i < argc) {
|
if (i < argc) {
|
||||||
tArgument->mUsedName = tCurrentArgument;
|
tArgument->mUsedName = tCurrentArgument;
|
||||||
tArgument->mRawValues.push_back(argv[i]);
|
tArgument->mRawValues.emplace_back(argv[i]);
|
||||||
if (tArgument->mAction != nullptr)
|
if (tArgument->mAction != nullptr)
|
||||||
tArgument->mValues.push_back(tArgument->mAction(argv[i]));
|
tArgument->mValues.emplace_back(tArgument->mAction(argv[i]));
|
||||||
else {
|
else {
|
||||||
if (tArgument->mDefaultValue.has_value())
|
if (tArgument->mDefaultValue.has_value())
|
||||||
tArgument->mValues.push_back(tArgument->mDefaultValue);
|
tArgument->mValues.emplace_back(tArgument->mDefaultValue);
|
||||||
else
|
else
|
||||||
tArgument->mValues.push_back(std::string(argv[i]));
|
tArgument->mValues.emplace_back(std::string(argv[i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tCount -= 1;
|
tCount -= 1;
|
||||||
@ -633,8 +589,7 @@ class ArgumentParser {
|
|||||||
|
|
||||||
void parse_args_validate() {
|
void parse_args_validate() {
|
||||||
// Check if all positional arguments are parsed
|
// Check if all positional arguments are parsed
|
||||||
for (size_t i = 0; i < mPositionalArguments.size(); i++) {
|
for (const auto& tArgument : mPositionalArguments) {
|
||||||
auto tArgument = mPositionalArguments[i];
|
|
||||||
if (tArgument->mValues.size() != tArgument->mNumArgs) {
|
if (tArgument->mValues.size() != tArgument->mNumArgs) {
|
||||||
std::cout << "error: " << tArgument->mUsedName << ": expected "
|
std::cout << "error: " << tArgument->mUsedName << ": expected "
|
||||||
<< tArgument->mNumArgs << (tArgument->mNumArgs == 1 ? " argument. " : " arguments. ")
|
<< tArgument->mNumArgs << (tArgument->mNumArgs == 1 ? " argument. " : " arguments. ")
|
||||||
@ -645,8 +600,7 @@ class ArgumentParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if all user-provided optional argument values are parsed correctly
|
// Check if all user-provided optional argument values are parsed correctly
|
||||||
for (size_t i = 0; i < mOptionalArguments.size(); i++) {
|
for (const auto& tArgument : mOptionalArguments) {
|
||||||
auto tArgument = mOptionalArguments[i];
|
|
||||||
if (tArgument->mIsUsed && tArgument->mNumArgs > 0) {
|
if (tArgument->mIsUsed && tArgument->mNumArgs > 0) {
|
||||||
if (tArgument->mValues.size() != tArgument->mNumArgs) {
|
if (tArgument->mValues.size() != tArgument->mNumArgs) {
|
||||||
// All cool if there's a default value to return
|
// All cool if there's a default value to return
|
||||||
@ -669,9 +623,9 @@ class ArgumentParser {
|
|||||||
// Used by print_help.
|
// Used by print_help.
|
||||||
size_t get_length_of_longest_argument() {
|
size_t get_length_of_longest_argument() {
|
||||||
size_t tResult = 0;
|
size_t tResult = 0;
|
||||||
for (size_t i = 0; i < mPositionalArguments.size(); i++) {
|
for (const auto& mPositionalArgument : mPositionalArguments) {
|
||||||
size_t tCurrentArgumentLength = 0;
|
size_t tCurrentArgumentLength = 0;
|
||||||
auto tNames = mPositionalArguments[i]->mNames;
|
auto tNames = mPositionalArgument->mNames;
|
||||||
for (size_t j = 0; j < tNames.size() - 1; j++) {
|
for (size_t j = 0; j < tNames.size() - 1; j++) {
|
||||||
auto tNameLength = tNames[j].length();
|
auto tNameLength = tNames[j].length();
|
||||||
tCurrentArgumentLength += tNameLength + 2; // +2 for ", "
|
tCurrentArgumentLength += tNameLength + 2; // +2 for ", "
|
||||||
@ -681,9 +635,9 @@ class ArgumentParser {
|
|||||||
tResult = tCurrentArgumentLength;
|
tResult = tCurrentArgumentLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < mOptionalArguments.size(); i++) {
|
for (const auto& mOptionalArgument : mOptionalArguments) {
|
||||||
size_t tCurrentArgumentLength = 0;
|
size_t tCurrentArgumentLength = 0;
|
||||||
auto tNames = mOptionalArguments[i]->mNames;
|
auto tNames = mOptionalArgument->mNames;
|
||||||
for (size_t j = 0; j < tNames.size() - 1; j++) {
|
for (size_t j = 0; j < tNames.size() - 1; j++) {
|
||||||
auto tNameLength = tNames[j].length();
|
auto tNameLength = tNames[j].length();
|
||||||
tCurrentArgumentLength += tNameLength + 2; // +2 for ", "
|
tCurrentArgumentLength += tNameLength + 2; // +2 for ", "
|
||||||
@ -699,7 +653,7 @@ class ArgumentParser {
|
|||||||
std::vector<ArgumentParser> mParentParsers;
|
std::vector<ArgumentParser> mParentParsers;
|
||||||
std::vector<std::shared_ptr<Argument>> mPositionalArguments;
|
std::vector<std::shared_ptr<Argument>> mPositionalArguments;
|
||||||
std::vector<std::shared_ptr<Argument>> mOptionalArguments;
|
std::vector<std::shared_ptr<Argument>> mOptionalArguments;
|
||||||
size_t mNextPositionalArgument;
|
size_t mNextPositionalArgument = 0;
|
||||||
std::map<std::string, std::shared_ptr<Argument>> mArgumentMap;
|
std::map<std::string, std::shared_ptr<Argument>> mArgumentMap;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user