From 82e2584b3054c1660340f7cc316486d2d4e6440c Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Sat, 22 Jul 2023 17:04:58 +0200 Subject: [PATCH 1/6] Add more bitset operators and trait-based opt-in Add new operator overloads to enhance the functionality of bitsets, and introduce a type trait that enables enums to opt in to being used as bitsets. --- libs/common/include/s25util/enumUtils.h | 53 ++++++++++++++++++++----- 1 file changed, 42 insertions(+), 11 deletions(-) diff --git a/libs/common/include/s25util/enumUtils.h b/libs/common/include/s25util/enumUtils.h index 4418692..3219395 100644 --- a/libs/common/include/s25util/enumUtils.h +++ b/libs/common/include/s25util/enumUtils.h @@ -6,16 +6,47 @@ #include +template +struct IsBitset : std::false_type +{}; + +template +constexpr bool IsValidBitset = IsBitset::value && std::is_unsigned>::value; + +template +using require_validBitset = std::enable_if_t>; + +template> +constexpr auto operator&(Enum lhs, Enum rhs) noexcept +{ + using Int = std::underlying_type_t; + return Enum(static_cast(lhs) & static_cast(rhs)); +} + +template> +constexpr auto operator|(Enum lhs, Enum rhs) noexcept +{ + using Int = std::underlying_type_t; + return Enum(static_cast(lhs) | static_cast(rhs)); +} + +template> +constexpr auto operator&=(Enum& lhs, Enum rhs) noexcept +{ + return lhs = lhs & rhs; +} + +template> +constexpr auto operator|=(Enum& lhs, Enum rhs) noexcept +{ + return lhs = lhs | rhs; +} + /// Makes a strongly typed enum usable as a bitset -#define MAKE_BITSET_STRONG(Type) \ - inline auto operator&(Type lhs, Type rhs) \ - { \ - return Type(static_cast(lhs) & static_cast(rhs)); \ - } \ - inline auto operator|(Type lhs, Type rhs) \ - { \ - return Type(static_cast(lhs) | static_cast(rhs)); \ - } \ - /* NOLINTNEXTLINE(bugprone-macro-parentheses) */ \ - static_assert(std::is_unsigned>::value, \ +#define MAKE_BITSET_STRONG(Type) \ + template<> \ + struct IsBitset : std::true_type \ + {}; \ + /* NOLINTNEXTLINE(bugprone-macro-parentheses) */ \ + static_assert(std::is_unsigned>::value, \ #Type " must use unsigned type as the underlying type") From ef0089c91340c317226057d55dcc41d5495557b9 Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Sat, 22 Jul 2023 17:20:18 +0200 Subject: [PATCH 2/6] Add bitset utility functions Implement utility functions to set, clear, toggle, and query a bitset for more concise code. --- libs/common/include/s25util/enumUtils.h | 33 +++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/libs/common/include/s25util/enumUtils.h b/libs/common/include/s25util/enumUtils.h index 3219395..7ef6409 100644 --- a/libs/common/include/s25util/enumUtils.h +++ b/libs/common/include/s25util/enumUtils.h @@ -4,6 +4,7 @@ #pragma once +#include #include template @@ -31,17 +32,45 @@ constexpr auto operator|(Enum lhs, Enum rhs) noexcept } template> -constexpr auto operator&=(Enum& lhs, Enum rhs) noexcept +constexpr Enum& operator&=(Enum& lhs, Enum rhs) noexcept { return lhs = lhs & rhs; } template> -constexpr auto operator|=(Enum& lhs, Enum rhs) noexcept +constexpr Enum& operator|=(Enum& lhs, Enum rhs) noexcept { return lhs = lhs | rhs; } +namespace bitset { +template> +BOOST_ATTRIBUTE_NODISCARD constexpr Enum clear(const Enum val, const Enum flag) +{ + using Int = std::underlying_type_t; + return val & Enum(~static_cast(flag)); +} + +template> +BOOST_ATTRIBUTE_NODISCARD constexpr Enum set(const Enum val, const Enum flag, const bool state = true) +{ + return state ? (val | flag) : clear(val, flag); +} + +template> +BOOST_ATTRIBUTE_NODISCARD constexpr Enum toggle(const Enum val, const Enum flag) +{ + using Int = std::underlying_type_t; + return Enum(static_cast(val) ^ static_cast(flag)); +} + +template> +BOOST_ATTRIBUTE_NODISCARD constexpr bool isSet(const Enum val, const Enum flag) +{ + return (val & flag) == flag; +} +} // namespace bitset + /// Makes a strongly typed enum usable as a bitset #define MAKE_BITSET_STRONG(Type) \ template<> \ From 448a1da8d6ca029d5ee752303f181e81ccd4bbcd Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Sat, 22 Jul 2023 13:07:07 +0200 Subject: [PATCH 3/6] Add bitset unit tests --- tests/testEnumUtils.cpp | 79 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 tests/testEnumUtils.cpp diff --git a/tests/testEnumUtils.cpp b/tests/testEnumUtils.cpp new file mode 100644 index 0000000..610b8ea --- /dev/null +++ b/tests/testEnumUtils.cpp @@ -0,0 +1,79 @@ +// Copyright (C) 2005 - 2023 Settlers Freaks (sf-team at siedler25.org) +// +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "s25util/enumUtils.h" +#include +#include + +enum class InvalidBitset : int +{ +}; +template<> +struct IsBitset : std::true_type +{}; + +enum class Bitset : unsigned +{ + None, + A = 1 << 0, + B = 1 << 1, + C = 1 << 2 +}; +MAKE_BITSET_STRONG(Bitset); + +// Check type traits +static_assert(IsBitset::value); +static_assert(!IsValidBitset); + +static_assert(IsBitset::value); +static_assert(IsValidBitset); + +BOOST_AUTO_TEST_SUITE(EnumUtils) + +BOOST_AUTO_TEST_CASE(Operators) +{ + BOOST_REQUIRE(static_cast(Bitset{}) == 0); + + { + Bitset b{}; + b = b | Bitset::A; + BOOST_TEST(static_cast(b) == 0x01u); + b |= Bitset::B; + BOOST_TEST(static_cast(b) == 0x03u); + } + + { + Bitset b = Bitset::A | Bitset::B | Bitset::C; + b = b & (Bitset::A | Bitset::B); + BOOST_TEST(static_cast(b) == 0x03u); + b &= Bitset::B; + BOOST_TEST(static_cast(b) == 0x02u); + } +} + +BOOST_AUTO_TEST_CASE(UtilityFunctions) +{ + Bitset b = Bitset::A | Bitset::C; + BOOST_TEST(bitset::isSet(b, Bitset::A)); + BOOST_TEST(bitset::isSet(b, Bitset::A | Bitset::C)); + BOOST_TEST(!bitset::isSet(b, Bitset::B)); + BOOST_TEST(!bitset::isSet(b, Bitset::B | Bitset::C)); + + b = bitset::set(b, Bitset::B /*, true */); + BOOST_TEST(static_cast(b) == 0x07u); + + b = bitset::set(b, Bitset::B, false); + BOOST_TEST(static_cast(b) == 0x05u); + + b = bitset::clear(b, Bitset::A); + BOOST_TEST(static_cast(b) == 0x04u); + + b = bitset::toggle(b, Bitset::A); + BOOST_TEST(static_cast(b) == 0x05u); + + b = bitset::toggle(b, Bitset::A | Bitset::B); + BOOST_TEST(static_cast(b) == 0x06u); +} + +BOOST_AUTO_TEST_SUITE_END() From 67d167d7c2b98c476ffecd56696a602b3145dc86 Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Mon, 24 Jul 2023 11:29:21 +0200 Subject: [PATCH 4/6] Use binary literals in unit tests --- tests/testEnumUtils.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/testEnumUtils.cpp b/tests/testEnumUtils.cpp index 610b8ea..1feeada 100644 --- a/tests/testEnumUtils.cpp +++ b/tests/testEnumUtils.cpp @@ -38,17 +38,17 @@ BOOST_AUTO_TEST_CASE(Operators) { Bitset b{}; b = b | Bitset::A; - BOOST_TEST(static_cast(b) == 0x01u); + BOOST_TEST(static_cast(b) == 0b001u); b |= Bitset::B; - BOOST_TEST(static_cast(b) == 0x03u); + BOOST_TEST(static_cast(b) == 0b011u); } { Bitset b = Bitset::A | Bitset::B | Bitset::C; b = b & (Bitset::A | Bitset::B); - BOOST_TEST(static_cast(b) == 0x03u); + BOOST_TEST(static_cast(b) == 0b011u); b &= Bitset::B; - BOOST_TEST(static_cast(b) == 0x02u); + BOOST_TEST(static_cast(b) == 0b010u); } } @@ -61,19 +61,19 @@ BOOST_AUTO_TEST_CASE(UtilityFunctions) BOOST_TEST(!bitset::isSet(b, Bitset::B | Bitset::C)); b = bitset::set(b, Bitset::B /*, true */); - BOOST_TEST(static_cast(b) == 0x07u); + BOOST_TEST(static_cast(b) == 0b111u); b = bitset::set(b, Bitset::B, false); - BOOST_TEST(static_cast(b) == 0x05u); + BOOST_TEST(static_cast(b) == 0b101u); b = bitset::clear(b, Bitset::A); - BOOST_TEST(static_cast(b) == 0x04u); + BOOST_TEST(static_cast(b) == 0b100u); b = bitset::toggle(b, Bitset::A); - BOOST_TEST(static_cast(b) == 0x05u); + BOOST_TEST(static_cast(b) == 0b101u); b = bitset::toggle(b, Bitset::A | Bitset::B); - BOOST_TEST(static_cast(b) == 0x06u); + BOOST_TEST(static_cast(b) == 0b110u); } BOOST_AUTO_TEST_SUITE_END() From 327c17cc04fdd484068b91fa7f4f606393763ab9 Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Tue, 25 Jul 2023 15:01:12 +0200 Subject: [PATCH 5/6] Test bitset assignment ops return lvalue refs --- tests/testEnumUtils.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/testEnumUtils.cpp b/tests/testEnumUtils.cpp index 1feeada..bfcbdfc 100644 --- a/tests/testEnumUtils.cpp +++ b/tests/testEnumUtils.cpp @@ -41,6 +41,8 @@ BOOST_AUTO_TEST_CASE(Operators) BOOST_TEST(static_cast(b) == 0b001u); b |= Bitset::B; BOOST_TEST(static_cast(b) == 0b011u); + (b |= Bitset::A) = Bitset::C; + BOOST_CHECK(b == Bitset::C); } { @@ -49,6 +51,8 @@ BOOST_AUTO_TEST_CASE(Operators) BOOST_TEST(static_cast(b) == 0b011u); b &= Bitset::B; BOOST_TEST(static_cast(b) == 0b010u); + (b &= Bitset::A) = Bitset::C; + BOOST_CHECK(b == Bitset::C); } } From ff40d7c73bd92d90fcf701fe278d5b3cb90aca8f Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Wed, 26 Jul 2023 10:26:56 +0200 Subject: [PATCH 6/6] Add `_v` suffix for template constant --- libs/common/include/s25util/enumUtils.h | 4 ++-- tests/testEnumUtils.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/common/include/s25util/enumUtils.h b/libs/common/include/s25util/enumUtils.h index 7ef6409..1115dfe 100644 --- a/libs/common/include/s25util/enumUtils.h +++ b/libs/common/include/s25util/enumUtils.h @@ -12,10 +12,10 @@ struct IsBitset : std::false_type {}; template -constexpr bool IsValidBitset = IsBitset::value && std::is_unsigned>::value; +constexpr bool IsValidBitset_v = IsBitset::value && std::is_unsigned>::value; template -using require_validBitset = std::enable_if_t>; +using require_validBitset = std::enable_if_t>; template> constexpr auto operator&(Enum lhs, Enum rhs) noexcept diff --git a/tests/testEnumUtils.cpp b/tests/testEnumUtils.cpp index bfcbdfc..ba4d3f1 100644 --- a/tests/testEnumUtils.cpp +++ b/tests/testEnumUtils.cpp @@ -24,10 +24,10 @@ MAKE_BITSET_STRONG(Bitset); // Check type traits static_assert(IsBitset::value); -static_assert(!IsValidBitset); +static_assert(!IsValidBitset_v); static_assert(IsBitset::value); -static_assert(IsValidBitset); +static_assert(IsValidBitset_v); BOOST_AUTO_TEST_SUITE(EnumUtils)