From a06308f673cff2ec7b045284e00aa8c2acbb1079 Mon Sep 17 00:00:00 2001 From: Pranav Srinivas Kumar Date: Sat, 30 Mar 2019 10:50:51 -0400 Subject: [PATCH] First pass implementation of a naive argument parser. API is shaping up --- src/argparse.hpp | 126 +++++++++++++++++++++++++++++++++++++++++++++-- tests/main.cpp | 26 +++++++++- 2 files changed, 148 insertions(+), 4 deletions(-) diff --git a/src/argparse.hpp b/src/argparse.hpp index bee762c..5e28921 100644 --- a/src/argparse.hpp +++ b/src/argparse.hpp @@ -1,14 +1,134 @@ #include +#include +#include +#include namespace argparse { +struct Argument { + std::vector mNames; + std::string mHelp; + std::function mDefaultValue; + std::function mAction; + std::any mValue; + std::string mRawValue; + + Argument() : + mNames({}), + mHelp(""), + mDefaultValue(nullptr), + mAction(nullptr), + mValue(nullptr), + mRawValue("") {} + + Argument& help(const std::string& aHelp) { + mHelp = aHelp; + return *this; + } + + Argument& default_value(std::function aDefaultValue) { + mDefaultValue = aDefaultValue; + return *this; + } + + Argument& action(std::function aAction) { + mAction = aAction; + return *this; + } + + template + T get() { + if (!mValue.has_value()) { + if (mDefaultValue != nullptr) { + return std::any_cast(mDefaultValue()); + } + else + return T(); + } + else { + if (mRawValue != "") + return std::any_cast(mValue); + else { + if (mDefaultValue != nullptr) + return std::any_cast(mDefaultValue()); + else + return T(); + } + } + } + +}; + class ArgumentParser { public: - ArgumentParser(const std::string& program_name) : - program_name_(program_name) {} + ArgumentParser(const std::string& aProgramName) : + mProgramName(aProgramName) {} + + 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...); + mArguments.push_back(tArgument); + return *tArgument; + } + + void parse_args(int argc, char * argv[]) { + for (int i = 1; i < argc; i++) { + for (auto& tArgument : mArguments) { + auto tIndex = std::find(tArgument->mNames.begin(), tArgument->mNames.end(), argv[i]); + if (tIndex != tArgument->mNames.end()) { + i = i + 1; + if (i < argc) { + tArgument->mRawValue = argv[i]; + if (tArgument->mAction != nullptr) + tArgument->mValue = tArgument->mAction(argv[i]); + else { + if (tArgument->mDefaultValue != nullptr) + tArgument->mValue = tArgument->mDefaultValue(); + else + tArgument->mValue = std::string(argv[i]); + } + } + } + } + } + } + + Argument& operator[](const char * key) { + for (auto& tArgument : mArguments) { + auto tIndex = std::find(tArgument->mNames.begin(), tArgument->mNames.end(), key); + if (tIndex != tArgument->mNames.end()) { + return *tArgument; + } + } + } + + template + T get(const char * aArgumentName) { + for (auto& tArgument : mArguments) { + auto tIndex = std::find(tArgument->mNames.begin(), tArgument->mNames.end(), aArgumentName); + if (tIndex != tArgument->mNames.end()) { + return tArgument->get(); + } + } + return T(); + } private: - std::string program_name_; + 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; + } + + std::string mProgramName; + std::vector> mArguments; }; } \ No newline at end of file diff --git a/tests/main.cpp b/tests/main.cpp index 9b8fb11..72f90a4 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -1,5 +1,29 @@ +#include #include +using namespace argparse; -int main() { +int main(int argc, char * argv[]) { + ArgumentParser program("test"); + + program.add_argument("--config") + .help("Path to input file") + .default_value([]() { return std::string{"config.yml"}; }); + + program.add_argument("-n", "--num_iterations") + .help("Number of iterations") + .action([](const std::string& value) { return std::stoi(value); }); + + program.add_argument("-v", "--verbose", "VERBOSE") + .default_value([]() { return true; }); + + program.parse_args(argc, argv); + + auto config_file = program.get("--config"); + auto num_iters = program.get("-n"); + auto verbose = program.get("-v"); + + std::cout << config_file << std::endl; + std::cout << num_iters << std::endl; + std::cout << verbose << std::endl; return 0; } \ No newline at end of file