#pragma once #include /** * @brief The namespace for convenient C++ enum class logic operations. * @details * C++ enum class statement is a modern way to declare enum in C++. * But it lack essential logic operations which is commonly used by programmer. * So we create this helper to resolve this issue. */ namespace yycc::flag_enum { // Reference: // Enum operator overload: https://stackoverflow.com/a/71107019 // Constexpr operator overload: https://stackoverflow.com/a/17746099 // YYC MARK: // Currently, the solution of "Constexpr operator overload" is not used. // We use explicit way, "Enum operator overload". // template, int> = 0> // inline constexpr TEnum operator|(TEnum lhs, TEnum rhs) { // using ut = std::underlying_type_t; // return static_cast(static_cast(lhs) | static_cast(rhs)); // } // template, int> = 0> // inline constexpr TEnum operator|=(TEnum& lhs, TEnum rhs) { // using ut = std::underlying_type_t; // lhs = lhs | rhs; // return lhs; // } // template, int> = 0> // inline constexpr TEnum operator&(TEnum lhs, TEnum rhs) { // using ut = std::underlying_type_t; // return static_cast(static_cast(lhs) & static_cast(rhs)); // } // template, int> = 0> // inline constexpr TEnum operator&=(TEnum& lhs, TEnum rhs) { // using ut = std::underlying_type_t; // lhs = lhs & rhs; // return lhs; // } // template, int> = 0> // inline constexpr TEnum operator^(TEnum lhs, TEnum rhs) { // using ut = std::underlying_type_t; // return static_cast(static_cast(lhs) ^ static_cast(rhs)); // } // template, int> = 0> // inline constexpr TEnum operator^=(TEnum& lhs, TEnum rhs) { // using ut = std::underlying_type_t; // lhs = lhs ^ rhs; // return lhs; // } // template, int> = 0> // inline constexpr TEnum operator~(TEnum lhs) { // using ut = std::underlying_type_t; // return static_cast(~(static_cast(lhs))); // } // template, int> = 0> // inline constexpr bool operator bool(TEnum lhs) { // using ut = std::underlying_type_t; // return static_cast(static_cast(lhs)); // } /** * @private * @brief The helper struct to check all given template argument are the same enum type. * @tparam TEnum The template parameter to be checked (first one). * @tparam Ts The template parameter to be checked. */ template struct AllSameEnum { public: // YYC MARK: // Please note that we must use std::is_same, not std::is_same_v! // That's std::conjunction_v required. static constexpr bool value = std::is_enum_v> && std::conjunction_v, std::remove_cv_t>...>; }; /** * @private * @brief The convenient calling to all_enum_values::value to check enum template parameter. * @tparam TEnum The template parameter to be checked (first one). * @tparam Ts The template parameter to be checked. */ template inline constexpr bool ALL_SAME_ENUM = AllSameEnum::value; /** * @brief Merge given enum flags like performing e1 | e2 | ... | en * @tparam TEnum Enum type for processing. * @param[in] val The first enum flag to be merged. * @param[in] val_left Left enum flags to be merged. * @return The merged enum flag. * @remarks * This function use recursive expansion to get final merge result. * So there is no difference of each arguments. * We independ first argument just served for expansion. */ template requires(ALL_SAME_ENUM) constexpr TEnum merge(TEnum val, Ts... val_left) { using ut = std::underlying_type_t; ut result = static_cast(val); if constexpr (sizeof...(val_left) > 0) { result |= static_cast(merge(val_left...)); } return static_cast(result); } /** * @brief Reverse given enum flags like performing ~(e) * @tparam TEnum Enum type for processing. * @param[in] e The list of enum flags to be inversed. * @return The inversed enum flag. */ template requires(std::is_enum_v) constexpr TEnum invert(TEnum e) { using ut = std::underlying_type_t; return static_cast(~(static_cast(e))); } /** * @brief Use specified enum flag to mask given enum flag like performing e1 &= e2 * @tparam TEnum Enum type for processing. * @param[in,out] e1 The enum flags to be masked. * @param[in] e2 The mask enum flag. */ template requires(std::is_enum_v) constexpr void mask(TEnum& e1, TEnum e2) { using ut = std::underlying_type_t; e1 = static_cast(static_cast(e1) & static_cast(e2)); } /** * @brief Add multiple enum flags to given enum flag like performing e1 |= (e2 | e3 | ... | en) * @tparam TEnum Enum type for processing. * @param[in,out] e1 The enum flag which flags add on. * @param[in] vals The enum flag to be added. */ template requires(ALL_SAME_ENUM) constexpr void add(TEnum& e1, Ts... vals) { using ut = std::underlying_type_t; e1 = static_cast(static_cast(e1) | static_cast(merge(vals...))); } /** * @brief Remove multiple enum flags from given enum flag like performing e1 &= ~(e2 | e3 | ... | en) * @tparam TEnum Enum type for processing. * @param[in,out] e1 The enum flag which flags removed from. * @param[in] vals The enum flag to be removed. */ template requires(ALL_SAME_ENUM) constexpr void remove(TEnum& e1, Ts... vals) { using ut = std::underlying_type_t; e1 = static_cast(static_cast(e1) & static_cast(invert(merge(vals...)))); } /** * @brief Check whether given enum flag has any of specified multiple enum flags (OR) like performing bool(e1 & (e2 | e3 | ... | en)) * @tparam TEnum Enum type for processing. * @param[in] e1 The enum flag where we check. * @param[in] vals The enum flags for checking. * @return True if it has any of given flags (OR), otherwise false. */ template requires(ALL_SAME_ENUM) constexpr bool has(TEnum e1, Ts... vals) { using ut = std::underlying_type_t; return static_cast(static_cast(e1) & static_cast(merge(vals...))); } /** * @brief Cast given enum flags to its equvalent boolean value like performing bool(e) * @tparam TEnum Enum type for processing. * @param e The enum flags to be cast. * @return The equvalent bool value of given enum flag. */ template requires(ALL_SAME_ENUM) constexpr bool boolean(TEnum e) { using ut = std::underlying_type_t; return static_cast(static_cast(e)); } /** * @brief Cast given enum flags to its equvalent underlying integer value like performing static_cast>(e) * @tparam TEnum Enum type for processing. * @param e The enum flags to be cast. * @return The equvalent integer value of given enum flag. */ template requires(std::is_enum_v) constexpr std::underlying_type_t integer(TEnum e) { using ut = std::underlying_type_t; return static_cast(e); } } // namespace yycc::flag_enum