enum-inl.h 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. #pragma once
  2. #ifndef ENUM_INL_H_
  3. #error "Direct inclusion of this file is not allowed, include enum.h"
  4. // For the sake of sane code completion.
  5. #include "enum.h"
  6. #endif
  7. #include <util/string/printf.h>
  8. #include <util/string/cast.h>
  9. #include <util/generic/cast.h>
  10. #include <algorithm>
  11. #include <stdexcept>
  12. namespace NYT {
  13. ////////////////////////////////////////////////////////////////////////////////
  14. #define ENUM__CLASS(enumType, underlyingType, seq) \
  15. enum class enumType : underlyingType \
  16. { \
  17. PP_FOR_EACH(ENUM__DOMAIN_ITEM, seq) \
  18. };
  19. #define ENUM__DOMAIN_ITEM(item) \
  20. PP_IF( \
  21. PP_IS_SEQUENCE(item), \
  22. ENUM__DOMAIN_ITEM_SEQ, \
  23. ENUM__DOMAIN_ITEM_ATOMIC \
  24. )(item)()
  25. #define ENUM__DOMAIN_ITEM_ATOMIC(item) \
  26. item PP_COMMA
  27. #define ENUM__DOMAIN_ITEM_SEQ(seq) \
  28. PP_ELEMENT(seq, 0) = PP_ELEMENT(seq, 1) PP_COMMA
  29. ////////////////////////////////////////////////////////////////////////////////
  30. namespace NDetail {
  31. template <typename TValues>
  32. constexpr bool CheckValuesMonotonic(const TValues& values)
  33. {
  34. return std::adjacent_find(values.begin(), values.end(), std::greater_equal<>()) == values.end();
  35. }
  36. template <typename TValues>
  37. constexpr bool CheckValuesUnique(const TValues& values)
  38. {
  39. for (size_t i = 0; i < std::size(values); ++i) {
  40. for (size_t j = i + 1; j < std::size(values); ++j) {
  41. if (values[i] == values[j]) {
  42. return false;
  43. }
  44. }
  45. }
  46. return true;
  47. }
  48. template <typename TNames>
  49. constexpr bool CheckDomainNames(const TNames& names)
  50. {
  51. for (size_t i = 0; i < std::size(names); ++i) {
  52. if (std::size(names[i]) == 0) {
  53. return false;
  54. }
  55. for (size_t j = 1; j < std::size(names[i]); ++j) {
  56. // If name does not start with a capital letter, all the others must be in lowercase.
  57. if (('A' <= names[i][j] && names[i][j] <= 'Z') && ('A' > names[i][0] || names[i][0] > 'Z')) {
  58. return false;
  59. }
  60. }
  61. }
  62. return true;
  63. }
  64. } // namespace NDetail
  65. ////////////////////////////////////////////////////////////////////////////////
  66. #define ENUM__BEGIN_TRAITS(enumType, underlyingType, isBit, isStringSerializable, seq) \
  67. struct TEnumTraitsImpl_##enumType \
  68. { \
  69. using T = enumType; \
  70. \
  71. [[maybe_unused]] static constexpr bool IsBitEnum = isBit; \
  72. [[maybe_unused]] static constexpr bool IsStringSerializableEnum = isStringSerializable; \
  73. static constexpr int DomainSize = PP_COUNT(seq); \
  74. \
  75. static constexpr int GetDomainSize() \
  76. { \
  77. return DomainSize; \
  78. } \
  79. \
  80. static constexpr std::array<TStringBuf, DomainSize> Names{{ \
  81. PP_FOR_EACH(ENUM__GET_DOMAIN_NAMES_ITEM, seq) \
  82. }}; \
  83. static_assert(::NYT::NDetail::CheckDomainNames(Names), \
  84. "Enumeration " #enumType " contains names in wrong format"); \
  85. static constexpr std::array<T, DomainSize> Values{{ \
  86. PP_FOR_EACH(ENUM__GET_DOMAIN_VALUES_ITEM, seq) \
  87. }}; \
  88. \
  89. [[maybe_unused]] static constexpr bool IsMonotonic = \
  90. ::NYT::NDetail::CheckValuesMonotonic(Values); \
  91. \
  92. static TStringBuf GetTypeName() \
  93. { \
  94. static constexpr TStringBuf Result = PP_STRINGIZE(enumType); \
  95. return Result; \
  96. } \
  97. \
  98. static const std::optional<TStringBuf> FindLiteralByValue(T value) \
  99. { \
  100. for (int i = 0; i < GetDomainSize(); ++i) { \
  101. if (Values[i] == value) { \
  102. return Names[i]; \
  103. } \
  104. } \
  105. return std::nullopt; \
  106. } \
  107. \
  108. static std::optional<T> FindValueByLiteral(TStringBuf literal) \
  109. { \
  110. for (int i = 0; i < GetDomainSize(); ++i) { \
  111. if (Names[i] == literal) { \
  112. return Values[i]; \
  113. } \
  114. } \
  115. return std::nullopt; \
  116. } \
  117. \
  118. static constexpr const std::array<TStringBuf, DomainSize>& GetDomainNames() \
  119. { \
  120. return Names; \
  121. } \
  122. \
  123. static constexpr const std::array<T, DomainSize>& GetDomainValues() \
  124. { \
  125. return Values; \
  126. }
  127. #define ENUM__GET_DOMAIN_VALUES_ITEM(item) \
  128. PP_IF( \
  129. PP_IS_SEQUENCE(item), \
  130. ENUM__GET_DOMAIN_VALUES_ITEM_SEQ, \
  131. ENUM__GET_DOMAIN_VALUES_ITEM_ATOMIC \
  132. )(item)
  133. #define ENUM__GET_DOMAIN_VALUES_ITEM_SEQ(seq) \
  134. ENUM__GET_DOMAIN_VALUES_ITEM_ATOMIC(PP_ELEMENT(seq, 0))
  135. #define ENUM__GET_DOMAIN_VALUES_ITEM_ATOMIC(item) \
  136. T::item,
  137. #define ENUM__GET_DOMAIN_NAMES_ITEM(item) \
  138. PP_IF( \
  139. PP_IS_SEQUENCE(item), \
  140. ENUM__GET_DOMAIN_NAMES_ITEM_SEQ, \
  141. ENUM__GET_DOMAIN_NAMES_ITEM_ATOMIC \
  142. )(item)
  143. #define ENUM__GET_DOMAIN_NAMES_ITEM_SEQ(seq) \
  144. PP_IF( \
  145. ENUM__ITEM_SEQ_HAS_DOMAIN_NAME(seq), \
  146. ENUM__GET_DOMAIN_NAMES_ITEM_SEQ_CUSTOM, \
  147. ENUM__GET_DOMAIN_NAMES_ITEM_SEQ_AUTO \
  148. )(seq)
  149. #define ENUM__ITEM_SEQ_HAS_DOMAIN_NAME(seq) \
  150. PP_CONCAT(ENUM__ITEM_SEQ_HAS_DOMAIN_NAME_, PP_COUNT(seq))
  151. #define ENUM__ITEM_SEQ_HAS_DOMAIN_NAME_2 PP_FALSE
  152. #define ENUM__ITEM_SEQ_HAS_DOMAIN_NAME_3 PP_TRUE
  153. #define ENUM__GET_DOMAIN_NAMES_ITEM_SEQ_CUSTOM(seq) \
  154. TStringBuf(PP_ELEMENT(seq, 2)),
  155. #define ENUM__GET_DOMAIN_NAMES_ITEM_SEQ_AUTO(seq) \
  156. ENUM__GET_DOMAIN_NAMES_ITEM_ATOMIC(PP_ELEMENT(seq, 0))
  157. #define ENUM__GET_DOMAIN_NAMES_ITEM_ATOMIC(item) \
  158. TStringBuf(PP_STRINGIZE(item)),
  159. #define ENUM__VALIDATE_UNIQUE(enumType) \
  160. static_assert(IsMonotonic || ::NYT::NDetail::CheckValuesUnique(Values), \
  161. "Enumeration " #enumType " contains duplicate values");
  162. #define ENUM__END_TRAITS(enumType) \
  163. }; \
  164. \
  165. [[maybe_unused]] inline TEnumTraitsImpl_##enumType GetEnumTraitsImpl(enumType) \
  166. { \
  167. return {}; \
  168. } \
  169. \
  170. using ::ToString; \
  171. [[maybe_unused]] inline TString ToString(enumType value) \
  172. { \
  173. return ::NYT::TEnumTraits<enumType>::ToString(value); \
  174. }
  175. ////////////////////////////////////////////////////////////////////////////////
  176. template <class T>
  177. constexpr int TEnumTraitsWithKnownDomain<T, true>::GetDomainSize()
  178. {
  179. return TEnumTraitsImpl<T>::GetDomainSize();
  180. }
  181. template <class T>
  182. constexpr auto TEnumTraitsWithKnownDomain<T, true>::GetDomainNames() -> const std::array<TStringBuf, GetDomainSize()>&
  183. {
  184. return TEnumTraitsImpl<T>::GetDomainNames();
  185. }
  186. template <class T>
  187. constexpr auto TEnumTraitsWithKnownDomain<T, true>::GetDomainValues() -> const std::array<T, GetDomainSize()>&
  188. {
  189. return TEnumTraitsImpl<T>::GetDomainValues();
  190. }
  191. template <class T>
  192. constexpr T TEnumTraitsWithKnownDomain<T, true>::GetMinValue()
  193. requires (!TEnumTraitsImpl<T>::IsBitEnum)
  194. {
  195. const auto& values = GetDomainValues();
  196. static_assert(!values.empty()); \
  197. return *std::min_element(std::begin(values), std::end(values));
  198. }
  199. template <class T>
  200. constexpr T TEnumTraitsWithKnownDomain<T, true>::GetMaxValue()
  201. requires (!TEnumTraitsImpl<T>::IsBitEnum)
  202. {
  203. const auto& values = GetDomainValues();
  204. static_assert(!values.empty()); \
  205. return *std::max_element(std::begin(values), std::end(values));
  206. }
  207. template <class T>
  208. std::vector<T> TEnumTraitsWithKnownDomain<T, true>::Decompose(T value)
  209. requires (TEnumTraitsImpl<T>::IsBitEnum)
  210. {
  211. std::vector<T> result;
  212. for (auto domainValue : GetDomainValues()) {
  213. if (Any(value & domainValue)) {
  214. result.push_back(domainValue);
  215. }
  216. }
  217. return result;
  218. }
  219. ////////////////////////////////////////////////////////////////////////////////
  220. template <class T>
  221. TStringBuf TEnumTraits<T, true>::GetTypeName()
  222. {
  223. return TEnumTraitsImpl<T>::GetTypeName();
  224. }
  225. template <class T>
  226. std::optional<T> TEnumTraits<T, true>::FindValueByLiteral(TStringBuf literal)
  227. {
  228. return TEnumTraitsImpl<T>::FindValueByLiteral(literal);
  229. }
  230. template <class T>
  231. std::optional<TStringBuf> TEnumTraits<T, true>::FindLiteralByValue(T value)
  232. {
  233. return TEnumTraitsImpl<T>::FindLiteralByValue(value);
  234. }
  235. template <class T>
  236. TString TEnumTraits<T, true>::ToString(T value)
  237. {
  238. using ::ToString;
  239. if (auto optionalLiteral = TEnumTraits<T>::FindLiteralByValue(value)) {
  240. return ToString(*optionalLiteral);
  241. }
  242. TString result;
  243. result = TEnumTraits<T>::GetTypeName();
  244. result += "(";
  245. result += ToString(ToUnderlying(value));
  246. result += ")";
  247. return result;
  248. }
  249. template <class T>
  250. T TEnumTraits<T, true>::FromString(TStringBuf literal)
  251. {
  252. auto optionalValue = FindValueByLiteral(literal);
  253. if (!optionalValue) {
  254. throw ::NYT::TSimpleException(Sprintf("Error parsing %s value %s",
  255. GetTypeName().data(),
  256. TString(literal).Quote().c_str()).c_str());
  257. }
  258. return *optionalValue;
  259. }
  260. ////////////////////////////////////////////////////////////////////////////////
  261. #define ENUM__BINARY_BITWISE_OPERATOR(T, assignOp, op) \
  262. [[maybe_unused]] inline constexpr T operator op (T lhs, T rhs) \
  263. { \
  264. return T(ToUnderlying(lhs) op ToUnderlying(rhs)); \
  265. } \
  266. \
  267. [[maybe_unused]] inline T& operator assignOp (T& lhs, T rhs) \
  268. { \
  269. lhs = T(ToUnderlying(lhs) op ToUnderlying(rhs)); \
  270. return lhs; \
  271. }
  272. #define ENUM__UNARY_BITWISE_OPERATOR(T, op) \
  273. [[maybe_unused]] inline constexpr T operator op (T value) \
  274. { \
  275. return T(op ToUnderlying(value)); \
  276. }
  277. #define ENUM__BIT_SHIFT_OPERATOR(T, assignOp, op) \
  278. [[maybe_unused]] inline constexpr T operator op (T lhs, size_t rhs) \
  279. { \
  280. return T(ToUnderlying(lhs) op rhs); \
  281. } \
  282. \
  283. [[maybe_unused]] inline T& operator assignOp (T& lhs, size_t rhs) \
  284. { \
  285. lhs = T(ToUnderlying(lhs) op rhs); \
  286. return lhs; \
  287. }
  288. #define ENUM__BITWISE_OPS(enumType) \
  289. ENUM__BINARY_BITWISE_OPERATOR(enumType, &=, &) \
  290. ENUM__BINARY_BITWISE_OPERATOR(enumType, |=, | ) \
  291. ENUM__BINARY_BITWISE_OPERATOR(enumType, ^=, ^) \
  292. ENUM__UNARY_BITWISE_OPERATOR(enumType, ~) \
  293. ENUM__BIT_SHIFT_OPERATOR(enumType, <<=, << ) \
  294. ENUM__BIT_SHIFT_OPERATOR(enumType, >>=, >> )
  295. ////////////////////////////////////////////////////////////////////////////////
  296. template <typename E>
  297. requires TEnumTraits<E>::IsBitEnum
  298. constexpr bool Any(E value) noexcept
  299. {
  300. return ToUnderlying(value) != 0;
  301. }
  302. template <typename E>
  303. requires TEnumTraits<E>::IsBitEnum
  304. constexpr bool None(E value) noexcept
  305. {
  306. return ToUnderlying(value) == 0;
  307. }
  308. ////////////////////////////////////////////////////////////////////////////////
  309. } // namespace NYT