utilities.h 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. #pragma once
  2. #include <string>
  3. #include <type_traits>
  4. #include <vector>
  5. namespace program::traits {
  6. template <typename T, typename = void>
  7. struct is_repeatable : std::false_type {};
  8. template <typename F>
  9. struct is_repeatable<F, std::enable_if_t<!std::is_void_v<
  10. std::result_of_t<F(std::vector<std::string>)>>>>
  11. : std::true_type {};
  12. template <typename T>
  13. constexpr bool const is_repeatable_v = is_repeatable<T>::value;
  14. template <typename T, typename = void> struct is_container : std::false_type {};
  15. template <> struct is_container<std::string> : std::false_type {};
  16. template <typename T>
  17. struct is_container<T, std::void_t<typename T::value_type>> : std::true_type {};
  18. template <typename T>
  19. constexpr bool const is_container_v = is_container<T>::value;
  20. template <typename T, typename = void> struct is_associative : std::false_type {};
  21. template <typename T>
  22. struct is_associative<T, std::void_t<typename T::mapped_type>> : std::true_type {};
  23. template <typename T>
  24. constexpr bool const is_associative_v = is_associative<T>::value;
  25. }
  26. namespace program {
  27. inline std::string join(std::string const & tok,
  28. std::vector<std::string> const & data) {
  29. std::string accum = data.empty() ? "" : data.front();
  30. for (size_t i = 1; i < data.size(); ++i) {
  31. accum += tok;
  32. accum += data[i];
  33. }
  34. return accum;
  35. }
  36. template <typename T, typename = void> struct conversion_helper;
  37. /**
  38. * \brief Conversion method for positional arguments. Positional arguments are
  39. * always represented with a singular string.
  40. * \param name The name of the argument being parsed, for logging purposes
  41. * \param data A string containing the value to be processed.
  42. * \return An object of the given type
  43. */
  44. template <typename T>
  45. T convert(std::string const & name, std::string const & data) try {
  46. return conversion_helper<T>{}(data);
  47. } catch (std::exception const & ex) {
  48. throw ArgumentStructureError(ex.what(), name);
  49. }
  50. template <typename T, typename V = typename T::mapped_type>
  51. T convert_associative(std::string const & name, std::vector<std::string> const &args) {
  52. T rval;
  53. for (std::string const &arg : args) {
  54. size_t pos = arg.find('=');
  55. if (pos == std::string::npos) {
  56. throw ArgumentStructureError("expected argument of the form key=value, got " + arg,
  57. name);
  58. }
  59. rval.emplace(arg.substr(0, pos), convert<V>(name, arg.substr(pos + 1)));
  60. }
  61. return rval;
  62. }
  63. template <typename T, typename V = typename T::value_type>
  64. T convert_container(std::string const & name, std::vector<std::string> const &args) {
  65. T rval;
  66. for (std::string const &arg : args) {
  67. rval.insert(rval.end(), convert<V>(name, arg));
  68. }
  69. return rval;
  70. }
  71. /**
  72. * \brief Conversion method for command-line options. Because some options are
  73. * repeatable, we need to pass in a vector of objects that might be used.
  74. * \param name The name of the option being parsed, for logging purposes
  75. * \param data A vector of arguments assigned to this option.
  76. * \invariant data.size() > 0
  77. * \return An object of the given type
  78. * \throws ArgumentStructureError if the argument has been repeated but is
  79. * not a repeatable type.
  80. */
  81. template <typename T>
  82. T convert(std::string const & name, std::vector<std::string> const & data) {
  83. if constexpr (traits::is_associative_v<T>) {
  84. return convert_associative<T>(name, data);
  85. } else if constexpr (traits::is_container_v<T> && !std::is_constructible_v<T, std::string>) {
  86. return convert_container<T>(name, data);
  87. } else if (data.size() == 1) {
  88. return convert<T>(name, data[0]);
  89. } else {
  90. throw ArgumentStructureError("Repeated option not allowed", name);
  91. }
  92. }
  93. template <typename T>
  94. struct conversion_helper<
  95. T, std::enable_if_t<std::is_convertible_v<std::string, T>>> {
  96. T operator()(std::string const & str) const { return T(str); }
  97. };
  98. template <> struct conversion_helper<int> {
  99. int operator()(std::string const & str) const { return std::stoi(str); }
  100. };
  101. using std::to_string;
  102. template <typename T> std::string to_string(T const &) { return "?"; }
  103. inline std::string to_string(char const * str) { return str; }
  104. inline std::string const & to_string(std::string const & str) { return str; }
  105. }