Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend bitset functionallity #28

Merged
merged 6 commits into from
Jul 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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()