mirror of
https://github.com/KeqingMoe/argparse.git
synced 2025-07-05 07:34:40 +00:00
Merge pull request #14 from svanveen/fix/refactoring
Do some refactoring to improve readability
This commit is contained in:
commit
cddde9f1b7
@ -40,9 +40,11 @@ SOFTWARE.
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <numeric>
|
||||||
|
|
||||||
namespace argparse {
|
namespace argparse {
|
||||||
|
|
||||||
|
namespace { // anonymous namespace for helper methods - not visible outside this header file
|
||||||
// Some utility structs to check template specialization
|
// Some utility structs to check template specialization
|
||||||
template<typename Test, template<typename...> class Ref>
|
template<typename Test, template<typename...> class Ref>
|
||||||
struct is_specialization : std::false_type {};
|
struct is_specialization : std::false_type {};
|
||||||
@ -66,6 +68,7 @@ T get_from_list(const std::list<T>& aList, size_t aIndex) {
|
|||||||
}
|
}
|
||||||
return T();
|
return T();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class Argument {
|
class Argument {
|
||||||
friend class ArgumentParser;
|
friend class ArgumentParser;
|
||||||
@ -104,6 +107,32 @@ public:
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @throws std::runtime_error if argument values are not valid
|
||||||
|
*/
|
||||||
|
void validate() const {
|
||||||
|
if (mIsOptional) {
|
||||||
|
if (mIsUsed && mValues.size() != mNumArgs && !mDefaultValue.has_value()) {
|
||||||
|
std::stringstream stream;
|
||||||
|
stream << "error: " << mUsedName << ": expected " << mNumArgs << " argument(s). "
|
||||||
|
<< mValues.size() << " provided.\n" << std::endl;
|
||||||
|
throw std::runtime_error(stream.str());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// TODO: check if an implicit value was programmed for this argument
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (mValues.size() != mNumArgs) {
|
||||||
|
std::stringstream stream;
|
||||||
|
stream << "error: " << mUsedName << ": expected " << mNumArgs << " argument(s). "
|
||||||
|
<< mValues.size() << " provided.\n" << std::endl;
|
||||||
|
throw std::runtime_error(stream.str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
bool operator!=(const T& aRhs) const {
|
bool operator!=(const T& aRhs) const {
|
||||||
return !(*this == aRhs);
|
return !(*this == aRhs);
|
||||||
@ -121,7 +150,7 @@ public:
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
typename std::enable_if<is_specialization<T, std::vector>::value, bool>::type
|
typename std::enable_if<is_specialization<T, std::vector>::value, bool>::type
|
||||||
operator==(const T& aRhs) const {
|
operator==(const T& aRhs) const {
|
||||||
T tLhs = get_vector<T>();
|
T tLhs = get<T>();
|
||||||
if (tLhs.size() != aRhs.size())
|
if (tLhs.size() != aRhs.size())
|
||||||
return false;
|
return false;
|
||||||
else {
|
else {
|
||||||
@ -138,7 +167,7 @@ public:
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
typename std::enable_if<is_specialization<T, std::list>::value, bool>::type
|
typename std::enable_if<is_specialization<T, std::list>::value, bool>::type
|
||||||
operator==(const T& aRhs) const {
|
operator==(const T& aRhs) const {
|
||||||
T tLhs = get_list<T>();
|
T tLhs = get<T>();
|
||||||
if (tLhs.size() != aRhs.size())
|
if (tLhs.size() != aRhs.size())
|
||||||
return false;
|
return false;
|
||||||
else {
|
else {
|
||||||
@ -159,7 +188,9 @@ public:
|
|||||||
|
|
||||||
// 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 {
|
typename std::enable_if<!is_specialization<T, std::vector>::value &&
|
||||||
|
!is_specialization<T, std::list>::value, T>::type
|
||||||
|
get() const {
|
||||||
if (mValues.empty()) {
|
if (mValues.empty()) {
|
||||||
if (mDefaultValue.has_value()) {
|
if (mDefaultValue.has_value()) {
|
||||||
return std::any_cast<T>(mDefaultValue);
|
return std::any_cast<T>(mDefaultValue);
|
||||||
@ -181,7 +212,8 @@ public:
|
|||||||
|
|
||||||
// Getter for std::vector. Here T = std::vector<...>
|
// Getter for std::vector. Here T = std::vector<...>
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T get_vector() const {
|
typename std::enable_if<is_specialization<T, std::vector>::value, T>::type
|
||||||
|
get() const {
|
||||||
T tResult;
|
T tResult;
|
||||||
if (mValues.empty()) {
|
if (mValues.empty()) {
|
||||||
if (mDefaultValue.has_value()) {
|
if (mDefaultValue.has_value()) {
|
||||||
@ -217,7 +249,8 @@ public:
|
|||||||
|
|
||||||
// Getter for std::list. Here T = std::list<...>
|
// Getter for std::list. Here T = std::list<...>
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T get_list() const {
|
typename std::enable_if<is_specialization<T, std::list>::value, T>::type
|
||||||
|
get() const {
|
||||||
T tResult;
|
T tResult;
|
||||||
if (mValues.empty()) {
|
if (mValues.empty()) {
|
||||||
if (mDefaultValue.has_value()) {
|
if (mDefaultValue.has_value()) {
|
||||||
@ -262,6 +295,10 @@ public:
|
|||||||
size_t mNumArgs = 1;
|
size_t mNumArgs = 1;
|
||||||
bool mIsOptional = false;
|
bool mIsOptional = false;
|
||||||
bool mIsUsed = false; // relevant for optional arguments. True if used by user
|
bool mIsUsed = false; // relevant for optional arguments. True if used by user
|
||||||
|
|
||||||
|
public:
|
||||||
|
static constexpr auto mHelpOption = "-h";
|
||||||
|
static constexpr auto mHelpOptionLong = "--help";
|
||||||
};
|
};
|
||||||
|
|
||||||
class ArgumentParser {
|
class ArgumentParser {
|
||||||
@ -269,14 +306,11 @@ class ArgumentParser {
|
|||||||
explicit ArgumentParser(std::string aProgramName = {}) :
|
explicit ArgumentParser(std::string aProgramName = {}) :
|
||||||
mProgramName(std::move(aProgramName))
|
mProgramName(std::move(aProgramName))
|
||||||
{
|
{
|
||||||
std::shared_ptr<Argument> tArgument = std::make_shared<Argument>("-h", "--help");
|
add_argument(Argument::mHelpOption, Argument::mHelpOptionLong)
|
||||||
tArgument->mHelp = "show this help message and exit";
|
.help("show this help message and exit")
|
||||||
tArgument->mNumArgs = 0;
|
.nargs(0)
|
||||||
tArgument->mDefaultValue = false;
|
.default_value(false)
|
||||||
tArgument->mImplicitValue = true;
|
.implicit_value(true);
|
||||||
mOptionalArguments.emplace_back(tArgument);
|
|
||||||
mArgumentMap.insert_or_assign(std::string("-h"), tArgument);
|
|
||||||
mArgumentMap.insert_or_assign(std::string("--help"), tArgument);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parameter packing
|
// Parameter packing
|
||||||
@ -285,41 +319,35 @@ class ArgumentParser {
|
|||||||
Argument& add_argument(Targs... Fargs) {
|
Argument& add_argument(Targs... Fargs) {
|
||||||
std::shared_ptr<Argument> tArgument = std::make_shared<Argument>(std::move(Fargs)...);
|
std::shared_ptr<Argument> tArgument = std::make_shared<Argument>(std::move(Fargs)...);
|
||||||
|
|
||||||
if (!tArgument->mIsOptional)
|
if (tArgument->mIsOptional)
|
||||||
mPositionalArguments.emplace_back(tArgument);
|
|
||||||
else
|
|
||||||
mOptionalArguments.emplace_back(tArgument);
|
mOptionalArguments.emplace_back(tArgument);
|
||||||
|
else
|
||||||
|
mPositionalArguments.emplace_back(tArgument);
|
||||||
|
|
||||||
for (auto& mName : tArgument->mNames) {
|
for (const auto& mName : tArgument->mNames) {
|
||||||
mArgumentMap.insert_or_assign(mName, tArgument);
|
mArgumentMap.insert_or_assign(mName, tArgument);
|
||||||
}
|
}
|
||||||
return *tArgument;
|
return *tArgument;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Base case for add_parents parameter packing
|
// Parameter packed add_parents method
|
||||||
void add_parents() {
|
// Accepts a variadic number of ArgumentParser objects
|
||||||
for (const auto& tParentParser : mParentParsers) {
|
template<typename... Targs>
|
||||||
auto tPositionalArguments = tParentParser.mPositionalArguments;
|
void add_parents(Targs... Fargs) {
|
||||||
for (auto& tArgument : tPositionalArguments) {
|
const auto tNewParentParsers = {Fargs...};
|
||||||
mPositionalArguments.emplace_back(tArgument);
|
for (const auto& tParentParser : tNewParentParsers) {
|
||||||
}
|
const auto& tPositionalArguments = tParentParser.mPositionalArguments;
|
||||||
auto tOptionalArguments = tParentParser.mOptionalArguments;
|
std::copy(std::begin(tPositionalArguments), std::end(tPositionalArguments), std::back_inserter(mPositionalArguments));
|
||||||
for (auto& tArgument : tOptionalArguments) {
|
|
||||||
mOptionalArguments.emplace_back(tArgument);
|
const auto& tOptionalArguments = tParentParser.mOptionalArguments;
|
||||||
}
|
std::copy(std::begin(tOptionalArguments), std::end(tOptionalArguments), std::back_inserter(mOptionalArguments));
|
||||||
auto tArgumentMap = tParentParser.mArgumentMap;
|
|
||||||
for (auto&[tKey, tValue] : tArgumentMap) {
|
const auto& tArgumentMap = tParentParser.mArgumentMap;
|
||||||
|
for (const auto&[tKey, tValue] : tArgumentMap) {
|
||||||
mArgumentMap.insert_or_assign(tKey, tValue);
|
mArgumentMap.insert_or_assign(tKey, tValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
std::move(std::begin(tNewParentParsers), std::end(tNewParentParsers), std::back_inserter(mParentParsers));
|
||||||
|
|
||||||
// Parameter packed add_parents method
|
|
||||||
// Accepts a variadic number of ArgumentParser objects
|
|
||||||
template<typename T, typename... Targs>
|
|
||||||
void add_parents(T aArgumentParser, Targs... Fargs) {
|
|
||||||
mParentParsers.emplace_back(aArgumentParser);
|
|
||||||
add_parents(Fargs...);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Call parse_args_internal - which does all the work
|
/* Call parse_args_internal - which does all the work
|
||||||
@ -342,9 +370,7 @@ 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 &&
|
T get(const std::string& aArgumentName) {
|
||||||
!is_specialization<T, std::list>::value, T>::type
|
|
||||||
get(const char * aArgumentName) {
|
|
||||||
auto 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>();
|
||||||
@ -352,28 +378,6 @@ class ArgumentParser {
|
|||||||
return T();
|
return T();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getter enabled for std::vector
|
|
||||||
template <typename T>
|
|
||||||
typename std::enable_if<is_specialization<T, std::vector>::value, T>::type
|
|
||||||
get(const char * aArgumentName) {
|
|
||||||
auto tIterator = mArgumentMap.find(aArgumentName);
|
|
||||||
if (tIterator != mArgumentMap.end()) {
|
|
||||||
return tIterator->second->get_vector<T>();
|
|
||||||
}
|
|
||||||
return T();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Getter enabled for std::list
|
|
||||||
template <typename T>
|
|
||||||
typename std::enable_if<is_specialization<T, std::list>::value, T>::type
|
|
||||||
get(const char * aArgumentName) {
|
|
||||||
auto tIterator = mArgumentMap.find(aArgumentName);
|
|
||||||
if (tIterator != mArgumentMap.end()) {
|
|
||||||
return tIterator->second->get_list<T>();
|
|
||||||
}
|
|
||||||
return T();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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) {
|
||||||
@ -482,7 +486,7 @@ class ArgumentParser {
|
|||||||
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]);
|
||||||
if (tCurrentArgument == "-h" || tCurrentArgument == "--help") {
|
if (tCurrentArgument == Argument::mHelpOption || tCurrentArgument == Argument::mHelpOptionLong) {
|
||||||
throw std::runtime_error("help called");
|
throw std::runtime_error("help called");
|
||||||
}
|
}
|
||||||
auto tIterator = mArgumentMap.find(argv[i]);
|
auto tIterator = mArgumentMap.find(argv[i]);
|
||||||
@ -601,65 +605,41 @@ class ArgumentParser {
|
|||||||
* @throws std::runtime_error in case of any invalid argument
|
* @throws std::runtime_error in case of any invalid argument
|
||||||
*/
|
*/
|
||||||
void parse_args_validate() {
|
void parse_args_validate() {
|
||||||
// Check if all positional arguments are parsed
|
try {
|
||||||
for (const auto& tArgument : mPositionalArguments) {
|
// Check if all positional arguments are parsed
|
||||||
if (tArgument->mValues.size() != tArgument->mNumArgs) {
|
std::for_each(std::begin(mPositionalArguments),
|
||||||
std::stringstream stream;
|
std::end(mPositionalArguments),
|
||||||
stream << "error: " << tArgument->mUsedName << ": expected "
|
std::mem_fn(&Argument::validate));
|
||||||
<< tArgument->mNumArgs << (tArgument->mNumArgs == 1 ? " argument. " : " arguments. ")
|
// Check if all user-provided optional argument values are parsed correctly
|
||||||
<< tArgument->mValues.size() << " provided.\n" << std::endl;
|
std::for_each(std::begin(mOptionalArguments),
|
||||||
throw std::runtime_error(stream.str());
|
std::end(mOptionalArguments),
|
||||||
}
|
std::mem_fn(&Argument::validate));
|
||||||
}
|
} catch (const std::runtime_error& err) {
|
||||||
|
throw err;
|
||||||
// Check if all user-provided optional argument values are parsed correctly
|
|
||||||
for (const auto& tArgument : mOptionalArguments) {
|
|
||||||
if (tArgument->mIsUsed && tArgument->mNumArgs > 0) {
|
|
||||||
if (tArgument->mValues.size() != tArgument->mNumArgs) {
|
|
||||||
// All cool if there's a default value to return
|
|
||||||
// If no default value, then there's a problem
|
|
||||||
if (!tArgument->mDefaultValue.has_value()) {
|
|
||||||
std::stringstream stream;
|
|
||||||
stream << "error: " << tArgument->mUsedName << ": expected "
|
|
||||||
<< tArgument->mNumArgs << (tArgument->mNumArgs == 1 ? " argument. " : " arguments. ")
|
|
||||||
<< tArgument->mValues.size() << " provided.\n" << std::endl;
|
|
||||||
throw std::runtime_error(stream.str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// TODO: check if an implicit value was programmed for this argument
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used by print_help.
|
// Used by print_help.
|
||||||
size_t get_length_of_longest_argument() {
|
size_t get_length_of_longest_argument(const std::vector<std::shared_ptr<Argument>>& aArguments) {
|
||||||
size_t tResult = 0;
|
if (aArguments.empty())
|
||||||
for (const auto& mPositionalArgument : mPositionalArguments) {
|
return 0;
|
||||||
size_t tCurrentArgumentLength = 0;
|
std::vector<size_t> argumentLengths(aArguments.size());
|
||||||
auto tNames = mPositionalArgument->mNames;
|
std::transform(std::begin(aArguments), std::end(aArguments), std::begin(argumentLengths), [](const auto& arg) {
|
||||||
for (size_t j = 0; j < tNames.size() - 1; j++) {
|
const auto& names = arg->mNames;
|
||||||
auto tNameLength = tNames[j].length();
|
auto maxLength = std::accumulate(std::begin(names), std::end(names), 0, [](const auto& sum, const auto& s) {
|
||||||
tCurrentArgumentLength += tNameLength + 2; // +2 for ", "
|
return sum + s.size() + 2; // +2 for ", "
|
||||||
}
|
});
|
||||||
tCurrentArgumentLength += tNames[tNames.size() - 1].length();
|
return maxLength - 2; // -2 since the last one doesn't need ", "
|
||||||
if (tCurrentArgumentLength > tResult)
|
});
|
||||||
tResult = tCurrentArgumentLength;
|
return *std::max_element(std::begin(argumentLengths), std::end(argumentLengths));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& mOptionalArgument : mOptionalArguments) {
|
// Used by print_help.
|
||||||
size_t tCurrentArgumentLength = 0;
|
size_t get_length_of_longest_argument() {
|
||||||
auto tNames = mOptionalArgument->mNames;
|
const auto positionalArgMaxSize = get_length_of_longest_argument(mPositionalArguments);
|
||||||
for (size_t j = 0; j < tNames.size() - 1; j++) {
|
const auto optionalArgMaxSize = get_length_of_longest_argument(mOptionalArguments);
|
||||||
auto tNameLength = tNames[j].length();
|
|
||||||
tCurrentArgumentLength += tNameLength + 2; // +2 for ", "
|
return std::max(positionalArgMaxSize, optionalArgMaxSize);
|
||||||
}
|
|
||||||
tCurrentArgumentLength += tNames[tNames.size() - 1].length();
|
|
||||||
if (tCurrentArgumentLength > tResult)
|
|
||||||
tResult = tCurrentArgumentLength;
|
|
||||||
}
|
|
||||||
return tResult;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string mProgramName;
|
std::string mProgramName;
|
||||||
@ -674,7 +654,7 @@ class ArgumentParser {
|
|||||||
try { \
|
try { \
|
||||||
parser.parse_args(argc, argv); \
|
parser.parse_args(argc, argv); \
|
||||||
} catch (const std::runtime_error& err) { \
|
} catch (const std::runtime_error& err) { \
|
||||||
std::cerr << err.what() << std::endl; \
|
std::cout << err.what() << std::endl; \
|
||||||
parser.print_help(); \
|
parser.print_help(); \
|
||||||
exit(0); \
|
exit(0); \
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user