#ifndef MIGRAPHX_GUARD_RTGLIB_ARGUMENT_PARSER_HPP #define MIGRAPHX_GUARD_RTGLIB_ARGUMENT_PARSER_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef _WIN32 #include #endif namespace migraphx { namespace driver { inline namespace MIGRAPHX_INLINE_NS { #ifdef MIGRAPHX_USE_CLANG_TIDY #define MIGRAPHX_DRIVER_STATIC #else #define MIGRAPHX_DRIVER_STATIC static #endif template using bare = std::remove_cv_t>; namespace detail { template auto is_container(int, T&& x) -> decltype(x.insert(x.end(), *x.begin()), std::true_type{}); template std::false_type is_container(float, T&&); } // namespace detail template struct is_container : decltype(detail::is_container(int(0), std::declval())) { }; template using is_multi_value = std::integral_constant{} and not std::is_convertible{})>; enum class color { reset = 0, bold = 1, underlined = 4, fg_red = 31, fg_green = 32, fg_yellow = 33, fg_blue = 34, fg_default = 39, bg_red = 41, bg_green = 42, bg_yellow = 43, bg_blue = 44, bg_default = 49 }; inline std::ostream& operator<<(std::ostream& os, const color& c) { #ifndef _WIN32 static const bool use_color = isatty(STDOUT_FILENO) != 0; if(use_color) return os << "\033[" << static_cast(c) << "m"; #endif return os; } template struct value_parser { template {} and not is_multi_value{})> static T apply(const std::string& x) { T result; std::stringstream ss; ss.str(x); ss >> result; if(ss.fail()) throw std::runtime_error("Failed to parse: " + x); return result; } template {} and not is_multi_value{})> static T apply(const std::string& x) { std::ptrdiff_t i; std::stringstream ss; ss.str(x); ss >> i; if(ss.fail()) throw std::runtime_error("Failed to parse: " + x); return static_cast(i); } template {} and not std::is_enum{})> static T apply(const std::string& x) { T result; using value_type = typename T::value_type; result.insert(result.end(), value_parser::apply(x)); return result; } }; template struct type_name { static const std::string& apply() { return migraphx::get_type_name(); } }; template <> struct type_name { static const std::string& apply() { static const std::string name = "std::string"; return name; } }; template struct type_name> { static const std::string& apply() { static const std::string name = "std::vector<" + type_name::apply() + ">"; return name; } }; struct argument_parser { struct argument { std::vector flags; std::function&)> action{}; std::string type = ""; std::string help = ""; std::string metavar = ""; std::string default_value = ""; unsigned nargs = 1; }; template {})> std::string as_string_value(const T& x) { return to_string_range(x); } template auto as_string_value(rank<1>, const T& x) -> decltype(to_string(x)) { return to_string(x); } template std::string as_string_value(rank<0>, const T&) { throw std::runtime_error("Can't convert to string"); } template {})> std::string as_string_value(const T& x) { return as_string_value(rank<1>{}, x); } template void operator()(T& x, const std::vector& flags, Fs... fs) { arguments.push_back({flags, [&](auto&&, const std::vector& params) { if(params.empty()) throw std::runtime_error("Flag with no value."); x = value_parser::apply(params.back()); return false; }}); argument& arg = arguments.back(); arg.type = type_name::apply(); migraphx::each_args([&](auto f) { f(x, arg); }, fs...); if(not arg.default_value.empty() and arg.nargs > 0) arg.default_value = as_string_value(x); } template void operator()(std::nullptr_t x, std::vector flags, Fs... fs) { arguments.push_back({std::move(flags)}); argument& arg = arguments.back(); arg.type = ""; arg.nargs = 0; migraphx::each_args([&](auto f) { f(x, arg); }, fs...); } MIGRAPHX_DRIVER_STATIC auto nargs(unsigned n = 1) { return [=](auto&&, auto& arg) { arg.nargs = n; }; } template MIGRAPHX_DRIVER_STATIC auto write_action(F f) { return [=](auto& x, auto& arg) { arg.action = [&, f](auto& self, const std::vector& params) { f(self, x, params); return false; }; }; } template MIGRAPHX_DRIVER_STATIC auto do_action(F f) { return [=](auto&, auto& arg) { arg.nargs = 0; arg.action = [&, f](auto& self, const std::vector&) { f(self); return true; }; }; } MIGRAPHX_DRIVER_STATIC auto append() { return write_action([](auto&, auto& x, auto& params) { using type = typename bare::value_type; std::transform(params.begin(), params.end(), std::inserter(x, x.end()), [](std::string y) { return value_parser::apply(y); }); }); } MIGRAPHX_DRIVER_STATIC auto show_help(const std::string& msg = "") { return do_action([=](auto& self) { std::cout << color::fg_yellow << "FLAGS:" << color::reset << std::endl; std::cout << std::endl; for(auto&& arg : self.arguments) { if(arg.nargs != 0) continue; const int col_align = 35; std::string prefix = " "; int len = 0; std::cout << color::fg_green; for(const std::string& a : arg.flags) { len += prefix.length() + a.length(); std::cout << prefix; std::cout << a; prefix = ", "; } std::cout << color::reset; int spaces = col_align - len; if(spaces < 0) { std::cout << std::endl; } else { for(int i = 0; i < spaces; i++) std::cout << " "; } std::cout << arg.help << std::endl; } std::cout << std::endl; std::cout << color::fg_yellow << "OPTIONS:" << color::reset << std::endl; for(auto&& arg : self.arguments) { if(arg.nargs == 0) continue; std::cout << std::endl; std::string prefix = " "; std::cout << color::fg_green; if(arg.flags.empty()) { std::cout << prefix; std::cout << arg.metavar; } for(const std::string& a : arg.flags) { std::cout << prefix; std::cout << a; prefix = ", "; } std::cout << color::reset; if(not arg.type.empty()) { std::cout << " [" << color::fg_blue << arg.type << color::reset << "]"; if(not arg.default_value.empty()) std::cout << " (Default: " << arg.default_value << ")"; } std::cout << std::endl; std::cout << " " << arg.help << std::endl; } std::cout << std::endl; if(not msg.empty()) std::cout << msg << std::endl; }); } MIGRAPHX_DRIVER_STATIC auto help(const std::string& help) { return [=](auto&, auto& arg) { arg.help = help; }; } MIGRAPHX_DRIVER_STATIC auto metavar(const std::string& metavar) { return [=](auto&, auto& arg) { arg.metavar = metavar; }; } MIGRAPHX_DRIVER_STATIC auto type(const std::string& type) { return [=](auto&, auto& arg) { arg.type = type; }; } template MIGRAPHX_DRIVER_STATIC auto set_value(T value) { return [=](auto& x, auto& arg) { arg.nargs = 0; arg.type = ""; arg.action = [&, value](auto&, const std::vector&) { x = value; return false; }; }; } bool parse(std::vector args) { std::unordered_map keywords; for(auto&& arg : arguments) { for(auto&& flag : arg.flags) keywords[flag] = arg.nargs + 1; } auto arg_map = generic_parse(std::move(args), [&](const std::string& x) { return keywords[x]; }); for(auto&& arg : arguments) { auto flags = arg.flags; if(flags.empty()) flags = {""}; for(auto&& flag : flags) { if(arg_map.count(flag) > 0) { if(arg.action(*this, arg_map[flag])) return true; } } } return false; } using string_map = std::unordered_map>; template static string_map generic_parse(std::vector as, IsKeyword is_keyword) { string_map result; std::string flag; bool clear = false; for(auto&& x : as) { auto k = is_keyword(x); if(k > 0) { flag = x; result[flag]; // Ensure the flag exists if(k == 1) flag = ""; else if(k == 2) clear = true; else clear = false; } else { result[flag].push_back(x); if(clear) flag = ""; clear = false; } } return result; } private: std::vector arguments; }; } // namespace MIGRAPHX_INLINE_NS } // namespace driver } // namespace migraphx #endif