#pragma once #include #include #include namespace program::traits { template struct is_repeatable : std::false_type {}; template struct is_repeatable)>>>> : std::true_type {}; template constexpr bool const is_repeatable_v = is_repeatable::value; template struct is_container : std::false_type {}; template <> struct is_container : std::false_type {}; template struct is_container> : std::true_type {}; template constexpr bool const is_container_v = is_container::value; template struct is_associative : std::false_type {}; template struct is_associative> : std::true_type {}; template constexpr bool const is_associative_v = is_associative::value; } namespace program { inline std::string join(std::string const & tok, std::vector const & data) { std::string accum = data.empty() ? "" : data.front(); for (size_t i = 1; i < data.size(); ++i) { accum += tok; accum += data[i]; } return accum; } template struct conversion_helper; /** * \brief Conversion method for positional arguments. Positional arguments are * always represented with a singular string. * \param name The name of the argument being parsed, for logging purposes * \param data A string containing the value to be processed. * \return An object of the given type */ template T convert(std::string const & name, std::string const & data) try { return conversion_helper{}(data); } catch (std::exception const & ex) { throw ArgumentStructureError(ex.what(), name); } template T convert_associative(std::string const & name, std::vector const &args) { T rval; for (std::string const &arg : args) { size_t pos = arg.find('='); if (pos == std::string::npos) { throw ArgumentStructureError("expected argument of the form key=value, got " + arg, name); } rval.emplace(arg.substr(0, pos), convert(name, arg.substr(pos + 1))); } return rval; } template T convert_container(std::string const & name, std::vector const &args) { T rval; for (std::string const &arg : args) { rval.insert(rval.end(), convert(name, arg)); } return rval; } /** * \brief Conversion method for command-line options. Because some options are * repeatable, we need to pass in a vector of objects that might be used. * \param name The name of the option being parsed, for logging purposes * \param data A vector of arguments assigned to this option. * \invariant data.size() > 0 * \return An object of the given type * \throws ArgumentStructureError if the argument has been repeated but is * not a repeatable type. */ template T convert(std::string const & name, std::vector const & data) { if constexpr (traits::is_associative_v) { return convert_associative(name, data); } else if constexpr (traits::is_container_v && !std::is_constructible_v) { return convert_container(name, data); } else if (data.size() == 1) { return convert(name, data[0]); } else { throw ArgumentStructureError("Repeated option not allowed", name); } } template struct conversion_helper< T, std::enable_if_t>> { T operator()(std::string const & str) const { return T(str); } }; template <> struct conversion_helper { int operator()(std::string const & str) const { return std::stoi(str); } }; using std::to_string; template std::string to_string(T const &) { return "?"; } inline std::string to_string(char const * str) { return str; } inline std::string const & to_string(std::string const & str) { return str; } }