Improve performance and binary size of til::enumset
This commit is contained in:
parent
fc85bdf314
commit
656e8c0b84
|
@ -3,130 +3,134 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <bitset>
|
||||
#ifdef __cpp_concepts
|
||||
#define TIL_ENUMSET_VARARG template<std::same_as<T>... Args>
|
||||
#else
|
||||
#define TIL_ENUMSET_VARARG template<typename... Args, typename = std::enable_if_t<std::conjunction_v<std::is_same<T, Args>...>>>
|
||||
#endif
|
||||
|
||||
namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
{
|
||||
// By design, this class hides several methods in the std::bitset class
|
||||
// so they can be called with an enum parameter instead of a size_t, so
|
||||
// we need to disable the "hides a non-virtual function" warning.
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 26434)
|
||||
|
||||
// til::enumset is a subclass of std::bitset, storing a fixed size array of
|
||||
// boolean elements, the positions in the array being identified by values
|
||||
// from a given enumerated type. By default it holds the same number of
|
||||
// bits as a size_t value.
|
||||
template<typename Type, size_t Bits = std::numeric_limits<size_t>::digits>
|
||||
class enumset : public std::bitset<Bits>
|
||||
template<typename T>
|
||||
class enumset
|
||||
{
|
||||
using _base = std::bitset<Bits>;
|
||||
using underlying_type = uintptr_t;
|
||||
|
||||
public:
|
||||
using reference = typename _base::reference;
|
||||
|
||||
enumset() = default;
|
||||
|
||||
// Method Description:
|
||||
// - Constructs a new bitset with the given list of positions set to true.
|
||||
template<typename... Args, typename = std::enable_if_t<std::conjunction_v<std::is_same<Type, Args>...>>>
|
||||
constexpr enumset(const Args... positions) noexcept :
|
||||
_base((... | (1ULL << static_cast<size_t>(positions))))
|
||||
TIL_ENUMSET_VARARG
|
||||
constexpr enumset(Args... positions) noexcept :
|
||||
_data{ to_underlying(positions...) }
|
||||
{
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Returns the value of the bit at the given position.
|
||||
constexpr bool operator[](const Type pos) const
|
||||
underlying_type bits() const noexcept
|
||||
{
|
||||
return _base::operator[](static_cast<size_t>(pos));
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Returns a reference to the bit at the given position.
|
||||
reference operator[](const Type pos)
|
||||
{
|
||||
return _base::operator[](static_cast<size_t>(pos));
|
||||
return _data;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Returns the value of the bit at the given position.
|
||||
// Throws std::out_of_range if it is not a valid position
|
||||
// in the bitset.
|
||||
bool test(const Type pos) const
|
||||
bool test(const T pos) const noexcept
|
||||
{
|
||||
return _base::test(static_cast<size_t>(pos));
|
||||
const auto mask = to_underlying(pos);
|
||||
return (_data & mask) != 0;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Returns true if any of the bits are set to true.
|
||||
bool any() const noexcept
|
||||
{
|
||||
return _base::any();
|
||||
return _data != 0;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Returns true if any of the bits in the given positions are true.
|
||||
template<typename... Args, typename = std::enable_if_t<std::conjunction_v<std::is_same<Type, Args>...>>>
|
||||
bool any(const Args... positions) const noexcept
|
||||
TIL_ENUMSET_VARARG
|
||||
bool any(Args... positions) const noexcept
|
||||
{
|
||||
return (enumset{ positions... } & *this) != 0;
|
||||
const auto mask = to_underlying(positions...);
|
||||
return (_data & mask) != 0;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Returns true if all of the bits are set to true.
|
||||
bool all() const noexcept
|
||||
{
|
||||
return _base::all();
|
||||
return _data == ~underlying_type(0);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Returns true if all of the bits in the given positions are true.
|
||||
template<typename... Args, typename = std::enable_if_t<std::conjunction_v<std::is_same<Type, Args>...>>>
|
||||
bool all(const Args... positions) const noexcept
|
||||
TIL_ENUMSET_VARARG
|
||||
bool all(Args... positions) const noexcept
|
||||
{
|
||||
return (enumset{ positions... } & *this) == enumset{ positions... };
|
||||
const auto mask = to_underlying(positions...);
|
||||
return (_data & mask) == mask;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Sets the bit in the given position to the specified value.
|
||||
enumset& set(const Type pos, const bool val = true)
|
||||
// - Sets all of the bits in the given positions to true.
|
||||
TIL_ENUMSET_VARARG
|
||||
constexpr enumset& set(Args... positions) noexcept
|
||||
{
|
||||
_base::set(static_cast<size_t>(pos), val);
|
||||
_data |= to_underlying(positions...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Resets the bit in the given position to false.
|
||||
enumset& reset(const Type pos)
|
||||
// - Sets the bit in the given position to the specified value.
|
||||
constexpr enumset& set(const T pos, const bool val) noexcept
|
||||
{
|
||||
_base::reset(static_cast<size_t>(pos));
|
||||
if (val)
|
||||
{
|
||||
set(pos);
|
||||
}
|
||||
else
|
||||
{
|
||||
reset(pos);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Resets all of the bits in the given positions to false.
|
||||
TIL_ENUMSET_VARARG
|
||||
constexpr enumset& reset(Args... positions) noexcept
|
||||
{
|
||||
_data &= ~to_underlying(positions...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Flips the bit at the given position.
|
||||
enumset& flip(const Type pos)
|
||||
TIL_ENUMSET_VARARG
|
||||
constexpr enumset& flip(Args... positions) noexcept
|
||||
{
|
||||
_base::flip(static_cast<size_t>(pos));
|
||||
_data ^= to_underlying(positions...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Sets all of the bits in the given positions to true.
|
||||
private:
|
||||
template<typename... Args>
|
||||
enumset& set_all(const Args... positions)
|
||||
static constexpr underlying_type to_underlying(Args... positions) noexcept
|
||||
{
|
||||
return (..., set(positions));
|
||||
return ((underlying_type{ 1 } << static_cast<underlying_type>(positions)) | ...);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Resets all of the bits in the given positions to false.
|
||||
template<typename... Args>
|
||||
enumset& reset_all(const Args... positions)
|
||||
template<>
|
||||
static constexpr underlying_type to_underlying() noexcept
|
||||
{
|
||||
return (..., reset(positions));
|
||||
return 0;
|
||||
}
|
||||
|
||||
underlying_type _data{};
|
||||
};
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
|
|
@ -256,7 +256,7 @@ void TerminalInput::SetInputMode(const Mode mode, const bool enabled)
|
|||
// We also clear out the last saved mouse position & button.
|
||||
if (mode == Mode::DefaultMouseTracking || mode == Mode::ButtonEventMouseTracking || mode == Mode::AnyEventMouseTracking)
|
||||
{
|
||||
_inputMode.reset_all(Mode::DefaultMouseTracking, Mode::ButtonEventMouseTracking, Mode::AnyEventMouseTracking);
|
||||
_inputMode.reset(Mode::DefaultMouseTracking, Mode::ButtonEventMouseTracking, Mode::AnyEventMouseTracking);
|
||||
_mouseInputState.lastPos = { -1, -1 };
|
||||
_mouseInputState.lastButton = 0;
|
||||
}
|
||||
|
@ -265,7 +265,7 @@ void TerminalInput::SetInputMode(const Mode mode, const bool enabled)
|
|||
// when enabling a new encoding - not when disabling.
|
||||
if ((mode == Mode::Utf8MouseEncoding || mode == Mode::SgrMouseEncoding) && enabled)
|
||||
{
|
||||
_inputMode.reset_all(Mode::Utf8MouseEncoding, Mode::SgrMouseEncoding);
|
||||
_inputMode.reset(Mode::Utf8MouseEncoding, Mode::SgrMouseEncoding);
|
||||
}
|
||||
|
||||
_inputMode.set(mode, enabled);
|
||||
|
|
|
@ -24,19 +24,19 @@ class EnumSetTests
|
|||
{
|
||||
Log::Comment(L"Default constructor with no bits set");
|
||||
til::enumset<Flags> flags;
|
||||
VERIFY_ARE_EQUAL(0b00000u, flags.to_ulong());
|
||||
VERIFY_ARE_EQUAL(0b00000u, flags.bits());
|
||||
}
|
||||
|
||||
{
|
||||
Log::Comment(L"Constructor with bit 3 set");
|
||||
til::enumset<Flags> flags{ Flags::Three };
|
||||
VERIFY_ARE_EQUAL(0b01000u, flags.to_ulong());
|
||||
VERIFY_ARE_EQUAL(0b01000u, flags.bits());
|
||||
}
|
||||
|
||||
{
|
||||
Log::Comment(L"Constructor with bits 0, 2, and 4 set");
|
||||
til::enumset<Flags> flags{ Flags::Zero, Flags::Two, Flags::Four };
|
||||
VERIFY_ARE_EQUAL(0b10101u, flags.to_ulong());
|
||||
VERIFY_ARE_EQUAL(0b10101u, flags.bits());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,39 +53,39 @@ class EnumSetTests
|
|||
|
||||
Log::Comment(L"Start with no bits set");
|
||||
til::enumset<Flags> flags;
|
||||
VERIFY_ARE_EQUAL(0b00000u, flags.to_ulong());
|
||||
VERIFY_ARE_EQUAL(0b00000u, flags.bits());
|
||||
|
||||
Log::Comment(L"Set bit 2 to true");
|
||||
flags.set(Flags::Two);
|
||||
VERIFY_ARE_EQUAL(0b00100u, flags.to_ulong());
|
||||
VERIFY_ARE_EQUAL(0b00100u, flags.bits());
|
||||
|
||||
Log::Comment(L"Flip bit 4 to true");
|
||||
flags.flip(Flags::Four);
|
||||
VERIFY_ARE_EQUAL(0b10100u, flags.to_ulong());
|
||||
VERIFY_ARE_EQUAL(0b10100u, flags.bits());
|
||||
|
||||
Log::Comment(L"Set bit 0 to true");
|
||||
flags.set(Flags::Zero);
|
||||
VERIFY_ARE_EQUAL(0b10101u, flags.to_ulong());
|
||||
VERIFY_ARE_EQUAL(0b10101u, flags.bits());
|
||||
|
||||
Log::Comment(L"Reset bit 2 to false, leaving 0 and 4 true");
|
||||
flags.reset(Flags::Two);
|
||||
VERIFY_ARE_EQUAL(0b10001u, flags.to_ulong());
|
||||
VERIFY_ARE_EQUAL(0b10001u, flags.bits());
|
||||
|
||||
Log::Comment(L"Set bit 0 to false, leaving 4 true");
|
||||
flags.set(Flags::Zero, false);
|
||||
VERIFY_ARE_EQUAL(0b10000u, flags.to_ulong());
|
||||
flags.reset(Flags::Zero);
|
||||
VERIFY_ARE_EQUAL(0b10000u, flags.bits());
|
||||
|
||||
Log::Comment(L"Flip bit 4, leaving all bits false ");
|
||||
flags.flip(Flags::Four);
|
||||
VERIFY_ARE_EQUAL(0b00000u, flags.to_ulong());
|
||||
VERIFY_ARE_EQUAL(0b00000u, flags.bits());
|
||||
|
||||
Log::Comment(L"Set bits 0, 3, and 2");
|
||||
flags.set_all(Flags::Zero, Flags::Three, Flags::Two);
|
||||
VERIFY_ARE_EQUAL(0b01101u, flags.to_ulong());
|
||||
flags.set(Flags::Zero, Flags::Three, Flags::Two);
|
||||
VERIFY_ARE_EQUAL(0b01101u, flags.bits());
|
||||
|
||||
Log::Comment(L"Reset bits 3, 4 (already reset), and 0, leaving 2 true");
|
||||
flags.reset_all(Flags::Three, Flags::Four, Flags::Zero);
|
||||
VERIFY_ARE_EQUAL(0b00100u, flags.to_ulong());
|
||||
flags.reset(Flags::Three, Flags::Four, Flags::Zero);
|
||||
VERIFY_ARE_EQUAL(0b00100u, flags.bits());
|
||||
}
|
||||
|
||||
TEST_METHOD(TestMethods)
|
||||
|
@ -101,15 +101,13 @@ class EnumSetTests
|
|||
|
||||
Log::Comment(L"Start with bits 0, 2, and 4 set");
|
||||
til::enumset<Flags> flags{ Flags::Zero, Flags::Two, Flags::Four };
|
||||
VERIFY_ARE_EQUAL(0b10101u, flags.to_ulong());
|
||||
VERIFY_ARE_EQUAL(0b10101u, flags.bits());
|
||||
|
||||
Log::Comment(L"Test bits 1 and 2 with the test method");
|
||||
VERIFY_IS_FALSE(flags.test(Flags::One));
|
||||
VERIFY_IS_TRUE(flags.test(Flags::Two));
|
||||
|
||||
Log::Comment(L"Test bit 3 and 4 with the array operator");
|
||||
VERIFY_IS_FALSE(flags[Flags::Three]);
|
||||
VERIFY_IS_TRUE(flags[Flags::Four]);
|
||||
Log::Comment(L"Test bits with the any method");
|
||||
VERIFY_IS_FALSE(flags.any(Flags::One));
|
||||
VERIFY_IS_TRUE(flags.any(Flags::Two));
|
||||
VERIFY_IS_FALSE(flags.any(Flags::Three));
|
||||
VERIFY_IS_TRUE(flags.any(Flags::Four));
|
||||
|
||||
Log::Comment(L"Test if any bits are set");
|
||||
VERIFY_IS_TRUE(flags.any());
|
||||
|
@ -125,45 +123,4 @@ class EnumSetTests
|
|||
Log::Comment(L"Test if both bits 0 and 3 are set");
|
||||
VERIFY_IS_FALSE(flags.all(Flags::Zero, Flags::Three));
|
||||
}
|
||||
|
||||
TEST_METHOD(ArrayReferenceOperator)
|
||||
{
|
||||
enum class Flags
|
||||
{
|
||||
Zero,
|
||||
One,
|
||||
Two,
|
||||
Three,
|
||||
Four
|
||||
};
|
||||
|
||||
Log::Comment(L"Start with no bits set");
|
||||
til::enumset<Flags> flags;
|
||||
VERIFY_ARE_EQUAL(0b00000u, flags.to_ulong());
|
||||
|
||||
Log::Comment(L"Test bit 3 reference is false");
|
||||
auto reference = flags[Flags::Three];
|
||||
VERIFY_IS_FALSE(reference);
|
||||
VERIFY_ARE_EQUAL(0b00000u, flags.to_ulong());
|
||||
|
||||
Log::Comment(L"Set bit 3 reference to true");
|
||||
flags.set(Flags::Three);
|
||||
VERIFY_IS_TRUE(reference);
|
||||
VERIFY_ARE_EQUAL(0b01000u, flags.to_ulong());
|
||||
|
||||
Log::Comment(L"Reset bit 3 reference to false");
|
||||
flags.reset(Flags::Three);
|
||||
VERIFY_IS_FALSE(reference);
|
||||
VERIFY_ARE_EQUAL(0b00000u, flags.to_ulong());
|
||||
|
||||
Log::Comment(L"Flip bit 3 reference to true");
|
||||
reference.flip();
|
||||
VERIFY_IS_TRUE(reference);
|
||||
VERIFY_ARE_EQUAL(0b01000u, flags.to_ulong());
|
||||
|
||||
Log::Comment(L"Flip bit 3 reference back to false");
|
||||
reference.flip();
|
||||
VERIFY_IS_FALSE(reference);
|
||||
VERIFY_ARE_EQUAL(0b00000u, flags.to_ulong());
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue