utilities.h 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  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. }
  15. namespace program {
  16. inline std::string join(std::string const & tok,
  17. std::vector<std::string> const & data) {
  18. std::string accum = data.empty() ? "" : data.front();
  19. for (size_t i = 1; i < data.size(); ++i) {
  20. accum += tok;
  21. accum += data[i];
  22. }
  23. return accum;
  24. }
  25. template <typename T, typename = void> struct conversion_helper;
  26. /**
  27. * \brief Conversion method for positional arguments. Positional arguments are
  28. * always represented with a singular string.
  29. * \param name The name of the argument being parsed, for logging purposes
  30. * \param data A string containing the value to be processed.
  31. * \return An object of the given type
  32. */
  33. template <typename T>
  34. T convert(std::string const & name, std::string const & data) {
  35. return conversion_helper<T>{}(data);
  36. }
  37. /**
  38. * \brief Conversion method for command-line options. Because some options are
  39. * repeatable, we need to pass in a vector of objects that might be used.
  40. * \param name The name of the option being parsed, for logging purposes
  41. * \param data A vector of arguments assigned to this option.
  42. * \invariant data.size() > 0
  43. * \return An object of the given type
  44. * \throws ArgumentStructureError if the argument has been repeated but is
  45. * not a repeatable type.
  46. */
  47. template <typename T>
  48. T convert(std::string const & name, std::vector<std::string> const & data) {
  49. conversion_helper<T> helper;
  50. try {
  51. if constexpr (traits::is_repeatable_v<decltype(helper)>) {
  52. return helper(data);
  53. } else if (data.size() == 1) {
  54. return helper(data.front());
  55. }
  56. } catch (std::exception const & ex) {
  57. throw ArgumentStructureError(ex.what(), name);
  58. }
  59. throw ArgumentStructureError("Repeated option not allowed", name);
  60. }
  61. template <typename T>
  62. struct conversion_helper<
  63. T, std::enable_if_t<std::is_convertible_v<std::string, T>>> {
  64. T operator()(std::string const & str) const { return T(str); }
  65. };
  66. template <> struct conversion_helper<int> {
  67. int operator()(std::string const & str) const { return std::stoi(str); }
  68. };
  69. template <typename T>
  70. struct conversion_helper<std::vector<T>> : conversion_helper<T> {
  71. using conversion_helper<T>::operator();
  72. std::vector<T> operator()(std::vector<std::string> const & data) const {
  73. std::vector<T> rval;
  74. for (auto & str : data) {
  75. rval.emplace_back((*this)(str));
  76. }
  77. return rval;
  78. }
  79. };
  80. using std::to_string;
  81. template <typename T> std::string to_string(T const &) { return "?"; }
  82. inline std::string to_string(char const * str) { return str; }
  83. inline std::string const & to_string(std::string const & str) { return str; }
  84. }