File: StringConverter.hpp

package info (click to toggle)
reflect-cpp 0.18.0%2Bds-3
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 12,524 kB
  • sloc: cpp: 44,484; python: 131; makefile: 30; sh: 3
file content (124 lines) | stat: -rw-r--r-- 3,603 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#ifndef RFL_INTERNAL_ENUMS_STRINGCONVERTER_HPP_
#define RFL_INTERNAL_ENUMS_STRINGCONVERTER_HPP_

#include <algorithm>
#include <array>
#include <string>
#include <string_view>
#include <type_traits>

#include "../../Result.hpp"
#include "../../internal/strings/strings.hpp"
#include "../../type_name_t.hpp"
#include "get_enum_names.hpp"
#include "is_flag_enum.hpp"

namespace rfl ::internal ::enums {

template <class EnumType>
class StringConverter {
 public:
  static constexpr bool is_flag_enum_ = is_flag_enum<EnumType>;

  static constexpr auto names_ = get_enum_names<EnumType, is_flag_enum_>();

  using NamesLiteral = typename decltype(names_)::Literal;

 public:
  /// Transform an enum to a matching string.
  static std::string enum_to_string(const EnumType _enum) {
    if constexpr (is_flag_enum_) {
      return flag_enum_to_string(_enum);
    } else {
      return enum_to_single_string(_enum);
    }
  }

  /// Transforms a string to the matching enum.
  static Result<EnumType> string_to_enum(const std::string& _str) {
    if constexpr (is_flag_enum_) {
      return string_to_flag_enum(_str);
    } else {
      return single_string_to_enum(_str);
    }
  }

 private:
  /// Iterates through the enum bit by bit and matches it against the flags.
  static std::string flag_enum_to_string(const EnumType _e) {
    using T = std::underlying_type_t<EnumType>;
    auto val = static_cast<T>(_e);
    int i = 0;
    std::vector<std::string> flags;
    while (val != 0) {
      const auto bit = val & static_cast<T>(1);
      if (bit == 1) {
        auto str = enum_to_single_string(
            static_cast<EnumType>(static_cast<T>(1) << i));
        flags.emplace_back(std::move(str));
      }
      ++i;
      val >>= 1;
    }
    return strings::join("|", flags);
  }

  /// This assumes that _enum can be exactly matched to one of the names and
  /// does not have to be combined using |.
  static std::string enum_to_single_string(const EnumType _enum) {
    const auto to_str = [](const auto _l) { return _l.str(); };

    for (size_t i = 0; i < names_.size; ++i) {
      if (names_.enums_[i] == _enum) {
        return NamesLiteral::from_value(
                   static_cast<typename NamesLiteral::ValueType>(i))
            .transform(to_str)
            .value();
      }
    }

    return std::to_string(static_cast<std::underlying_type_t<EnumType>>(_enum));
  }

  /// Finds the enum matching the literal.
  static EnumType literal_to_enum(const NamesLiteral _lit) noexcept {
    return names_.enums_[_lit.value()];
  }

  /// This assumes that _enum can be exactly matched to one of the names and
  /// does not have to be combined using |.
  static Result<EnumType> single_string_to_enum(const std::string& _str) {
    const auto r = NamesLiteral::from_string(_str).transform(literal_to_enum);
    if (r) {
      return r;
    } else {
      try {
        return static_cast<EnumType>(std::stoi(_str));
      } catch (std::exception& exp) {
        return error(exp.what());
      }
    }
  }

  /// Only relevant if this is a flag enum - combines the different matches
  /// using |.
  static Result<EnumType> string_to_flag_enum(
      const std::string& _str) noexcept {
    using T = std::underlying_type_t<EnumType>;
    const auto split = strings::split(_str, "|");
    auto res = static_cast<T>(0);
    for (const auto& s : split) {
      const auto r = single_string_to_enum(s);
      if (r) {
        res |= static_cast<T>(*r);
      } else {
        return r;
      }
    }
    return static_cast<EnumType>(res);
  }
};

}  // namespace rfl::internal::enums

#endif