#pragma once #include #include #include #include #include #include #include #include #include namespace argparse { template class Ref> struct is_specialization : std::false_type {}; template class Ref, typename... Args> struct is_specialization, Ref> : std::true_type {}; template bool upsert(std::map& aMap, KeyType const& aKey, ElementType const& aNewValue) { typedef typename std::map::iterator Iterator; typedef typename std::pair Result; Result tResult = aMap.insert(typename std::map::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 } bool starts_with(const std::string& haystack, const std::string& needle) { return needle.length() <= haystack.length() && std::equal(needle.begin(), needle.end(), haystack.begin()); }; template T get_from_list(const std::list& aList, size_t aIndex) { if (aList.size() > aIndex) { auto tIterator = aList.begin(); std::advance(tIterator, aIndex); return *tIterator; } return T(); } struct Argument { std::vector mNames; std::string mHelp; std::any mDefaultValue; std::any mImplicitValue; std::function mAction; std::vector mValues; std::vector mRawValues; size_t mNumArgs; bool mIsOptional; Argument() : mNames({}), mHelp(""), mAction([](const std::string& aValue) { return aValue; }), mValues({}), mRawValues({}), mNumArgs(1), mIsOptional(false) {} Argument& help(const std::string& aHelp) { mHelp = aHelp; return *this; } Argument& default_value(std::any aDefaultValue) { mDefaultValue = aDefaultValue; return *this; } Argument& implicit_value(std::any aImplicitValue) { mImplicitValue = aImplicitValue; mNumArgs = 0; return *this; } Argument& action(std::function aAction) { mAction = aAction; return *this; } Argument& nargs(size_t aNumArgs) { mNumArgs = aNumArgs; return *this; } template T get() { if (mValues.size() == 0) { if (mDefaultValue.has_value()) { return std::any_cast(mDefaultValue); } else return T(); } else { if (mRawValues.size() > 0) return std::any_cast(mValues[0]); else { if (mDefaultValue.has_value()) return std::any_cast(mDefaultValue); else return T(); } } } template T get_vector() { T tResult; if (mValues.size() == 0) { if (mDefaultValue.has_value()) { std::any tDefaultValueLambdaResult = mDefaultValue; T tDefaultValues = std::any_cast(tDefaultValueLambdaResult); for (size_t i = 0; i < tDefaultValues.size(); i++) { tResult.push_back(std::any_cast(tDefaultValues[i])); } return tResult; } else return T(); } else { if (mRawValues.size() > 0) { for (size_t i = 0; i < mValues.size(); i++) { tResult.push_back(std::any_cast(mValues[i])); } return tResult; } else { if (mDefaultValue.has_value()) { std::any tDefaultValueLambdaResult = mDefaultValue; std::vector tDefaultValues = std::any_cast>(tDefaultValueLambdaResult); for (size_t i = 0; i < tDefaultValues.size(); i++) { tResult.push_back(std::any_cast(tDefaultValues[i])); } return tResult; } else return T(); } } } template T get_list() { T tResult; if (mValues.size() == 0) { if (mDefaultValue.has_value()) { T tDefaultValues = std::any_cast(mDefaultValue); for (size_t i = 0; i < tDefaultValues.size(); i++) { tResult.push_back(std::any_cast(get_from_list(tDefaultValues, i))); } return tResult; } else return T(); } else { if (mRawValues.size() > 0) { for (size_t i = 0; i < mValues.size(); i++) { tResult.push_back(std::any_cast(mValues[i])); } return tResult; } else { if (mDefaultValue.has_value()) { std::list tDefaultValues = std::any_cast>(mDefaultValue); for (size_t i = 0; i < tDefaultValues.size(); i++) { tResult.push_back(std::any_cast(get_from_list(tDefaultValues, i))); } return tResult; } else return T(); } } } }; class ArgumentParser { public: ArgumentParser(const std::string& aProgramName = "") : mProgramName(aProgramName), mNextPositionalArgument(0) {} template Argument& add_argument(T value, Targs... Fargs) { std::shared_ptr tArgument = std::make_shared(); 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) mPositionalArguments.push_back(tArgument); for (auto& mName : tArgument->mNames) { upsert(mArgumentMap, mName, tArgument); } return *tArgument; } void parse_args(const std::vector& aArguments) { std::vector argv; for (const auto& arg : aArguments) argv.push_back((char*)arg.data()); argv.push_back(nullptr); return parse_args(argv.size() - 1, argv.data()); } void parse_args(int argc, char * argv[]) { if (mProgramName == "" && argc > 0) mProgramName = argv[0]; for (int i = 1; i < argc; i++) { auto tCurrentArgument = argv[i]; std::map>::iterator tIterator = mArgumentMap.find(argv[i]); if (tIterator != mArgumentMap.end()) { // Start parsing optional argument auto tArgument = tIterator->second; auto tCount = tArgument->mNumArgs; // Check to see if implicit value should be used // Two cases to handle here: // (1) User has explicitly programmed nargs to be 0 // (2) User has provided an implicit value, which also sets nargs to 0 if (tCount == 0) { // Use implicit value for this optional argument tArgument->mValues.push_back(tArgument->mImplicitValue); tArgument->mRawValues.push_back(""); tCount = 0; } while (tCount > 0) { i = i + 1; if (i < argc) { tArgument->mRawValues.push_back(argv[i]); if (tArgument->mAction != nullptr) tArgument->mValues.push_back(tArgument->mAction(argv[i])); else { if (tArgument->mDefaultValue.has_value()) tArgument->mValues.push_back(tArgument->mDefaultValue); else tArgument->mValues.push_back(std::string(argv[i])); } } tCount -= 1; } } else { if (is_optional(argv[i])) { // This is possibly a compound optional argument // Example: We have three optional arguments -a, -u and -x // The user provides ./main -aux ... // Here -aux is a compound optional argument std::string tCompoundArgument = std::string(argv[i]); for (size_t j = 1; j < tCompoundArgument.size(); j++) { std::string tArgument(1, tCompoundArgument[j]); size_t tNumArgs = 0; std::map>::iterator tIterator = mArgumentMap.find("-" + tArgument); if (tIterator != mArgumentMap.end()) { auto tArgumentObject = tIterator->second; tNumArgs = tArgumentObject->mNumArgs; } std::vector tArgumentsForRecursiveParsing = { "", "-" + tArgument }; while (tNumArgs > 0) { i += 1; tArgumentsForRecursiveParsing.push_back(argv[i]); tNumArgs -= 1; } parse_args(tArgumentsForRecursiveParsing); } } else { // This is a positional argument. // Parse and save into mPositionalArguments vector auto tArgument = mPositionalArguments[mNextPositionalArgument]; auto tCount = tArgument->mNumArgs - tArgument->mRawValues.size(); while (tCount > 0) { std::map>::iterator tIterator = mArgumentMap.find(argv[i]); if (tIterator != mArgumentMap.end() || is_optional(argv[i])) { i = i - 1; break; } if (i < argc) { tArgument->mRawValues.push_back(argv[i]); if (tArgument->mAction != nullptr) tArgument->mValues.push_back(tArgument->mAction(argv[i])); else { if (tArgument->mDefaultValue.has_value()) tArgument->mValues.push_back(tArgument->mDefaultValue); else tArgument->mValues.push_back(std::string(argv[i])); } } tCount -= 1; if (tCount > 0) i += 1; } if (tCount == 0) mNextPositionalArgument += 1; } } } } template typename std::enable_if::value == false && is_specialization::value == false, T>::type get(const char * aArgumentName) { std::map>::iterator tIterator = mArgumentMap.find(aArgumentName); if (tIterator != mArgumentMap.end()) { return tIterator->second->get(); } return T(); } template typename std::enable_if::value, T>::type get(const char * aArgumentName) { std::map>::iterator tIterator = mArgumentMap.find(aArgumentName); if (tIterator != mArgumentMap.end()) { return tIterator->second->get_vector(); } return T(); } template typename std::enable_if::value, T>::type get(const char * aArgumentName) { std::map>::iterator tIterator = mArgumentMap.find(aArgumentName); if (tIterator != mArgumentMap.end()) { return tIterator->second->get_list(); } return T(); } std::map> get_arguments() const { return mArgumentMap; } std::shared_ptr operator[](const std::string& aArgumentName) { std::map>::iterator tIterator = mArgumentMap.find(aArgumentName); if (tIterator != mArgumentMap.end()) { return tIterator->second; } else { return nullptr; } } private: Argument& add_argument_internal(std::shared_ptr aArgument) { return *aArgument; } template Argument& add_argument_internal(std::shared_ptr aArgument, T aArgumentName, Targs... Fargs) { aArgument->mNames.push_back(aArgumentName); add_argument_internal(aArgument, Fargs...); return *aArgument; } bool is_optional(const std::string& aName) { return (starts_with(aName, "--") || starts_with(aName, "-")); } bool is_valid_argument(const std::string& aName) { std::map>::iterator tIterator = mArgumentMap.find(aName); return (tIterator != mArgumentMap.end()); } std::string mProgramName; std::vector> mPositionalArguments; size_t mNextPositionalArgument; std::map> mArgumentMap; }; }