Skip to content

Commit

Permalink
Merge pull request #28 from falbrechtskirchinger/bitset
Browse files Browse the repository at this point in the history
Extend bitset functionallity
  • Loading branch information
Flamefire authored Jul 26, 2023
2 parents c2e1c58 + ff40d7c commit 5a4148d
Show file tree
Hide file tree
Showing 2 changed files with 154 additions and 11 deletions.
82 changes: 71 additions & 11 deletions libs/common/include/s25util/enumUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,78 @@

#pragma once

#include <boost/config.hpp>
#include <type_traits>

template<typename Enum>
struct IsBitset : std::false_type
{};

template<typename Enum>
constexpr bool IsValidBitset_v = IsBitset<Enum>::value && std::is_unsigned<std::underlying_type_t<Enum>>::value;

template<typename Enum>
using require_validBitset = std::enable_if_t<IsValidBitset_v<Enum>>;

template<typename Enum, typename = require_validBitset<Enum>>
constexpr auto operator&(Enum lhs, Enum rhs) noexcept
{
using Int = std::underlying_type_t<Enum>;
return Enum(static_cast<Int>(lhs) & static_cast<Int>(rhs));
}

template<typename Enum, typename = require_validBitset<Enum>>
constexpr auto operator|(Enum lhs, Enum rhs) noexcept
{
using Int = std::underlying_type_t<Enum>;
return Enum(static_cast<Int>(lhs) | static_cast<Int>(rhs));
}

template<typename Enum, typename = require_validBitset<Enum>>
constexpr Enum& operator&=(Enum& lhs, Enum rhs) noexcept
{
return lhs = lhs & rhs;
}

template<typename Enum, typename = require_validBitset<Enum>>
constexpr Enum& operator|=(Enum& lhs, Enum rhs) noexcept
{
return lhs = lhs | rhs;
}

namespace bitset {
template<typename Enum, typename = require_validBitset<Enum>>
BOOST_ATTRIBUTE_NODISCARD constexpr Enum clear(const Enum val, const Enum flag)
{
using Int = std::underlying_type_t<Enum>;
return val & Enum(~static_cast<const Int>(flag));
}

template<typename Enum, typename = require_validBitset<Enum>>
BOOST_ATTRIBUTE_NODISCARD constexpr Enum set(const Enum val, const Enum flag, const bool state = true)
{
return state ? (val | flag) : clear(val, flag);
}

template<typename Enum, typename = require_validBitset<Enum>>
BOOST_ATTRIBUTE_NODISCARD constexpr Enum toggle(const Enum val, const Enum flag)
{
using Int = std::underlying_type_t<Enum>;
return Enum(static_cast<const Int>(val) ^ static_cast<const Int>(flag));
}

template<typename Enum, typename = require_validBitset<Enum>>
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) \
inline auto operator&(Type lhs, Type rhs) \
{ \
return Type(static_cast<unsigned>(lhs) & static_cast<unsigned>(rhs)); \
} \
inline auto operator|(Type lhs, Type rhs) \
{ \
return Type(static_cast<unsigned>(lhs) | static_cast<unsigned>(rhs)); \
} \
/* NOLINTNEXTLINE(bugprone-macro-parentheses) */ \
static_assert(std::is_unsigned<std::underlying_type_t<Type>>::value, \
#define MAKE_BITSET_STRONG(Type) \
template<> \
struct IsBitset<Type> : std::true_type \
{}; \
/* NOLINTNEXTLINE(bugprone-macro-parentheses) */ \
static_assert(std::is_unsigned<std::underlying_type_t<Type>>::value, \
#Type " must use unsigned type as the underlying type")
83 changes: 83 additions & 0 deletions tests/testEnumUtils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Copyright (C) 2005 - 2023 Settlers Freaks (sf-team at siedler25.org)
//
// SPDX-License-Identifier: GPL-2.0-or-later

#include "s25util/enumUtils.h"
#include <boost/test/unit_test.hpp>
#include <sstream>

enum class InvalidBitset : int
{
};
template<>
struct IsBitset<InvalidBitset> : 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<InvalidBitset>::value);
static_assert(!IsValidBitset_v<InvalidBitset>);

static_assert(IsBitset<Bitset>::value);
static_assert(IsValidBitset_v<Bitset>);

BOOST_AUTO_TEST_SUITE(EnumUtils)

BOOST_AUTO_TEST_CASE(Operators)
{
BOOST_REQUIRE(static_cast<unsigned>(Bitset{}) == 0);

{
Bitset b{};
b = b | Bitset::A;
BOOST_TEST(static_cast<unsigned>(b) == 0b001u);
b |= Bitset::B;
BOOST_TEST(static_cast<unsigned>(b) == 0b011u);
(b |= Bitset::A) = Bitset::C;
BOOST_CHECK(b == Bitset::C);
}

{
Bitset b = Bitset::A | Bitset::B | Bitset::C;
b = b & (Bitset::A | Bitset::B);
BOOST_TEST(static_cast<unsigned>(b) == 0b011u);
b &= Bitset::B;
BOOST_TEST(static_cast<unsigned>(b) == 0b010u);
(b &= Bitset::A) = Bitset::C;
BOOST_CHECK(b == Bitset::C);
}
}

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<unsigned>(b) == 0b111u);

b = bitset::set(b, Bitset::B, false);
BOOST_TEST(static_cast<unsigned>(b) == 0b101u);

b = bitset::clear(b, Bitset::A);
BOOST_TEST(static_cast<unsigned>(b) == 0b100u);

b = bitset::toggle(b, Bitset::A);
BOOST_TEST(static_cast<unsigned>(b) == 0b101u);

b = bitset::toggle(b, Bitset::A | Bitset::B);
BOOST_TEST(static_cast<unsigned>(b) == 0b110u);
}

BOOST_AUTO_TEST_SUITE_END()

0 comments on commit 5a4148d

Please sign in to comment.