[kutil] Make enum bitfields usable in other scopes

Changing the SFINAE/enable_if strategy from a type to a constexpr
function means that it can be defined in other scopes than the functions
themselves, because of function overloading. This lets us put everything
into the kutil::bitfields namespace, and make bitfields out of enums in
other namespaces. Also took the chance to clean up the implementation a
bit.
This commit is contained in:
Justin C. Miller
2021-02-19 20:42:49 -08:00
parent cf22ed57a2
commit f9a967caf7
10 changed files with 101 additions and 147 deletions

View File

@@ -2,16 +2,16 @@
#include <type_traits>
template<typename E>
struct is_enum_bitfield { static constexpr bool value = false; };
namespace kutil {
namespace bitfields {
#define IS_BITFIELD(name) \
template<> struct ::is_enum_bitfield<name> {static constexpr bool value=true;}
template <typename E>
constexpr bool is_enum_bitfield(E) { return false; }
template <typename E>
struct enum_or_int {
static constexpr bool value =
std::disjunction< is_enum_bitfield<E>, std::is_integral<E> >::value;
is_enum_bitfield(E{}) || std::is_integral<E>::value;
};
template <typename E, typename F>
@@ -34,116 +34,62 @@ template <> struct integral<unsigned long> { using type = unsigned long; };
template <> struct integral<long long> { using type = long long; };
template <> struct integral<unsigned long long> { using type = unsigned long long; };
template <typename E, typename F>
constexpr typename std::enable_if<both_enum_or_int<E, F>::value,E>::type
operator & (E lhs, F rhs)
{
return static_cast<E> (
static_cast<typename integral<E>::type>(lhs) &
static_cast<typename integral<F>::type>(rhs));
}
template <typename E, typename F>
constexpr typename std::enable_if<both_enum_or_int<E, F>::value,E>::type
operator | (E lhs, F rhs)
{
return static_cast<E> (
static_cast<typename integral<E>::type>(lhs) |
static_cast<typename integral<F>::type>(rhs));
}
template <typename E, typename F>
constexpr typename std::enable_if<both_enum_or_int<E, F>::value,E>::type
operator ^ (E lhs, F rhs)
{
return static_cast<E> (
static_cast<typename integral<E>::type>(lhs) ^
static_cast<typename integral<F>::type>(rhs));
}
template <typename E>
constexpr typename std::enable_if<is_enum_bitfield<E>::value,E>::type
operator ~ (E rhs)
{
return static_cast<E>(~static_cast<typename std::underlying_type<E>::type>(rhs));
}
template <typename E, typename F>
constexpr typename std::enable_if<both_enum_or_int<E, F>::value,E>::type&
operator |= (E &lhs, F rhs)
{
lhs = static_cast<E>(
return lhs = static_cast<E>(
static_cast<typename integral<E>::type>(lhs) |
static_cast<typename integral<F>::type>(rhs));
return lhs;
}
template <typename E, typename F>
constexpr typename std::enable_if<both_enum_or_int<E, F>::value,E>::type&
operator &= (E &lhs, F rhs)
{
lhs = static_cast<E>(
return lhs = static_cast<E>(
static_cast<typename integral<E>::type>(lhs) &
static_cast<typename integral<F>::type>(rhs));
return lhs;
}
template <typename E, typename F>
constexpr typename std::enable_if<both_enum_or_int<E, F>::value,E>::type&
operator ^= (E &lhs, F rhs)
{
lhs = static_cast<E>(
return lhs = static_cast<E>(
static_cast<typename integral<E>::type>(lhs) ^
static_cast<typename integral<F>::type>(rhs));
return lhs;
}
template <typename E, typename F>
constexpr typename std::enable_if<both_enum_or_int<E, F>::value,E>::type&
operator -= (E &lhs, F rhs)
{
lhs = static_cast<E>(
static_cast<typename integral<E>::type>(lhs) &
~static_cast<typename integral<F>::type>(rhs));
return lhs;
}
constexpr typename std::enable_if<both_enum_or_int<E, F>::value,E>::type
operator & (E lhs, F rhs) { return lhs &= rhs; }
template <typename E, typename F>
constexpr typename std::enable_if<both_enum_or_int<E, F>::value,E>::type&
operator += (E &lhs, F rhs)
{
lhs = static_cast<E>(
static_cast<typename integral<E>::type>(lhs) |
static_cast<typename integral<F>::type>(rhs));
constexpr typename std::enable_if<both_enum_or_int<E, F>::value,E>::type
operator | (E lhs, F rhs) { return lhs |= rhs; }
return lhs;
}
template <typename E, typename F>
constexpr typename std::enable_if<both_enum_or_int<E, F>::value,E>::type
operator ^ (E lhs, F rhs) { return lhs ^= rhs; }
template <typename E>
constexpr typename std::enable_if<is_enum_bitfield<E>::value,bool>::type
operator ! (E rhs)
{
return static_cast<typename std::underlying_type<E>::type>(rhs) == 0;
}
constexpr typename std::enable_if<is_enum_bitfield(E{}),E>::type
operator ~ (E rhs) { return static_cast<E>(~static_cast<typename std::underlying_type<E>::type>(rhs)); }
template <typename E>
constexpr bool
bitfield_has(E set, E flag)
{
return
(static_cast<typename std::underlying_type<E>::type>(set) &
static_cast<typename std::underlying_type<E>::type>(flag)) ==
static_cast<typename std::underlying_type<E>::type>(flag);
}
constexpr typename std::enable_if<is_enum_bitfield(E{}),bool>::type
operator ! (E rhs) { return static_cast<typename std::underlying_type<E>::type>(rhs) == 0; }
// Overload the logical-and operator to be 'bitwise-and, bool-cast'
/// Override logical-and to mean 'rhs contains all bits in lhs'
template <typename E>
constexpr typename std::enable_if<is_enum_bitfield<E>::value,bool>::type
operator && (E set, E flag)
{
return (set & flag) == flag;
}
constexpr typename std::enable_if<is_enum_bitfield(E{}),bool>::type
operator && (E rhs, E lhs) { return (rhs & lhs) == lhs; }
} // namespace bitfields
} // namespace kutil
#define is_bitfield(name) \
constexpr bool is_enum_bitfield(name) { return true; } \
using namespace kutil::bitfields;