farm-ng-core
enum_flag_details.h
Go to the documentation of this file.
1 // Copyright 2022, farm-ng inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 // Copyright (c) Facebook, Inc. and its affiliates.
16 //
17 // This source code is licensed under the MIT license found in the
18 // LICENSE file in the root directory of this source tree.
19 
20 #pragma once
21 
22 #include <farm_pp/preprocessor/seq/for_each.hpp>
23 #include <farm_pp/preprocessor/seq/for_each_i.hpp>
24 #include <farm_pp/preprocessor/seq/pop_front.hpp>
25 #include <farm_pp/preprocessor/stringize.hpp>
26 #include <farm_pp/preprocessor/tuple/elem.hpp>
27 #include <farm_pp/preprocessor/tuple/size.hpp>
28 #include <farm_pp/preprocessor/tuple/to_seq.hpp>
29 
30 #include <array>
31 #include <string>
32 #include <string_view>
33 #include <type_traits>
34 
35 // Output: , ${FLAG} = (1 << ($I + 1))
36 #define FARM_ENUMFLAGS_DETAILS_OP_INIT_FLAG(dummy1, dummy2, I, FLAG) \
37  , FLAG = (1 << (I + 1))
38 
39 // Input: (A, B, C, D)
40 // Output: A=1, B=(1<<1), C=(1<<2), D=(1<<3)
41 #define FARM_ENUMFLAGS_DETAILS_FLAG_DEFINITIONS(...) \
42  FARM_PP_TUPLE_ELEM(0, __VA_ARGS__) = 1 FARM_PP_SEQ_FOR_EACH_I( \
43  FARM_ENUMFLAGS_DETAILS_OP_INIT_FLAG, \
44  _, \
45  FARM_PP_SEQ_POP_FRONT(FARM_PP_TUPLE_TO_SEQ(__VA_ARGS__)))
46 
47 // Output:
48 // if (str == "${FLAGS}") {
49 // value = ${TYPE}:${FLAG};
50 // return true;
51 // }
52 #define FARM_ENUMFLAG_DETAILS_OP_SET_ENUM_FROM_STRING(dummy1, TYPE, FLAG) \
53  if (str == FARM_PP_STRINGIZE(FLAG)) { \
54  value = value | TYPE::FLAG; \
55  return true; \
56  }
57 
58 // Output:
59 // case ${TYPE}::$FLAG}: {
60 // static_assert(
61 // std::popcount(uint64_t(${TYPE}::${FLAG})) == 1,
62 // "${FLAG} must be a single bit flag");
63 // return true;
64 // }
65 #if (defined __linux__ || defined __APPLE__)
66 #define FARM_ENUMFLAG_DETAILS_OP_SINGLE_FLAG_CHECK(dummy1, TYPE, FLAG) \
67  case TYPE::FLAG: { \
68  static_assert( \
69  __builtin_popcount(uint64_t(TYPE::FLAG)) == 1, \
70  FARM_PP_STRINGIZE(FLAG) " must be a single bit flag"); \
71  return true; \
72  }
73 #else
74 #define FARM_ENUMFLAG_DETAILS_OP_SINGLE_FLAG_CHECK(dummy1, TYPE, FLAG) \
75  case TYPE::FLAG: { \
76  return true; \
77  }
78 #endif
79 
80 // Output:
81 // if (hasMask(value, ${TYPE}::${FLAG})) {
82 // strings.push_back("${FLAG}");
83 // }
84 #define FARM_ENUMFLAG_DETAILS_OP_PUSHBACK_STRING(dummy1, TYPE, FLAG) \
85  if (hasMask(value, TYPE::FLAG)) { \
86  strings.push_back(FARM_PP_STRINGIZE(FLAG)); \
87  }
88 
89 // Output:
90 // if (hasMask(value, ${TYPE}::${FLAG})) {
91 // prettySet += "${FLAG}, ";
92 // }
93 #define FARM_ENUMFLAG_DETAILS_OP_STRING_FOR_FLAG(dummy1, TYPE, FLAG) \
94  if (hasMask(value, TYPE::FLAG)) { \
95  prettySet += \
96  FARM_PP_STRINGIZE(FLAG) " (=" + std::to_string(uint64_t(TYPE::FLAG)) + \
97  "), "; \
98  }
99 
100 // Output: ", ${STRING}"
101 #define FARM_ENUMFLAGS_DETAILS_OP_COMMA_STRING(dummy1, dummy2, STRING) \
102  , FARM_PP_STRINGIZE(STRING)
103 
104 // Input: (A, B, C)
105 // Output: "A", "B", "C"
106 #define FARM_ENUMFLAGS_DETAILS_COMMA_SEP_STRINGS(...) \
107  FARM_PP_STRINGIZE(FARM_PP_TUPLE_ELEM(0, __VA_ARGS__)) \
108  FARM_PP_SEQ_FOR_EACH( \
109  FARM_ENUMFLAGS_DETAILS_OP_COMMA_STRING, \
110  _, \
111  FARM_PP_SEQ_POP_FRONT(FARM_PP_TUPLE_TO_SEQ(__VA_ARGS__)))
112 
113 // Output: ", ${STRING}"
114 #define FARM_ENUMFLAGS_DETAILS_OP_COMMA_VALUE_STRING(dummy1, dummy2, STRING) \
115  ", " FARM_PP_STRINGIZE(STRING)
116 
117 // Input: (A, B, C)
118 // Output: "A, B, C"
119 #define FARM_ENUMFLAGS_DETAILS_CSV_STRING(...) \
120  FARM_PP_STRINGIZE(FARM_PP_TUPLE_ELEM(0, __VA_ARGS__)) \
121  FARM_PP_SEQ_FOR_EACH( \
122  FARM_ENUMFLAGS_DETAILS_OP_COMMA_VALUE_STRING, \
123  _, \
124  FARM_PP_SEQ_POP_FRONT(FARM_PP_TUPLE_TO_SEQ(__VA_ARGS__)))
125 
126 // Output: , uint64_t(${TYPE}::${VAL})
127 #define FARM_ENUMFLAGS_DETAILS_OP_COMMA_INT(dummy1, TYPE, VAL) \
128  , uint64_t(TYPE::VAL)
129 
130 // Input: (A, B, C)
131 // Output: 1, 2, 4
132 //
133 // Note: Here, "1, 2, 4" are the values of "A, B, C".
134 #define FARM_ENUMFLAGS_DETAILS_COMMA_SEP_INTS(TYPE, ...) \
135  uint64_t(TYPE::FARM_PP_TUPLE_ELEM(0, __VA_ARGS__)) FARM_PP_SEQ_FOR_EACH( \
136  FARM_ENUMFLAGS_DETAILS_OP_COMMA_INT, \
137  TYPE, \
138  FARM_PP_SEQ_POP_FRONT(FARM_PP_TUPLE_TO_SEQ(__VA_ARGS__)))
139 
140 #define FARM_ENUMFLAGS_DEF_IMPL(NAME, UINT_TYPE, ...) \
141  namespace enum_wrapper_ { \
142  \
143  static_assert( \
144  std::is_unsigned<UINT_TYPE>::value, \
145  FARM_PP_STRINGIZE(UINT_TYPE) " must be an unsigned integer type"); \
146  \
147  enum class NAME##Impl : UINT_TYPE{ \
148  none = 0u, FARM_ENUMFLAGS_DETAILS_FLAG_DEFINITIONS(__VA_ARGS__)}; \
149  \
150  inline auto constexpr operator|(NAME##Impl left, NAME##Impl right) -> NAME \
151  ##Impl { \
152  return NAME##Impl(UINT_TYPE(left) | UINT_TYPE(right)); \
153  } \
154  \
155  inline auto constexpr operator&(NAME##Impl left, NAME##Impl right) -> NAME \
156  ##Impl { \
157  return NAME##Impl(UINT_TYPE(left) & UINT_TYPE(right)); \
158  } \
159  \
160  [[maybe_unused]] inline auto trySetFlagFromString( \
161  NAME##Impl &value, std::string const &str) -> bool { \
162  FARM_PP_SEQ_FOR_EACH( \
163  FARM_ENUMFLAG_DETAILS_OP_SET_ENUM_FROM_STRING, \
164  NAME##Impl, \
165  FARM_PP_TUPLE_TO_SEQ(__VA_ARGS__)) \
166  return false; \
167  } \
168  \
169  [[maybe_unused]] inline auto constexpr hasMask( \
170  NAME##Impl value, NAME##Impl mask) -> bool { \
171  return (value & mask) == mask; \
172  } \
173  \
174  [[maybe_unused]] inline auto setMask(NAME##Impl &value, NAME##Impl mask) \
175  -> void { \
176  value = value | mask; \
177  } \
178  \
179  [[maybe_unused]] inline auto clearMask(NAME##Impl &value, NAME##Impl mask) \
180  -> void { \
181  value = value & NAME##Impl(~UINT_TYPE(mask)); \
182  } \
183  \
184  [[maybe_unused]] inline auto toggleMask(NAME##Impl &value, NAME##Impl mask) \
185  -> void { \
186  value = NAME##Impl(UINT_TYPE(value) ^ UINT_TYPE(mask)); \
187  } \
188  \
189  [[maybe_unused]] inline auto constexpr isSingleFlag(NAME##Impl value) \
190  -> bool { \
191  switch (value) { \
192  FARM_PP_SEQ_FOR_EACH( \
193  FARM_ENUMFLAG_DETAILS_OP_SINGLE_FLAG_CHECK, \
194  NAME##Impl, \
195  FARM_PP_TUPLE_TO_SEQ(__VA_ARGS__)) \
196  case NAME##Impl::none: \
197  break; \
198  } \
199  return false; \
200  } \
201  \
202  [[maybe_unused]] inline auto toStrings(NAME##Impl value) \
203  -> std::vector<std::string> { \
204  std::vector<std::string> strings; \
205  FARM_PP_SEQ_FOR_EACH( \
206  FARM_ENUMFLAG_DETAILS_OP_PUSHBACK_STRING, \
207  NAME##Impl, \
208  FARM_PP_TUPLE_TO_SEQ(__VA_ARGS__)) \
209  return strings; \
210  } \
211  \
212  [[maybe_unused]] inline auto toPretty(NAME##Impl value) -> std::string { \
213  std::string prettySet("{"); \
214  FARM_PP_SEQ_FOR_EACH( \
215  FARM_ENUMFLAG_DETAILS_OP_STRING_FOR_FLAG, \
216  NAME##Impl, \
217  FARM_PP_TUPLE_TO_SEQ(__VA_ARGS__)) \
218  prettySet += "} (=" + std::to_string(UINT_TYPE(value)) + ")"; \
219  return prettySet; \
220  } \
221  \
222  [[maybe_unused]] [[nodiscard]] auto constexpr getCount(NAME##Impl) \
223  -> size_t { \
224  return FARM_PP_TUPLE_SIZE(__VA_ARGS__); \
225  } \
226  \
227  [[maybe_unused]] [[nodiscard]] inline auto getNames(NAME##Impl) \
228  -> std::array<std::string_view, FARM_PP_TUPLE_SIZE(__VA_ARGS__)> { \
229  return {FARM_ENUMFLAGS_DETAILS_COMMA_SEP_STRINGS(__VA_ARGS__)}; \
230  } \
231  \
232  [[maybe_unused]] [[nodiscard]] inline auto getStringOfNames(NAME##Impl) \
233  -> std::string_view { \
234  return FARM_ENUMFLAGS_DETAILS_CSV_STRING(__VA_ARGS__); \
235  } \
236  \
237  [[maybe_unused]] [[nodiscard]] auto constexpr getValues(NAME##Impl) \
238  -> std::array<UINT_TYPE, FARM_PP_TUPLE_SIZE(__VA_ARGS__)> { \
239  return {FARM_ENUMFLAGS_DETAILS_COMMA_SEP_INTS(NAME##Impl, __VA_ARGS__)}; \
240  } \
241  \
242  [[maybe_unused]] [[nodiscard]] inline auto constexpr getTypeName(NAME##Impl) \
243  -> std::string_view { \
244  return FARM_PP_STRINGIZE(NAME); \
245  } \
246  } // namespace enum_wrapper_