diff --git a/include/utils/command_line/arguments.hpp b/include/utils/command_line/arguments.hpp index 8221bddea..fae36211a 100644 --- a/include/utils/command_line/arguments.hpp +++ b/include/utils/command_line/arguments.hpp @@ -1,52 +1,158 @@ #pragma once #include +#include +#include +#include +#include +#include #include #include #include "utils/option.hpp" -namespace -{ +namespace { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-function" -auto all_arguments(int argc, char *argv[]) -{ - return std::vector(argv + 1, argv + argc); +class ProgramArgumentException : public std::exception { + public: + virtual const char *what() const throw() { + return "Invalid Command Line Arguments"; + } +} arg_exception; + +class ProgramArguments { + private: + std::map> arguments_; + std::vector required_arguments_; + std::mutex mutex_; + + bool is_flag(const std::string &arg) { return arg[0] == '-'; } + + bool is_valid() { + for (const auto &arg : required_arguments_) + for (const auto &a : arguments_) + if (!arguments_.count(arg)) return false; + return true; + } + + protected: + ProgramArguments() {} + ~ProgramArguments() {} + + public: + class Argument { + private: + std::string arg_; + + public: + Argument(const std::string &arg) : arg_(arg) {} + std::string GetString() { return arg_; } + int GetInteger() { return std::atoi(arg_.c_str()); }; + float GetFloat() { return std::atof(arg_.c_str()); }; + long GetLong() { return std::atol(arg_.c_str()); } + }; + + static ProgramArguments &instance() { + static ProgramArguments instance_; + return instance_; + } + + ProgramArguments(ProgramArguments const &) = delete; + ProgramArguments(ProgramArguments &&) = delete; + ProgramArguments &operator=(ProgramArguments const &) = delete; + ProgramArguments &operator=(ProgramArguments &&) = delete; + + void register_args(int argc, char *argv[]) { + std::lock_guard lock(mutex_); + + for (int i = 1; i < argc; i++) { + std::string flag(*(argv + i)); + if (is_flag(flag)) { + arguments_[flag] = {}; + while (i < argc - 1) { + i++; + std::string arg(*(argv + i)); + if (!is_flag(arg)) + arguments_[flag].push_back(arg); + else { + i--; + break; + } + } + } + } + + if (required_arguments_.empty()) return; + + if (!is_valid()) throw arg_exception; + } + + void register_required_args(std::vector args) { + required_arguments_ = args; + + if (arguments_.empty()) return; + + if (!is_valid()) throw arg_exception; + } + + bool contains_flag(const std::string &flag) { + return arguments_.count(flag) > 0; + } + + auto get_arg(const std::string &flag, const std::string &default_value) { + if (contains_flag(flag)) return Argument(arguments_[flag][0]); + return Argument(default_value); + } + + auto get_arg_list(const std::string &flag, + const std::vector &default_value) { + std::vector ret; + if (contains_flag(flag)) { + for (const auto &arg : arguments_[flag]) ret.emplace_back(arg); + } else + for (const auto &arg : default_value) ret.emplace_back(arg); + return ret; + } + + void clear() { + arguments_.clear(); + required_arguments_.clear(); + } +}; + +auto all_arguments(int argc, char *argv[]) { + return std::vector(argv + 1, argv + argc); } bool contains_argument(const std::vector &all, - const std::string &flag) -{ - return std::find(all.begin(), all.end(), flag) != all.end(); + const std::string &flag) { + return std::find(all.begin(), all.end(), flag) != all.end(); } // just returns argument value auto get_argument(const std::vector &all, const std::string &flag, - const std::string &default_value) -{ - auto it = std::find(all.begin(), all.end(), flag); + const std::string &default_value) { + auto it = std::find(all.begin(), all.end(), flag); - if (it == all.end()) return default_value; + if (it == all.end()) return default_value; - return all[std::distance(all.begin(), it) + 1]; + return all[std::distance(all.begin(), it) + 1]; } -// removes argument value from the all vector Option take_argument(std::vector &all, - const std::string &flag) -{ - auto it = std::find(all.begin(), all.end(), flag); + const std::string &flag) { + auto it = std::find(all.begin(), all.end(), flag); - if (it == all.end()) return make_option(); + if (it == all.end()) return make_option(); - auto s = std::string(all[std::distance(all.begin(), it) + 1]); - it++; - it++; - all.erase(std::find(all.begin(), all.end(), flag), it); + auto s = std::string(all[std::distance(all.begin(), it) + 1]); + it++; + it++; + all.erase(std::find(all.begin(), all.end(), flag), it); - return make_option(std::move(s)); + return make_option(std::move(s)); } #pragma clang diagnostic pop diff --git a/tests/benchmark/data_structures/concurrent/concurrent_map.cpp b/tests/benchmark/data_structures/concurrent/concurrent_map.cpp index c07736c19..a62260817 100644 --- a/tests/benchmark/data_structures/concurrent/concurrent_map.cpp +++ b/tests/benchmark/data_structures/concurrent/concurrent_map.cpp @@ -107,15 +107,20 @@ auto BM_ContainsValue = [](benchmark::State& state, auto* map, auto elements) { -string-length number */ void parse_arguments(int argc, char** argv) { - auto para = all_arguments(argc, argv); + ProgramArguments::instance().register_args(argc, argv); - RANGE_START = std::stoi(get_argument(para, "-start", "0")); - RANGE_END = std::stoi(get_argument(para, "-end", "1000000000")); + RANGE_START = + ProgramArguments::instance().get_arg("-start", "0").GetInteger(); + RANGE_END = + ProgramArguments::instance().get_arg("-end", "1000000000").GetInteger(); - THREADS = std::min(std::stoi(get_argument(para, "-threads", "1")), - (int)std::thread::hardware_concurrency()); + THREADS = std::min( + ProgramArguments::instance().get_arg("-threads", "1").GetInteger(), + (int)std::thread::hardware_concurrency()); - STRING_LENGTH = std::stoi(get_argument(para, "-string-length", "128")); + STRING_LENGTH = ProgramArguments::instance() + .get_arg("-string-length", "128") + .GetInteger(); } int main(int argc, char** argv) { diff --git a/tests/benchmark/data_structures/concurrent/concurrent_map_mix.cpp b/tests/benchmark/data_structures/concurrent/concurrent_map_mix.cpp index 53bd5454e..85cd85192 100644 --- a/tests/benchmark/data_structures/concurrent/concurrent_map_mix.cpp +++ b/tests/benchmark/data_structures/concurrent/concurrent_map_mix.cpp @@ -36,11 +36,10 @@ static void Rape(benchmark::State& state, ConcurrentMap* map, auto accessor = map->access(); for (int start = 0; start < state.range(0); start++) { - float current_percentage = (float)start / (float)number_of_elements; - if (current_percentage * 100 < (float)INSERT_PERC) { + float current_percentage = (float)start / (float)number_of_elements * 100; + if (current_percentage < (float)INSERT_PERC) { accessor.insert(elements[start].first, elements[start].second); - } else if (current_percentage * 100 < - (float)CONTAINS_PERC + INSERT_PERC) { + } else if (current_percentage < (float)CONTAINS_PERC + INSERT_PERC) { accessor.contains(elements[start].first); } else { accessor.remove(elements[start].first); @@ -78,11 +77,14 @@ auto BM_Rape = [](benchmark::State& state, auto* map, auto& elements) { -threads number */ void parse_arguments(int argc, char** argv) { - auto para = all_arguments(argc, argv); + ProgramArguments::instance().register_args(argc, argv); - INSERT_PERC = std::stoi(get_argument(para, "-insert", "50")); - DELETE_PERC = std::stoi(get_argument(para, "-delete", "20")); - CONTAINS_PERC = std::stoi(get_argument(para, "-find", "30")); + INSERT_PERC = + ProgramArguments::instance().get_arg("-insert", "50").GetInteger(); + DELETE_PERC = + ProgramArguments::instance().get_arg("-delete", "20").GetInteger(); + CONTAINS_PERC = + ProgramArguments::instance().get_arg("-find", "30").GetInteger(); if (INSERT_PERC + DELETE_PERC + CONTAINS_PERC != 100) { std::cout << "Invalid percentage" << std::endl; @@ -90,12 +92,15 @@ void parse_arguments(int argc, char** argv) { exit(-1); } - RANGE_START = std::stoi(get_argument(para, "-start", "0")); - RANGE_END = std::stoi(get_argument(para, "-end", "1000000000")); + RANGE_START = + ProgramArguments::instance().get_arg("-start", "0").GetInteger(); - std::cout << "start" << RANGE_START << std::endl; - THREADS = std::min(std::stoi(get_argument(para, "-threads", "1")), - (int)std::thread::hardware_concurrency()); + RANGE_END = + ProgramArguments::instance().get_arg("-end", "1000000000").GetInteger(); + + THREADS = std::min( + ProgramArguments::instance().get_arg("-threads", "1").GetInteger(), + (int)std::thread::hardware_concurrency()); } int main(int argc, char** argv) { diff --git a/tests/unit/program_argument.cpp b/tests/unit/program_argument.cpp new file mode 100644 index 000000000..0e1a95f8a --- /dev/null +++ b/tests/unit/program_argument.cpp @@ -0,0 +1,95 @@ +#define CATCH_CONFIG_MAIN +#include "catch.hpp" + +#include "utils/command_line/arguments.hpp" + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wwritable-strings" + +TEST_CASE("ProgramArgument FlagOnly Test") { + ProgramArguments::instance().clear(); + + int argc = 2; + + char* argv[] = {"ProgramArgument FlagOnly Test", "-test"}; + + ProgramArguments::instance().register_args(argc, argv); + ProgramArguments::instance().register_required_args({"-test"}); + + REQUIRE(ProgramArguments::instance().contains_flag("-test") == true); +} + +TEST_CASE("ProgramArgument Single Entry Test") { + ProgramArguments::instance().clear(); + + int argc = 3; + + char* argv[] = {"ProgramArgument Single Entry Test", "-bananas", "99"}; + + ProgramArguments::instance().register_required_args({"-bananas"}); + ProgramArguments::instance().register_args(argc, argv); + + REQUIRE( + ProgramArguments::instance().get_arg("-bananas", "100").GetInteger() == + 99); +} + +TEST_CASE("ProgramArgument Multiple Entries Test") { + ProgramArguments::instance().clear(); + + int argc = 4; + + char* argv[] = {"ProgramArgument Multiple Entries Test", "-files", + "first_file.txt", "second_file.txt"}; + + ProgramArguments::instance().register_args(argc, argv); + + auto files = ProgramArguments::instance().get_arg_list("-files", {}); + + REQUIRE(files[0].GetString() == "first_file.txt"); +} + +TEST_CASE("ProgramArgument Combination Test") { + ProgramArguments::instance().clear(); + + int argc = 14; + + char* argv[] = {"ProgramArgument Combination Test", + "-run_tests", + "-tests", + "Test1", + "Test2", + "Test3", + "-run_times", + "10", + "-export", + "test1.txt", + "test2.txt", + "test3.txt", + "-import", + "data.txt"}; + + ProgramArguments::instance().register_args(argc, argv); + + REQUIRE(ProgramArguments::instance().contains_flag("-run_tests") == true); + + auto tests = ProgramArguments::instance().get_arg_list("-tests", {}); + REQUIRE(tests[0].GetString() == "Test1"); + REQUIRE(tests[1].GetString() == "Test2"); + REQUIRE(tests[2].GetString() == "Test3"); + + REQUIRE( + ProgramArguments::instance().get_arg("-run_times", "0").GetInteger() == + 10); + + auto exports = ProgramArguments::instance().get_arg_list("-export", {}); + REQUIRE(exports[0].GetString() == "test1.txt"); + REQUIRE(exports[1].GetString() == "test2.txt"); + REQUIRE(exports[2].GetString() == "test3.txt"); + + REQUIRE( + ProgramArguments::instance().get_arg("-import", "test.txt").GetString() == + "data.txt"); +} + +#pragma clang diagnostic pop