Skip to content

Commit 72e501f

Browse files
committed
Add FlagsOf implementation
1 parent a35a912 commit 72e501f

15 files changed

Lines changed: 307 additions & 0 deletions

co-cpp19.qbs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Project {
1111
"src/array19.lib",
1212
"src/coro19.lib",
1313
"src/enum19.lib",
14+
"src/flags19.lib",
1415
"src/meta19.lib",
1516
"src/lookup19.lib",
1617
"src/optional19.lib",

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
add_subdirectory(array19.lib)
22
add_subdirectory(coro19.lib)
33
add_subdirectory(enum19.lib)
4+
add_subdirectory(flags19.lib)
45
add_subdirectory(lookup19.lib)
56
add_subdirectory(meta19.lib)
67
add_subdirectory(optional19.lib)

src/flags19.lib/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
include(flags19/flags19.cmake)
3+
include(flags19/flags19.tests.cmake)

src/flags19.lib/flags19.lib.qbs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
2+
Project {
3+
name: "flags19.lib"
4+
5+
references: [
6+
"flags19/flags19.qbs",
7+
"flags19/flags19.tests.qbs",
8+
]
9+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#pragma once
2+
#include "FlagsOf.h"
3+
#include "enum19/Enum.names.h"
4+
5+
/// noto: for this header you need fmt library (not included as library dependency)
6+
#include <fmt/format.h>
7+
#include <string_view>
8+
9+
/// adds enum support for fmt
10+
template<enum19::HasMetaEnum Enum, class Char> struct fmt::formatter<flags19::FlagsOf<Enum>, Char> {
11+
using T = flags19::FlagsOf<Enum>;
12+
using Value = typename T::Value;
13+
constexpr auto parse(fmt::basic_format_parse_context<Char>& ctx) { return ctx.begin(); }
14+
15+
template<typename FormatCtx> auto format(const T& v, FormatCtx& ctx) const {
16+
// auto underlying = static_cast<std::underlying_type_t<T>>(v);
17+
using namespace std::string_view_literals;
18+
auto printed = false;
19+
for (auto& member : enum19::meta_enum_for<Enum>.members) {
20+
if (!v[member.value]) continue;
21+
fmt::format_to(ctx.out(), "{}{}", (printed ? "|"sv : ""sv), enum19::valueName(member.value));
22+
printed = true;
23+
}
24+
return fmt::format_to(
25+
ctx.out(),
26+
"{} ({:0{}b})",
27+
(printed ? ""sv : "<>"sv),
28+
static_cast<Value>(v),
29+
enum19::max_underlying_value_of<Enum>);
30+
}
31+
};

src/flags19.lib/flags19/FlagsOf.h

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#pragma once
2+
#include "enum19/Enum.h"
3+
#include "enum19/Enum.max.h"
4+
5+
#include <stdint.h> // int64_t, uint64_t
6+
7+
namespace flags19 {
8+
9+
using enum19::HasMetaEnum;
10+
using enum19::max_underlying_value_of;
11+
using enum19::meta_enum_for;
12+
13+
namespace details {
14+
15+
template<size_t maxBit> constexpr auto storageTypeForMaxBit() {
16+
if constexpr (maxBit <= sizeof(uint8_t)) {
17+
return uint8_t{};
18+
}
19+
else if constexpr (maxBit <= sizeof(uint16_t)) {
20+
return uint16_t{};
21+
}
22+
else if constexpr (maxBit <= sizeof(uint32_t)) {
23+
return uint32_t{};
24+
}
25+
else if constexpr (maxBit <= sizeof(uint64_t)) {
26+
return uint64_t{};
27+
}
28+
else {
29+
static_assert(maxBit > sizeof(uint64_t), "not supported right now");
30+
}
31+
}
32+
33+
} // namespace details
34+
35+
template<HasMetaEnum Enum> struct FlagsOf {
36+
using UnderlyingBit = std::underlying_type_t<Enum>;
37+
using Value = decltype(details::storageTypeForMaxBit<max_underlying_value_of<Enum>>());
38+
39+
constexpr FlagsOf() = default;
40+
explicit constexpr FlagsOf(Value const& value) : m_value{value} {}
41+
42+
template<class... Args> requires((sizeof...(Args) > 0) && ... && std::is_same_v<Args, Enum>)
43+
explicit constexpr FlagsOf(Args... args) : FlagsOf{((1U << static_cast<UnderlyingBit>(args)) | ...)} {}
44+
45+
auto operator==(FlagsOf const&) const -> bool = default;
46+
47+
explicit operator Value() const { return m_value; }
48+
49+
[[nodiscard]] constexpr auto operator[](Enum bit) const noexcept -> bool {
50+
return 0U != (m_value & (1U << static_cast<UnderlyingBit>(bit)));
51+
}
52+
53+
template<class... Args> requires((sizeof...(Args) > 0) && ... && std::is_same_v<Args, Enum>)
54+
constexpr auto allOf(Args... args) const -> bool {
55+
auto const mask = ((1U << static_cast<UnderlyingBit>(args)) | ...);
56+
return mask == (m_value & mask);
57+
}
58+
59+
template<class... Args> requires((sizeof...(Args) > 0) && ... && std::is_same_v<Args, Enum>)
60+
constexpr auto someOf(Args... args) const -> bool {
61+
auto const mask = ((1U << static_cast<UnderlyingBit>(args)) | ...);
62+
return 0U != (m_value & mask);
63+
}
64+
65+
template<class... Args> requires((sizeof...(Args) > 0) && ... && std::is_same_v<Args, Enum>)
66+
constexpr auto noneOf(Args... args) const -> bool {
67+
auto const mask = ((1U << static_cast<UnderlyingBit>(args)) | ...);
68+
return 0U == (m_value & mask);
69+
}
70+
71+
constexpr void resetAll() { m_value = {}; }
72+
73+
template<class... Args> requires((sizeof...(Args) > 0) && ... && std::is_same_v<Args, Enum>)
74+
constexpr void set(Args... args) {
75+
m_value |= ((1U << static_cast<UnderlyingBit>(args)) | ...);
76+
}
77+
78+
template<class... Args> requires((sizeof...(Args) > 0) && ... && std::is_same_v<Args, Enum>)
79+
constexpr void reset(Args... args) {
80+
m_value &= ~static_cast<Value>(((1U << static_cast<UnderlyingBit>(args)) | ...));
81+
}
82+
83+
template<class... Args> requires((sizeof...(Args) > 0) && ... && std::is_same_v<Args, Enum>)
84+
constexpr void toggle(Args... args) {
85+
m_value ^= ((1U << static_cast<UnderlyingBit>(args)) | ...);
86+
}
87+
88+
constexpr auto operator|(FlagsOf const& other) const -> FlagsOf { return FlagsOf{m_value | other.m_value}; }
89+
constexpr auto operator&(FlagsOf const& other) const -> FlagsOf { return FlagsOf{m_value & other.m_value}; }
90+
constexpr auto operator|=(FlagsOf const& other) -> FlagsOf& { return m_value |= other.m_value, *this; }
91+
constexpr auto operator&=(FlagsOf const& other) -> FlagsOf& { return m_value &= other.m_value, *this; }
92+
93+
private:
94+
Value m_value{};
95+
};
96+
97+
} // namespace flags19
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#pragma once
2+
#include "FlagsOf.h"
3+
4+
#include <bitset>
5+
#include <enum19/Enum.names.h>
6+
#include <iosfwd>
7+
#include <string19/StringView.ostream.h>
8+
9+
namespace flags19 {
10+
11+
template<class Enum> auto operator<<(std::ostream& out, FlagsOf<Enum> const& flags) -> std::ostream& {
12+
using namespace std::string_view_literals;
13+
auto printed = false;
14+
for (auto& member : enum19::meta_enum_for<Enum>.members) {
15+
if (!flags[member.value]) continue;
16+
out << (printed ? "|"sv : ""sv) << enum19::valueName(member.value);
17+
printed = true;
18+
}
19+
return out << (printed ? " ("sv : "<> ("sv)
20+
<< std::bitset<max_underlying_value_of<Enum>>{static_cast<typename FlagsOf<Enum>::Value>(flags)} << ')';
21+
}
22+
23+
} // namespace flags19
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#include "FlagsOf.h"
2+
3+
#include "FlagsOf.ostream.h"
4+
5+
#include <gtest/gtest.h>
6+
7+
using namespace flags19;
8+
9+
namespace my_test {
10+
11+
ENUM19(CheckBit, uint8_t, Ordered, Packaged, Delivered, Received, Complained, ReturnLabeled, ReturnReceived);
12+
using CheckBits = FlagsOf<CheckBit>;
13+
14+
} // namespace my_test
15+
16+
TEST(Flags, example) {
17+
using enum my_test::CheckBit;
18+
constexpr auto checks = my_test::CheckBits{Ordered, Delivered};
19+
20+
static_assert(checks[Ordered]);
21+
static_assert(checks[Delivered]);
22+
static_assert(!checks[Packaged]);
23+
static_assert(!checks[ReturnReceived]);
24+
25+
static_assert(checks.allOf(Delivered));
26+
static_assert(!checks.allOf(Delivered, Received));
27+
28+
static_assert(checks.someOf(Delivered, Received));
29+
30+
static_assert(checks.noneOf(Complained, Received));
31+
32+
auto mutChecks = checks;
33+
mutChecks.set(Packaged);
34+
EXPECT_TRUE(mutChecks[Packaged]);
35+
36+
mutChecks.reset(Delivered, Received);
37+
EXPECT_FALSE(mutChecks[Delivered]);
38+
EXPECT_TRUE(mutChecks.noneOf(Delivered, Received));
39+
40+
mutChecks.toggle(Packaged, Delivered);
41+
EXPECT_EQ(mutChecks, checks);
42+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#pragma once
2+
#include "FlagsOf.h"
3+
4+
namespace flags19 {
5+
6+
template<class T> constexpr auto is_flags_of = false;
7+
template<class Enum> constexpr auto is_flags_of<FlagsOf<Enum>> = true;
8+
9+
} // namespace flags19
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
add_library(flags19 INTERFACE)
2+
target_link_libraries(flags19
3+
INTERFACE CoCpp19::enum19
4+
)
5+
target_include_directories(flags19
6+
INTERFACE
7+
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/>
8+
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
9+
)
10+
file(GLOB flags19_headers "${CMAKE_CURRENT_LIST_DIR}/*.h")
11+
target_sources(flags19
12+
INTERFACE FILE_SET public_headers
13+
TYPE HEADERS
14+
FILES ${flags19_headers}
15+
)
16+
17+
add_library(CoCpp19::flags19 ALIAS flags19)
18+
install(TARGETS flags19
19+
EXPORT flags19Targets
20+
FILE_SET public_headers
21+
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
22+
COMPONENT "flags19"
23+
)
24+
install(EXPORT flags19Targets
25+
FILE "CoCpp19-flags19-targets.cmake"
26+
NAMESPACE "CoCpp19::"
27+
DESTINATION ${COCPP19_CMAKE_CONFIG_DESTINATION}
28+
COMPONENT "flags19"
29+
)

0 commit comments

Comments
 (0)