Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
166 changes: 166 additions & 0 deletions include/rsl/enum
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
#pragma once
#include <meta>
#include <ranges>
#include <type_traits>
#include <rsl/meta_traits>

namespace rsl {
inline namespace annotations {
struct FlagEnumTag {};
constexpr inline FlagEnumTag flag_enum;

} // namespace annotations
template <typename T>
concept is_flag_enum =
std::is_scoped_enum_v<T> && meta::has_annotation(^^T, ^^annotations::FlagEnumTag);

template <is_flag_enum E>
constexpr bool has_flag(E flags, E needle) {
using U = std::underlying_type_t<E>;
return (static_cast<U>(flags) & static_cast<U>(needle)) == static_cast<U>(needle);
}

template <is_flag_enum E>
constexpr bool has_flag(E flags, std::underlying_type_t<E> needle) {
using U = std::underlying_type_t<E>;
return (static_cast<U>(flags) & needle) == needle;
}

template <typename T>
requires std::is_enum_v<T>
constexpr bool is_named_enumerator(T value) {
constexpr static auto named_enumerators =
define_static_array(enumerators_of(^^T) | std::views::transform([](std::meta::info r) {
return extract<T>(constant_of(r));
}));
return std::ranges::contains(named_enumerators, value);
}
} // namespace rsl

template <rsl::is_flag_enum E>
constexpr E operator~(E v) noexcept {
using U = std::underlying_type_t<E>;
return static_cast<E>(~static_cast<U>(v));
}

template <rsl::is_flag_enum E, std::convertible_to<std::underlying_type_t<E>> T>
constexpr bool operator==(E lhs, T rhs) noexcept {
using U = std::underlying_type_t<E>;
return static_cast<U>(lhs) == static_cast<U>(rhs);
}

template <rsl::is_flag_enum E, std::convertible_to<std::underlying_type_t<E>> T>
constexpr bool operator==(T lhs, E rhs) noexcept {
using U = std::underlying_type_t<E>;
return static_cast<U>(lhs) == static_cast<U>(rhs);
}

template <rsl::is_flag_enum E, std::convertible_to<std::underlying_type_t<E>> T>
constexpr bool operator!=(E lhs, T rhs) noexcept {
using U = std::underlying_type_t<E>;
return static_cast<U>(lhs) != static_cast<U>(rhs);
}

template <rsl::is_flag_enum E, std::convertible_to<std::underlying_type_t<E>> T>
constexpr bool operator!=(T lhs, E rhs) noexcept {
using U = std::underlying_type_t<E>;
return static_cast<U>(lhs) != static_cast<U>(rhs);
}

template <rsl::is_flag_enum E>
constexpr E operator|(E lhs, E rhs) noexcept {
using U = std::underlying_type_t<E>;
return static_cast<E>(static_cast<U>(lhs) | static_cast<U>(rhs));
}

template <rsl::is_flag_enum E>
constexpr E operator&(E lhs, E rhs) noexcept {
using U = std::underlying_type_t<E>;
return static_cast<E>(static_cast<U>(lhs) & static_cast<U>(rhs));
}

template <rsl::is_flag_enum E>
constexpr E operator^(E lhs, E rhs) noexcept {
using U = std::underlying_type_t<E>;
return static_cast<E>(static_cast<U>(lhs) ^ static_cast<U>(rhs));
}
template <rsl::is_flag_enum E, std::convertible_to<std::underlying_type_t<E>> T>
constexpr E operator|(E lhs, T rhs) noexcept {
using U = std::underlying_type_t<E>;
return static_cast<E>(static_cast<U>(lhs) | static_cast<U>(rhs));
}

template <rsl::is_flag_enum E, std::convertible_to<std::underlying_type_t<E>> T>
constexpr E operator&(E lhs, T rhs) noexcept {
using U = std::underlying_type_t<E>;
return static_cast<E>(static_cast<U>(lhs) & static_cast<U>(rhs));
}

template <rsl::is_flag_enum E, std::convertible_to<std::underlying_type_t<E>> T>
constexpr E operator^(E lhs, T rhs) noexcept {
using U = std::underlying_type_t<E>;
return static_cast<E>(static_cast<U>(lhs) ^ static_cast<U>(rhs));
}

template <rsl::is_flag_enum E, std::convertible_to<std::underlying_type_t<E>> T>
constexpr E operator|(T lhs, E rhs) noexcept {
using U = std::underlying_type_t<E>;
return static_cast<E>(static_cast<U>(lhs) | static_cast<U>(rhs));
}

template <rsl::is_flag_enum E, std::convertible_to<std::underlying_type_t<E>> T>
constexpr E operator&(T lhs, E rhs) noexcept {
using U = std::underlying_type_t<E>;
return static_cast<E>(static_cast<U>(lhs) & static_cast<U>(rhs));
}

template <rsl::is_flag_enum E, std::convertible_to<std::underlying_type_t<E>> T>
constexpr E operator^(T lhs, E rhs) noexcept {
using U = std::underlying_type_t<E>;
return static_cast<E>(static_cast<U>(lhs) ^ static_cast<U>(rhs));
}

template <rsl::is_flag_enum E>
constexpr E& operator|=(E& lhs, E rhs) noexcept {
return lhs = lhs | rhs;
}

template <rsl::is_flag_enum E>
constexpr E& operator&=(E& lhs, E rhs) noexcept {
return lhs = lhs & rhs;
}

template <rsl::is_flag_enum E>
constexpr E& operator^=(E& lhs, E rhs) noexcept {
return lhs = lhs ^ rhs;
}

template <rsl::is_flag_enum E, std::convertible_to<std::underlying_type_t<E>> T>
constexpr E& operator|=(E& lhs, T rhs) noexcept {
return lhs = lhs | rhs;
}

template <rsl::is_flag_enum E, std::convertible_to<std::underlying_type_t<E>> T>
constexpr E& operator&=(E& lhs, T rhs) noexcept {
return lhs = lhs & rhs;
}

template <rsl::is_flag_enum E, std::convertible_to<std::underlying_type_t<E>> T>
constexpr E& operator^=(E& lhs, T rhs) noexcept {
return lhs = lhs ^ rhs;
}

template <rsl::is_flag_enum E, std::convertible_to<std::underlying_type_t<E>> T>
constexpr T& operator|=(T& lhs, E rhs) noexcept {
return lhs = static_cast<uint8_t>(lhs | rhs);
}

template <rsl::is_flag_enum E, std::convertible_to<std::underlying_type_t<E>> T>
constexpr T& operator&=(T& lhs, E rhs) noexcept {
return lhs = static_cast<uint8_t>(lhs & rhs);
}

template <rsl::is_flag_enum E, std::convertible_to<std::underlying_type_t<E>> T>
constexpr T& operator^=(T& lhs, E rhs) noexcept {
return lhs = static_cast<uint8_t>(lhs ^ rhs);
}
3 changes: 2 additions & 1 deletion test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ add_subdirectory(serializer)
add_subdirectory(string_view)
add_subdirectory(span)
add_subdirectory(format)
add_subdirectory(kwargs)
add_subdirectory(kwargs)
add_subdirectory(enum)
4 changes: 4 additions & 0 deletions test/enum/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
target_sources(rsl-util-test PRIVATE
operator.cpp
utilities.cpp
)
92 changes: 92 additions & 0 deletions test/enum/operator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#include <rsl/enum>
#include <gtest/gtest.h>

namespace {
enum class[[= rsl::flag_enum]] Permissions : uint8_t {
None = 0U,
Read = 1U << 0U,
Write = 1U << 1U,
Execute = 1U << 2U,
All = Read | Write | Execute
};
}

TEST(FlagEnum, HasFlag) {
ASSERT_TRUE(rsl::has_flag(Permissions::All, Permissions::Read));
ASSERT_TRUE(rsl::has_flag(Permissions::All, Permissions::All));

ASSERT_TRUE(rsl::has_flag(Permissions::All, 1));
ASSERT_TRUE(rsl::has_flag(Permissions::All, 0b11));
}

TEST(FlagEnum, Operators) {
// operator |
ASSERT_EQ(static_cast<uint8_t>(Permissions::Read | Permissions::Write), 0b11);
ASSERT_EQ(static_cast<uint8_t>(1 | Permissions::Write), 0b11);
ASSERT_EQ(static_cast<uint8_t>(Permissions::Write | 1), 0b11);

// operator &
ASSERT_TRUE(static_cast<uint8_t>(Permissions::All & Permissions::Read) != 0);
ASSERT_TRUE(static_cast<uint8_t>(Permissions::All & 1) != 0);
ASSERT_TRUE(static_cast<uint8_t>(0b111 & Permissions::Read) != 0);

// operator ^
ASSERT_EQ(static_cast<uint8_t>(Permissions::All ^ Permissions::Read), 0b110);
ASSERT_EQ(static_cast<uint8_t>(Permissions::All ^ 1), 0b110);
ASSERT_EQ(static_cast<uint8_t>(0b111 ^ Permissions::Read), 0b110);

// operator ==
ASSERT_TRUE(Permissions::Read == static_cast<uint8_t>(1));
ASSERT_TRUE(static_cast<uint8_t>(1) == Permissions::Read);

// operator !=
ASSERT_TRUE(Permissions::Read != static_cast<uint8_t>(2));
ASSERT_TRUE(static_cast<uint8_t>(2) != Permissions::Read);
}

TEST(FlagEnum, CompoundOperator) {
// operator|=
{
Permissions p1 = Permissions::None;
Permissions p2 = Permissions::None;
uint8_t p3 = 0;

p1 |= Permissions::Read;
p2 |= 1;
p3 |= Permissions::Read;

ASSERT_EQ(p1, Permissions::Read);
ASSERT_EQ(p2, Permissions::Read);
ASSERT_EQ(p3, 1);
}

// operator &=
{
Permissions p1 = Permissions::All;
Permissions p2 = Permissions::All;
uint8_t p3 = 0b111;

p1 &= Permissions::Write;
p2 &= 1U << 1U;
p3 &= Permissions::Write;

ASSERT_EQ(static_cast<uint8_t>(p1), 0b10);
ASSERT_EQ(static_cast<uint8_t>(p2), 0b10);
ASSERT_EQ(static_cast<uint8_t>(p3), 0b10);
}

// operator ^=
{
Permissions p1 = Permissions::All;
Permissions p2 = Permissions::All;
uint8_t p3 = 0b111;

p1 ^= Permissions::Execute;
p2 ^= 1U << 2U;
p3 ^= Permissions::Execute;

ASSERT_EQ(static_cast<uint8_t>(p1), 0b11);
ASSERT_EQ(static_cast<uint8_t>(p2), 0b11);
ASSERT_EQ(static_cast<uint8_t>(p3), 0b11);
}
}
15 changes: 15 additions & 0 deletions test/enum/utilities.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#include <rsl/enum>
#include <gtest/gtest.h>

namespace {
enum class Foo : uint8_t {
foo,
bar = 10
};
}

TEST(Enum, IsNamed) {
ASSERT_TRUE(rsl::is_named_enumerator(Foo::bar));
ASSERT_TRUE(rsl::is_named_enumerator(Foo::foo));
ASSERT_FALSE(rsl::is_named_enumerator(Foo(20)));
}