Skip to content

Commit 5a4148d

Browse files
authored
Merge pull request #28 from falbrechtskirchinger/bitset
Extend bitset functionallity
2 parents c2e1c58 + ff40d7c commit 5a4148d

File tree

2 files changed

+154
-11
lines changed

2 files changed

+154
-11
lines changed

libs/common/include/s25util/enumUtils.h

Lines changed: 71 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,78 @@
44

55
#pragma once
66

7+
#include <boost/config.hpp>
78
#include <type_traits>
89

10+
template<typename Enum>
11+
struct IsBitset : std::false_type
12+
{};
13+
14+
template<typename Enum>
15+
constexpr bool IsValidBitset_v = IsBitset<Enum>::value && std::is_unsigned<std::underlying_type_t<Enum>>::value;
16+
17+
template<typename Enum>
18+
using require_validBitset = std::enable_if_t<IsValidBitset_v<Enum>>;
19+
20+
template<typename Enum, typename = require_validBitset<Enum>>
21+
constexpr auto operator&(Enum lhs, Enum rhs) noexcept
22+
{
23+
using Int = std::underlying_type_t<Enum>;
24+
return Enum(static_cast<Int>(lhs) & static_cast<Int>(rhs));
25+
}
26+
27+
template<typename Enum, typename = require_validBitset<Enum>>
28+
constexpr auto operator|(Enum lhs, Enum rhs) noexcept
29+
{
30+
using Int = std::underlying_type_t<Enum>;
31+
return Enum(static_cast<Int>(lhs) | static_cast<Int>(rhs));
32+
}
33+
34+
template<typename Enum, typename = require_validBitset<Enum>>
35+
constexpr Enum& operator&=(Enum& lhs, Enum rhs) noexcept
36+
{
37+
return lhs = lhs & rhs;
38+
}
39+
40+
template<typename Enum, typename = require_validBitset<Enum>>
41+
constexpr Enum& operator|=(Enum& lhs, Enum rhs) noexcept
42+
{
43+
return lhs = lhs | rhs;
44+
}
45+
46+
namespace bitset {
47+
template<typename Enum, typename = require_validBitset<Enum>>
48+
BOOST_ATTRIBUTE_NODISCARD constexpr Enum clear(const Enum val, const Enum flag)
49+
{
50+
using Int = std::underlying_type_t<Enum>;
51+
return val & Enum(~static_cast<const Int>(flag));
52+
}
53+
54+
template<typename Enum, typename = require_validBitset<Enum>>
55+
BOOST_ATTRIBUTE_NODISCARD constexpr Enum set(const Enum val, const Enum flag, const bool state = true)
56+
{
57+
return state ? (val | flag) : clear(val, flag);
58+
}
59+
60+
template<typename Enum, typename = require_validBitset<Enum>>
61+
BOOST_ATTRIBUTE_NODISCARD constexpr Enum toggle(const Enum val, const Enum flag)
62+
{
63+
using Int = std::underlying_type_t<Enum>;
64+
return Enum(static_cast<const Int>(val) ^ static_cast<const Int>(flag));
65+
}
66+
67+
template<typename Enum, typename = require_validBitset<Enum>>
68+
BOOST_ATTRIBUTE_NODISCARD constexpr bool isSet(const Enum val, const Enum flag)
69+
{
70+
return (val & flag) == flag;
71+
}
72+
} // namespace bitset
73+
974
/// Makes a strongly typed enum usable as a bitset
10-
#define MAKE_BITSET_STRONG(Type) \
11-
inline auto operator&(Type lhs, Type rhs) \
12-
{ \
13-
return Type(static_cast<unsigned>(lhs) & static_cast<unsigned>(rhs)); \
14-
} \
15-
inline auto operator|(Type lhs, Type rhs) \
16-
{ \
17-
return Type(static_cast<unsigned>(lhs) | static_cast<unsigned>(rhs)); \
18-
} \
19-
/* NOLINTNEXTLINE(bugprone-macro-parentheses) */ \
20-
static_assert(std::is_unsigned<std::underlying_type_t<Type>>::value, \
75+
#define MAKE_BITSET_STRONG(Type) \
76+
template<> \
77+
struct IsBitset<Type> : std::true_type \
78+
{}; \
79+
/* NOLINTNEXTLINE(bugprone-macro-parentheses) */ \
80+
static_assert(std::is_unsigned<std::underlying_type_t<Type>>::value, \
2181
#Type " must use unsigned type as the underlying type")

tests/testEnumUtils.cpp

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Copyright (C) 2005 - 2023 Settlers Freaks (sf-team at siedler25.org)
2+
//
3+
// SPDX-License-Identifier: GPL-2.0-or-later
4+
5+
#include "s25util/enumUtils.h"
6+
#include <boost/test/unit_test.hpp>
7+
#include <sstream>
8+
9+
enum class InvalidBitset : int
10+
{
11+
};
12+
template<>
13+
struct IsBitset<InvalidBitset> : std::true_type
14+
{};
15+
16+
enum class Bitset : unsigned
17+
{
18+
None,
19+
A = 1 << 0,
20+
B = 1 << 1,
21+
C = 1 << 2
22+
};
23+
MAKE_BITSET_STRONG(Bitset);
24+
25+
// Check type traits
26+
static_assert(IsBitset<InvalidBitset>::value);
27+
static_assert(!IsValidBitset_v<InvalidBitset>);
28+
29+
static_assert(IsBitset<Bitset>::value);
30+
static_assert(IsValidBitset_v<Bitset>);
31+
32+
BOOST_AUTO_TEST_SUITE(EnumUtils)
33+
34+
BOOST_AUTO_TEST_CASE(Operators)
35+
{
36+
BOOST_REQUIRE(static_cast<unsigned>(Bitset{}) == 0);
37+
38+
{
39+
Bitset b{};
40+
b = b | Bitset::A;
41+
BOOST_TEST(static_cast<unsigned>(b) == 0b001u);
42+
b |= Bitset::B;
43+
BOOST_TEST(static_cast<unsigned>(b) == 0b011u);
44+
(b |= Bitset::A) = Bitset::C;
45+
BOOST_CHECK(b == Bitset::C);
46+
}
47+
48+
{
49+
Bitset b = Bitset::A | Bitset::B | Bitset::C;
50+
b = b & (Bitset::A | Bitset::B);
51+
BOOST_TEST(static_cast<unsigned>(b) == 0b011u);
52+
b &= Bitset::B;
53+
BOOST_TEST(static_cast<unsigned>(b) == 0b010u);
54+
(b &= Bitset::A) = Bitset::C;
55+
BOOST_CHECK(b == Bitset::C);
56+
}
57+
}
58+
59+
BOOST_AUTO_TEST_CASE(UtilityFunctions)
60+
{
61+
Bitset b = Bitset::A | Bitset::C;
62+
BOOST_TEST(bitset::isSet(b, Bitset::A));
63+
BOOST_TEST(bitset::isSet(b, Bitset::A | Bitset::C));
64+
BOOST_TEST(!bitset::isSet(b, Bitset::B));
65+
BOOST_TEST(!bitset::isSet(b, Bitset::B | Bitset::C));
66+
67+
b = bitset::set(b, Bitset::B /*, true */);
68+
BOOST_TEST(static_cast<unsigned>(b) == 0b111u);
69+
70+
b = bitset::set(b, Bitset::B, false);
71+
BOOST_TEST(static_cast<unsigned>(b) == 0b101u);
72+
73+
b = bitset::clear(b, Bitset::A);
74+
BOOST_TEST(static_cast<unsigned>(b) == 0b100u);
75+
76+
b = bitset::toggle(b, Bitset::A);
77+
BOOST_TEST(static_cast<unsigned>(b) == 0b101u);
78+
79+
b = bitset::toggle(b, Bitset::A | Bitset::B);
80+
BOOST_TEST(static_cast<unsigned>(b) == 0b110u);
81+
}
82+
83+
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)