enum-inl.h 12 KB

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