impl/EnumFlagsDetails.h (165 lines of code) (raw):
// Copyright (c) Facebook, Inc. and its affiliates.
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
#pragma once
#include <array>
#include <string>
#include <type_traits>
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/seq/pop_front.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/tuple/elem.hpp>
#include <boost/preprocessor/tuple/size.hpp>
#include <boost/preprocessor/tuple/to_seq.hpp>
#include "CompilerAttributes.h"
#include "StringView.h"
// Output: , ${FLAG} = (1 << ($I + 1))
#define MY_ENUMFLAGS_DETAILS_OP_INIT_FLAG(dummy1, dummy2, I, FLAG) \
, FLAG = (1 << (I + 1))
// Input: (A, B, C, D)
// Output: A=1, B=(1<<1), C=(1<<2), D=(1<<3)
#define MY_ENUMFLAGS_DETAILS_FLAG_DEFINITIONS(...) \
BOOST_PP_TUPLE_ELEM(0, __VA_ARGS__) = 1 BOOST_PP_SEQ_FOR_EACH_I( \
MY_ENUMFLAGS_DETAILS_OP_INIT_FLAG, _, \
BOOST_PP_SEQ_POP_FRONT(BOOST_PP_TUPLE_TO_SEQ(__VA_ARGS__)))
// Output:
// if (str == "${FLAGS}") {
// value = ${TYPE}:${FLAG};
// return true;
// }
#define MY_ENUMFLAG_DETAILS_OP_SET_ENUM_FROM_STRING(dummy1, TYPE, FLAG) \
if (str == BOOST_PP_STRINGIZE(FLAG)) { \
value = value | TYPE::FLAG; \
return true; \
}
// Output:
// case ${TYPE}::$FLAG}: {
// static_assert(
// std::popcount(uint64_t(${TYPE}::${FLAG})) == 1,
// "${FLAG} must be a single bit flag");
// return true;
// }
#if (defined __linux__ || defined __APPLE__)
#define MY_ENUMFLAG_DETAILS_OP_SINGLE_FLAG_CHECK(dummy1, TYPE, FLAG) \
case TYPE::FLAG: { \
static_assert(__builtin_popcount(uint64_t(TYPE::FLAG)) == 1, \
BOOST_PP_STRINGIZE(FLAG) " must be a single bit flag"); \
return true; \
}
#else
#define MY_ENUMFLAG_DETAILS_OP_SINGLE_FLAG_CHECK(dummy1, TYPE, FLAG) \
case TYPE::FLAG: { \
return true; \
}
#endif
// Output:
// if (hasMask(value, ${TYPE}::${FLAG})) {
// strings.push_back("${FLAG}");
// }
#define MY_ENUMFLAG_DETAILS_OP_PUSHBACK_STRING(dummy1, TYPE, FLAG) \
if (hasMask(value, TYPE::FLAG)) { \
strings.push_back(BOOST_PP_STRINGIZE(FLAG)); \
}
// Output:
// if (hasMask(value, ${TYPE}::${FLAG})) {
// prettySet += "${FLAG}, ";
// }
#define MY_ENUMFLAG_DETAILS_OP_STRING_FOR_FLAG(dummy1, TYPE, FLAG) \
if (hasMask(value, TYPE::FLAG)) { \
prettySet += BOOST_PP_STRINGIZE(FLAG) " (=" + std::to_string(uint64_t(TYPE::FLAG)) + "), "; \
}
// Output: ", ${STRING}"
#define MY_ENUMFLAGS_DETAILS_OP_COMMA_STRING(dummy1, dummy2, STRING) \
, BOOST_PP_STRINGIZE(STRING)
// Input: (A, B, C)
// Output: "A", "B", "C"
#define MY_ENUMFLAGS_DETAILS_COMMA_SEP_STRINGS(...) \
BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(0, __VA_ARGS__)) \
BOOST_PP_SEQ_FOR_EACH( \
MY_ENUMFLAGS_DETAILS_OP_COMMA_STRING, _, \
BOOST_PP_SEQ_POP_FRONT(BOOST_PP_TUPLE_TO_SEQ(__VA_ARGS__)))
// Output: ", ${STRING}"
#define MY_ENUMFLAGS_DETAILS_OP_COMMA_VALUE_STRING(dummy1, dummy2, STRING) \
", " BOOST_PP_STRINGIZE(STRING)
// Input: (A, B, C)
// Output: "A, B, C"
#define MY_ENUMFLAGS_DETAILS_CSV_STRING(...) \
BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(0, __VA_ARGS__)) \
BOOST_PP_SEQ_FOR_EACH( \
MY_ENUMFLAGS_DETAILS_OP_COMMA_VALUE_STRING, _, \
BOOST_PP_SEQ_POP_FRONT(BOOST_PP_TUPLE_TO_SEQ(__VA_ARGS__)))
// Output: , uint64_t(${TYPE}::${VAL})
#define MY_ENUMFLAGS_DETAILS_OP_COMMA_INT(dummy1, TYPE, VAL) \
, uint64_t(TYPE::VAL)
// Input: (A, B, C)
// Output: 1, 2, 4
//
// Note: Here, "1, 2, 4" are the values of "A, B, C".
#define MY_ENUMFLAGS_DETAILS_COMMA_SEP_INTS(TYPE, ...) \
uint64_t(TYPE::BOOST_PP_TUPLE_ELEM(0, __VA_ARGS__)) BOOST_PP_SEQ_FOR_EACH( \
MY_ENUMFLAGS_DETAILS_OP_COMMA_INT, TYPE, \
BOOST_PP_SEQ_POP_FRONT(BOOST_PP_TUPLE_TO_SEQ(__VA_ARGS__)))
#define MY_ENUMFLAGS_DEF_IMPL(NAME, UINT_TYPE, ...) \
namespace enum_wrapper_ { \
\
static_assert( \
std::is_unsigned<UINT_TYPE>::value, \
BOOST_PP_STRINGIZE(UINT_TYPE) " must be an unsigned integer type"); \
\
enum class NAME##Impl : UINT_TYPE{ \
none = 0u, MY_ENUMFLAGS_DETAILS_FLAG_DEFINITIONS(__VA_ARGS__)}; \
\
NAME##Impl inline operator|(NAME##Impl left, NAME##Impl right) { \
return NAME##Impl(UINT_TYPE(left) | UINT_TYPE(right)); \
} \
\
NAME##Impl inline operator&(NAME##Impl left, NAME##Impl right) { \
return NAME##Impl(UINT_TYPE(left) & UINT_TYPE(right)); \
} \
\
MY_ENUM_MAYBE_UNUSED inline bool trySetFlagFromString( \
NAME##Impl &value, const std::string &str) { \
BOOST_PP_SEQ_FOR_EACH(MY_ENUMFLAG_DETAILS_OP_SET_ENUM_FROM_STRING, \
NAME##Impl, BOOST_PP_TUPLE_TO_SEQ(__VA_ARGS__)) \
return false; \
} \
\
MY_ENUM_MAYBE_UNUSED inline bool hasMask(NAME##Impl value, \
NAME##Impl mask) { \
return (value & mask) == mask; \
} \
\
MY_ENUM_MAYBE_UNUSED inline void setMask(NAME##Impl &value, \
NAME##Impl mask) { \
value = value | mask; \
} \
\
MY_ENUM_MAYBE_UNUSED inline void clearMask(NAME##Impl &value, \
NAME##Impl mask) { \
value = value & NAME##Impl(~UINT_TYPE(mask)); \
} \
\
MY_ENUM_MAYBE_UNUSED inline void toggleMask(NAME##Impl &value, \
NAME##Impl mask) { \
value = NAME##Impl(UINT_TYPE(value) ^ UINT_TYPE(mask)); \
} \
\
MY_ENUM_MAYBE_UNUSED inline bool isSingleFlag(NAME##Impl value) { \
switch (value) { \
BOOST_PP_SEQ_FOR_EACH(MY_ENUMFLAG_DETAILS_OP_SINGLE_FLAG_CHECK, \
NAME##Impl, BOOST_PP_TUPLE_TO_SEQ(__VA_ARGS__)) \
case NAME##Impl::none: \
break; \
} \
return false; \
} \
\
MY_ENUM_MAYBE_UNUSED inline std::vector<std::string> toStrings( \
NAME##Impl value) { \
std::vector<std::string> strings; \
BOOST_PP_SEQ_FOR_EACH(MY_ENUMFLAG_DETAILS_OP_PUSHBACK_STRING, NAME##Impl, \
BOOST_PP_TUPLE_TO_SEQ(__VA_ARGS__)) \
return strings; \
} \
\
MY_ENUM_MAYBE_UNUSED inline std::string toPretty(NAME##Impl value) { \
std::string prettySet("{"); \
BOOST_PP_SEQ_FOR_EACH(MY_ENUMFLAG_DETAILS_OP_STRING_FOR_FLAG, NAME##Impl, \
BOOST_PP_TUPLE_TO_SEQ(__VA_ARGS__)) \
prettySet += "} (=" + std::to_string(UINT_TYPE(value)) + ")"; \
return prettySet; \
} \
\
MY_ENUM_MAYBE_UNUSED MY_ENUM_NODISCARD constexpr size_t getCount( \
NAME##Impl) { \
return BOOST_PP_TUPLE_SIZE(__VA_ARGS__); \
} \
\
MY_ENUM_MAYBE_UNUSED \
MY_ENUM_NODISCARD inline std::array<MY_ENUM_STRING_VIEW, \
BOOST_PP_TUPLE_SIZE(__VA_ARGS__)> \
getNames(NAME##Impl) { \
return {MY_ENUMFLAGS_DETAILS_COMMA_SEP_STRINGS(__VA_ARGS__)}; \
} \
\
MY_ENUM_MAYBE_UNUSED MY_ENUM_NODISCARD inline MY_ENUM_STRING_VIEW \
getStringOfNames(NAME##Impl) { \
return MY_ENUMFLAGS_DETAILS_CSV_STRING(__VA_ARGS__); \
} \
\
MY_ENUM_MAYBE_UNUSED \
MY_ENUM_NODISCARD constexpr std::array<UINT_TYPE, \
BOOST_PP_TUPLE_SIZE(__VA_ARGS__)> \
getValues(NAME##Impl) { \
return {MY_ENUMFLAGS_DETAILS_COMMA_SEP_INTS(NAME##Impl, __VA_ARGS__)}; \
} \
\
MY_ENUM_MAYBE_UNUSED MY_ENUM_NODISCARD inline MY_ENUM_STRING_VIEW \
getTypeName(NAME##Impl) { \
return BOOST_PP_STRINGIZE(NAME); \
} \
} // namespace enum_wrapper_