// SPDX-License-Identifier: LGPL-2.1-or-later /*************************************************************************** * Copyright (c) 2022 Werner Mayer * * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 51 Franklin Street, * * Fifth Floor, Boston, MA 02110-1301, USA * * * ***************************************************************************/ #ifndef BASE_BITMASK_H #define BASE_BITMASK_H #include /*! Using enum classes as type-safe bitmasks. @code enum class Color { Red = 1 << 0, Green = 1 << 1, Blue = 1 << 2 }; ENABLE_BITMASK_OPERATORS(Color) Color yellow = Color::Red | Color::Green; Flags flags(yellow); flags.testFlag(Color::Red); flags.testFlag(Color::Green); @endcode */ // NOLINTBEGIN // clang-format off // Based on https://stackoverflow.com/questions/1448396/how-to-use-enums-as-flags-in-c template struct enum_traits {}; template<> struct enum_traits { struct _allow_bitops { static constexpr bool allow_bitops = true; }; using allow_bitops = _allow_bitops; template using t = typename std::enable_if::value && enum_traits::allow_bitops, R>::type; template using u = typename std::underlying_type::type; }; template constexpr enum_traits<>::t operator~(T a) { return static_cast(~static_cast::u>(a)); } template constexpr enum_traits<>::t operator|(T a, T b) { return static_cast( static_cast::u>(a) | static_cast::u>(b)); } template constexpr enum_traits<>::t operator&(T a, T b) { return static_cast( static_cast::u>(a) & static_cast::u>(b)); } template constexpr enum_traits<>::t operator^(T a, T b) { return static_cast( static_cast::u>(a) ^ static_cast::u>(b)); } template constexpr enum_traits<>::t operator|=(T& a, T b) { a = a | b; return a; } template constexpr enum_traits<>::t operator&=(T& a, T b) { a = a & b; return a; } template constexpr enum_traits<>::t operator^=(T& a, T b) { a = a ^ b; return a; } #define ENABLE_BITMASK_OPERATORS(x) \ template<> \ struct enum_traits : \ enum_traits<>::allow_bitops {}; namespace Base { template class Flags { static_assert(std::is_enum::value, "Flags is only usable on enumeration types."); Enum i; public: // Linter seems wrong on next line, don't want explicit here forcing downstream changes constexpr inline Flags(Enum f = Enum()) : i(f) {} // NOLINT (runtime/explicit) constexpr bool testFlag(Enum f) const { using u = typename std::underlying_type::type; return (i & f) == f && (static_cast(f) != 0 || i == f); } constexpr inline void setFlag(Enum f, bool on = true) { on ? (i |= f) : (i &= ~f); } constexpr bool isEqual(Flags f) const { using u = typename std::underlying_type::type; return static_cast(i) == static_cast(f.i); } constexpr Enum getFlags() const { return i; } constexpr Flags &operator|=(const Flags &other) { i |= other.i; return *this; } constexpr Flags &operator|=(const Enum &f) { i |= f; return *this; } constexpr Flags operator|(const Flags &other) const { return i | other.i; } constexpr Flags operator|(const Enum &f) const { return i | f; } constexpr Flags &operator&=(const Flags &other) { i &= other.i; return *this; } constexpr Flags &operator&=(const Enum &f) { i &= f; return *this; } constexpr Flags operator&(const Flags &other) const { return i & other.i; } constexpr Flags operator&(const Enum &f) const { return i & f; } constexpr Flags operator~() const { return ~i; } constexpr bool operator!() const { return !i; } explicit operator bool() const { return toUnderlyingType() != 0; } typename std::underlying_type::type toUnderlyingType() const { return static_cast::type>(i); } }; } // clang-format on // NOLINTEND #endif