Skip to content

Commit

Permalink
Add FlagsOf implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
arBmind committed Jul 3, 2024
1 parent a35a912 commit 72e501f
Show file tree
Hide file tree
Showing 15 changed files with 307 additions and 0 deletions.
1 change: 1 addition & 0 deletions co-cpp19.qbs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Project {
"src/array19.lib",
"src/coro19.lib",
"src/enum19.lib",
"src/flags19.lib",
"src/meta19.lib",
"src/lookup19.lib",
"src/optional19.lib",
Expand Down
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
add_subdirectory(array19.lib)
add_subdirectory(coro19.lib)
add_subdirectory(enum19.lib)
add_subdirectory(flags19.lib)
add_subdirectory(lookup19.lib)
add_subdirectory(meta19.lib)
add_subdirectory(optional19.lib)
Expand Down
3 changes: 3 additions & 0 deletions src/flags19.lib/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

include(flags19/flags19.cmake)
include(flags19/flags19.tests.cmake)
9 changes: 9 additions & 0 deletions src/flags19.lib/flags19.lib.qbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

Project {
name: "flags19.lib"

references: [
"flags19/flags19.qbs",
"flags19/flags19.tests.qbs",
]
}
31 changes: 31 additions & 0 deletions src/flags19.lib/flags19/FlagsOf.fmt.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#pragma once
#include "FlagsOf.h"
#include "enum19/Enum.names.h"

/// noto: for this header you need fmt library (not included as library dependency)
#include <fmt/format.h>
#include <string_view>

/// adds enum support for fmt
template<enum19::HasMetaEnum Enum, class Char> struct fmt::formatter<flags19::FlagsOf<Enum>, Char> {
using T = flags19::FlagsOf<Enum>;
using Value = typename T::Value;
constexpr auto parse(fmt::basic_format_parse_context<Char>& ctx) { return ctx.begin(); }

template<typename FormatCtx> auto format(const T& v, FormatCtx& ctx) const {
// auto underlying = static_cast<std::underlying_type_t<T>>(v);
using namespace std::string_view_literals;
auto printed = false;
for (auto& member : enum19::meta_enum_for<Enum>.members) {
if (!v[member.value]) continue;
fmt::format_to(ctx.out(), "{}{}", (printed ? "|"sv : ""sv), enum19::valueName(member.value));
printed = true;
}
return fmt::format_to(
ctx.out(),
"{} ({:0{}b})",
(printed ? ""sv : "<>"sv),
static_cast<Value>(v),
enum19::max_underlying_value_of<Enum>);
}
};
97 changes: 97 additions & 0 deletions src/flags19.lib/flags19/FlagsOf.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#pragma once
#include "enum19/Enum.h"
#include "enum19/Enum.max.h"

#include <stdint.h> // int64_t, uint64_t

namespace flags19 {

using enum19::HasMetaEnum;
using enum19::max_underlying_value_of;
using enum19::meta_enum_for;

namespace details {

template<size_t maxBit> constexpr auto storageTypeForMaxBit() {
if constexpr (maxBit <= sizeof(uint8_t)) {
return uint8_t{};
}
else if constexpr (maxBit <= sizeof(uint16_t)) {
return uint16_t{};
}
else if constexpr (maxBit <= sizeof(uint32_t)) {
return uint32_t{};
}
else if constexpr (maxBit <= sizeof(uint64_t)) {
return uint64_t{};
}
else {
static_assert(maxBit > sizeof(uint64_t), "not supported right now");
}
}

} // namespace details

template<HasMetaEnum Enum> struct FlagsOf {
using UnderlyingBit = std::underlying_type_t<Enum>;
using Value = decltype(details::storageTypeForMaxBit<max_underlying_value_of<Enum>>());

constexpr FlagsOf() = default;
explicit constexpr FlagsOf(Value const& value) : m_value{value} {}

template<class... Args> requires((sizeof...(Args) > 0) && ... && std::is_same_v<Args, Enum>)
explicit constexpr FlagsOf(Args... args) : FlagsOf{((1U << static_cast<UnderlyingBit>(args)) | ...)} {}

auto operator==(FlagsOf const&) const -> bool = default;

explicit operator Value() const { return m_value; }

[[nodiscard]] constexpr auto operator[](Enum bit) const noexcept -> bool {
return 0U != (m_value & (1U << static_cast<UnderlyingBit>(bit)));
}

template<class... Args> requires((sizeof...(Args) > 0) && ... && std::is_same_v<Args, Enum>)
constexpr auto allOf(Args... args) const -> bool {
auto const mask = ((1U << static_cast<UnderlyingBit>(args)) | ...);
return mask == (m_value & mask);
}

template<class... Args> requires((sizeof...(Args) > 0) && ... && std::is_same_v<Args, Enum>)
constexpr auto someOf(Args... args) const -> bool {
auto const mask = ((1U << static_cast<UnderlyingBit>(args)) | ...);
return 0U != (m_value & mask);
}

template<class... Args> requires((sizeof...(Args) > 0) && ... && std::is_same_v<Args, Enum>)
constexpr auto noneOf(Args... args) const -> bool {
auto const mask = ((1U << static_cast<UnderlyingBit>(args)) | ...);
return 0U == (m_value & mask);
}

constexpr void resetAll() { m_value = {}; }

template<class... Args> requires((sizeof...(Args) > 0) && ... && std::is_same_v<Args, Enum>)
constexpr void set(Args... args) {
m_value |= ((1U << static_cast<UnderlyingBit>(args)) | ...);
}

template<class... Args> requires((sizeof...(Args) > 0) && ... && std::is_same_v<Args, Enum>)
constexpr void reset(Args... args) {
m_value &= ~static_cast<Value>(((1U << static_cast<UnderlyingBit>(args)) | ...));
}

template<class... Args> requires((sizeof...(Args) > 0) && ... && std::is_same_v<Args, Enum>)
constexpr void toggle(Args... args) {
m_value ^= ((1U << static_cast<UnderlyingBit>(args)) | ...);
}

constexpr auto operator|(FlagsOf const& other) const -> FlagsOf { return FlagsOf{m_value | other.m_value}; }
constexpr auto operator&(FlagsOf const& other) const -> FlagsOf { return FlagsOf{m_value & other.m_value}; }
constexpr auto operator|=(FlagsOf const& other) -> FlagsOf& { return m_value |= other.m_value, *this; }
constexpr auto operator&=(FlagsOf const& other) -> FlagsOf& { return m_value &= other.m_value, *this; }

private:
Value m_value{};
};

} // namespace flags19
23 changes: 23 additions & 0 deletions src/flags19.lib/flags19/FlagsOf.ostream.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#pragma once
#include "FlagsOf.h"

#include <bitset>
#include <enum19/Enum.names.h>
#include <iosfwd>
#include <string19/StringView.ostream.h>

namespace flags19 {

template<class Enum> auto operator<<(std::ostream& out, FlagsOf<Enum> const& flags) -> std::ostream& {
using namespace std::string_view_literals;
auto printed = false;
for (auto& member : enum19::meta_enum_for<Enum>.members) {
if (!flags[member.value]) continue;
out << (printed ? "|"sv : ""sv) << enum19::valueName(member.value);
printed = true;
}
return out << (printed ? " ("sv : "<> ("sv)
<< std::bitset<max_underlying_value_of<Enum>>{static_cast<typename FlagsOf<Enum>::Value>(flags)} << ')';
}

} // namespace flags19
42 changes: 42 additions & 0 deletions src/flags19.lib/flags19/FlagsOf.test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#include "FlagsOf.h"

#include "FlagsOf.ostream.h"

#include <gtest/gtest.h>

using namespace flags19;

namespace my_test {

ENUM19(CheckBit, uint8_t, Ordered, Packaged, Delivered, Received, Complained, ReturnLabeled, ReturnReceived);
using CheckBits = FlagsOf<CheckBit>;

} // namespace my_test

TEST(Flags, example) {
using enum my_test::CheckBit;
constexpr auto checks = my_test::CheckBits{Ordered, Delivered};

static_assert(checks[Ordered]);
static_assert(checks[Delivered]);
static_assert(!checks[Packaged]);
static_assert(!checks[ReturnReceived]);

static_assert(checks.allOf(Delivered));
static_assert(!checks.allOf(Delivered, Received));

static_assert(checks.someOf(Delivered, Received));

static_assert(checks.noneOf(Complained, Received));

auto mutChecks = checks;
mutChecks.set(Packaged);
EXPECT_TRUE(mutChecks[Packaged]);

mutChecks.reset(Delivered, Received);
EXPECT_FALSE(mutChecks[Delivered]);
EXPECT_TRUE(mutChecks.noneOf(Delivered, Received));

mutChecks.toggle(Packaged, Delivered);
EXPECT_EQ(mutChecks, checks);
}
9 changes: 9 additions & 0 deletions src/flags19.lib/flags19/FlagsOf.trait.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#pragma once
#include "FlagsOf.h"

namespace flags19 {

template<class T> constexpr auto is_flags_of = false;
template<class Enum> constexpr auto is_flags_of<FlagsOf<Enum>> = true;

} // namespace flags19
29 changes: 29 additions & 0 deletions src/flags19.lib/flags19/flags19.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
add_library(flags19 INTERFACE)
target_link_libraries(flags19
INTERFACE CoCpp19::enum19
)
target_include_directories(flags19
INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
file(GLOB flags19_headers "${CMAKE_CURRENT_LIST_DIR}/*.h")
target_sources(flags19
INTERFACE FILE_SET public_headers
TYPE HEADERS
FILES ${flags19_headers}
)

add_library(CoCpp19::flags19 ALIAS flags19)
install(TARGETS flags19
EXPORT flags19Targets
FILE_SET public_headers
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
COMPONENT "flags19"
)
install(EXPORT flags19Targets
FILE "CoCpp19-flags19-targets.cmake"
NAMESPACE "CoCpp19::"
DESTINATION ${COCPP19_CMAKE_CONFIG_DESTINATION}
COMPONENT "flags19"
)
19 changes: 19 additions & 0 deletions src/flags19.lib/flags19/flags19.qbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@

Product {
Depends { name: "cpp" }
Depends { name: "enum19" }
Depends { name: "fmt"; required: false }

Export {
Depends { name: "cpp" }
cpp.includePaths: [".."]
Depends { name: "enum19" }
}

files: [
"FlagsOf.fmt.h",
"FlagsOf.h",
"FlagsOf.ostream.h",
"FlagsOf.trait.h",
]
}
11 changes: 11 additions & 0 deletions src/flags19.lib/flags19/flags19.tests.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

add_executable(flags19-test)
file(GLOB flags19_test_sources "${CMAKE_CURRENT_LIST_DIR}/*.test.cpp")
target_sources(flags19-test
PRIVATE ${flags19_test_sources}
)
target_link_libraries(flags19-test
PRIVATE GTest::gtest_main
PRIVATE CoCpp19::flags19
)
add_test(NAME flags19 COMMAND flags19-test)
15 changes: 15 additions & 0 deletions src/flags19.lib/flags19/flags19.tests.qbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

Application {
name: "flags19.tests"
condition: googletest.present

consoleApplication: true
type: ["application", "autotest"]

Depends { name: "flags19" }
Depends { name: "googletest" }

files: [
"FlagsOf.test.cpp",
]
}
16 changes: 16 additions & 0 deletions src/serialize19.lib/serialize19/serialize.Flags.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#pragma once
#include "flags19/FlagsOf.h" // requires variant19
#include "serialize.h"

namespace serialize19 {

template<Archive A, class Enum> void serialize(A& a, flags19::FlagsOf<Enum>& flags) {
using Value = typename flags19::FlagsOf<Enum>::Value;
auto value = static_cast<Value>(flags);
serialize(a, value);
if constexpr (A::mode == ArchiveMode::Read) {
flags = flags19::FlagsOf<Enum>{value};
}
}

} // namespace serialize19
1 change: 1 addition & 0 deletions src/serialize19.lib/serialize19/serialize19.qbs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ Product {
"serialize.Array.h",
"serialize.BufferSlice.h",
"serialize.DynamicArrayOf.h",
"serialize.Flags.h",
"serialize.None.h",
"serialize.Optional.h",
"serialize.PackedOptional.h",
Expand Down

0 comments on commit 72e501f

Please sign in to comment.