// -*- c++ -*- // Distributed under the BSD 2-Clause License. // See accompanying file LICENSE for details. #pragma once #include #include #include #include #include #include template class ArgParser { public: struct missing_arg{}; int parse(int argc, const char* const argv[]) { if(argc < 1) { return 1; } for(int i = 1; i < argc; ++i) // skip argv[0] which is program name { bool was_handled{false}; for(const auto& option : options) { enum class state { handled, unhandled }; std::pair ret = std::visit([&](auto&& opt) -> std::pair { if(opt.shortopt != argv[i] && opt.longopt != argv[i]) { return {0, state::unhandled}; } try { auto arg = convert(argc, argv, i, opt.t); return {opt.cb(arg), state::handled}; } catch(std::invalid_argument&) { std::cout << "Invalid " << opt.shortopt << " argument.\n"; std::cout << opt.help << "\n"; return {1, state::handled}; } catch(missing_arg&) { std::cout << "Missing " << opt.shortopt << " argument.\n"; std::cout << opt.help << "\n"; return {1, state::handled}; } }, option); if(ret.second == state::handled && ret.first != 0) { return ret.first; } was_handled |= ret.second == state::handled; if(was_handled) { break; } } if(!was_handled) { std::cout << "Unknown argument " << argv[i] << '\n'; return 1; } } return 0; } template void add(const std::string& shortopt, const std::string& longopt, std::function cb, const std::string& help) { options.emplace_back(Opt{shortopt, longopt, cb, help}); } private: template struct Opt { //using contained_type = T; std::string shortopt; std::string longopt; std::function cb; std::string help; T t{}; }; template T convert(int argc, const char* const argv[], int& i, T) { auto opt = convert(argc, argv, i, std::optional{}); if(!opt) { throw missing_arg{}; } return *opt; } template std::optional convert(int argc, const char* const argv[], int& i, std::optional) { std::string arg; bool has_arg{false}; if(i+1 < argc) { arg = argv[i+1]; has_arg = !arg.starts_with("-"); if(has_arg) { ++i; } } if(!has_arg) { return {}; } if constexpr (std::is_same_v) { return std::stoi(arg); } else if constexpr (std::is_same_v) { return std::stod(arg); } else if constexpr (std::is_same_v) { return arg; } else { static_assert(std::is_same_v, "missing"); } return {}; } using Opts = std::variant...>; std::vector options; };