Skip to content

Commit

Permalink
add EnumBitset for bitmask enumerations
Browse files Browse the repository at this point in the history
  • Loading branch information
scheffle committed Mar 15, 2024
1 parent 4171acb commit a2ee09d
Show file tree
Hide file tree
Showing 2 changed files with 168 additions and 54 deletions.
34 changes: 28 additions & 6 deletions vstgui/lib/enumbitset.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace VSTGUI {
//------------------------------------------------------------------------
/** enum class bitset
*
* example:
* example with an integer sequence enumeration:
*
* enum class Flag {
* One,
Expand All @@ -24,10 +24,27 @@ namespace VSTGUI {
* Flags f;
* f = Flag::One
* f |= Flag::Two;
* if (f & Flag::One) {}
* if (f & Flag::One)
* {
* }
*
* example with an enumeration where the elements are bitmasks:
*
* enum class Flag {
* One = 1 << 0,
* Two = 1 << 1,
* Three = 1 << 2
* };
* using Flags = EnumBitSet<Flag, true>
* Flags f;
* f = Flag::One
* f |= Flag::Two;
* if (f & Flag::One)
* {
* }
*
*/
template<typename Enum>
template<typename Enum, bool EnumIsBitMask = false>
struct EnumBitset
{
using value_type = std::underlying_type_t<Enum>;
Expand All @@ -49,8 +66,8 @@ struct EnumBitset
constexpr void remove (Enum e) { val &= ~to_value_type (e); }
constexpr void clear () { val = {}; }

constexpr bool test (Enum e) const { return (val & to_value_type (e)) != 0; }
constexpr bool empty () const { return val == 0; }
constexpr bool test (Enum e) const { return (val & to_value_type (e)) != value_type {}; }
constexpr bool empty () const { return val == value_type {}; }

constexpr EnumBitset& operator= (Enum e)
{
Expand Down Expand Up @@ -106,7 +123,12 @@ struct EnumBitset
constexpr value_type value () const { return val; }

private:
constexpr value_type to_value_type (Enum e) const { return 1 << static_cast<value_type> (e); }
constexpr value_type to_value_type (Enum e) const
{
if constexpr (EnumIsBitMask)
return static_cast<value_type> (e);
return 1 << static_cast<value_type> (e);
}

value_type val {};
};
Expand Down
188 changes: 140 additions & 48 deletions vstgui/tests/unittest/lib/enumbitset_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ enum class Flag
};
using Flags = EnumBitset<Flag>;

//------------------------------------------------------------------------
enum class FlagMask : uint8_t
{
One = 1 << 0,
Two = 1 << 1,
Three = 1 << 2,
};
using FlagMasks = EnumBitset<FlagMask, true>;

//------------------------------------------------------------------------
TEST_CASE (EnumBitsetTest, InitTest)
{
Expand All @@ -32,81 +41,164 @@ TEST_CASE (EnumBitsetTest, InitTest)
Flags f ({Flag::One, Flag::Two});
EXPECT_EQ (f.value (), 3);
}
{
FlagMasks f;
EXPECT_EQ (f.value (), 0);
}
{
FlagMasks f (FlagMask::Two);
EXPECT_EQ (f.value (), 2);
}
{
FlagMasks f ({FlagMask::One, FlagMask::Two});
EXPECT_EQ (f.value (), 3);
}
}

//------------------------------------------------------------------------
TEST_CASE (EnumBitsetTest, AssignmentTest)
{
Flags f;
f = Flag::One;
EXPECT_EQ (f.value (), 1);
{
Flags f;
f = Flag::One;
EXPECT_EQ (f.value (), 1);

Flags f2 = Flag::Two;
EXPECT_EQ (f2.value (), 2);
Flags f2 = Flag::Two;
EXPECT_EQ (f2.value (), 2);

f = f2;
EXPECT_EQ (f.value (), f2.value ());
f = f2;
EXPECT_EQ (f.value (), f2.value ());

f2.exlusive (Flag::Three);
EXPECT_EQ (f2.value (), 4);
f2.exlusive (Flag::Three);
EXPECT_EQ (f2.value (), 4);
}
{
FlagMasks f;
f = FlagMask::One;
EXPECT_EQ (f.value (), 1);

FlagMasks f2 = FlagMask::Two;
EXPECT_EQ (f2.value (), 2);

f = f2;
EXPECT_EQ (f.value (), f2.value ());

f2.exlusive (FlagMask::Three);
EXPECT_EQ (f2.value (), 4);
}
}

//------------------------------------------------------------------------
TEST_CASE (EnumBitsetTest, AddRemoveTest)
{
Flags f;
f.add (Flag::One);
EXPECT_EQ (f.value (), 1);
f.add (Flag::Two);
EXPECT_EQ (f.value (), 3);
f.remove (Flag::One);
EXPECT_EQ (f.value (), 2);

f.clear ();

f |= Flag::One;
EXPECT_EQ (f.value (), 1);
f |= Flag::Two;
EXPECT_EQ (f.value (), 3);

f ^= Flag::One;
EXPECT_EQ (f.value (), 2);

f.clear ();

f << Flag::One;
EXPECT_EQ (f.value (), 1);
f << Flag::Two;
EXPECT_EQ (f.value (), 3);
f >> Flag::One;
EXPECT_EQ (f.value (), 2);
{
Flags f;
f.add (Flag::One);
EXPECT_EQ (f.value (), 1);
f.add (Flag::Two);
EXPECT_EQ (f.value (), 3);
f.remove (Flag::One);
EXPECT_EQ (f.value (), 2);

f.clear ();

f |= Flag::One;
EXPECT_EQ (f.value (), 1);
f |= Flag::Two;
EXPECT_EQ (f.value (), 3);

f ^= Flag::One;
EXPECT_EQ (f.value (), 2);

f.clear ();

f << Flag::One;
EXPECT_EQ (f.value (), 1);
f << Flag::Two;
EXPECT_EQ (f.value (), 3);
f >> Flag::One;
EXPECT_EQ (f.value (), 2);
}
{
FlagMasks f;
f.add (FlagMask::One);
EXPECT_EQ (f.value (), 1);
f.add (FlagMask::Two);
EXPECT_EQ (f.value (), 3);
f.remove (FlagMask::One);
EXPECT_EQ (f.value (), 2);

f.clear ();

f |= FlagMask::One;
EXPECT_EQ (f.value (), 1);
f |= FlagMask::Two;
EXPECT_EQ (f.value (), 3);

f ^= FlagMask::One;
EXPECT_EQ (f.value (), 2);

f.clear ();

f << FlagMask::One;
EXPECT_EQ (f.value (), 1);
f << FlagMask::Two;
EXPECT_EQ (f.value (), 3);
f >> FlagMask::One;
EXPECT_EQ (f.value (), 2);
}
}

//------------------------------------------------------------------------
TEST_CASE (EnumBitsetTest, OperatorTest)
{
Flags f1 = Flag::One;
Flags f2 ({Flag::Two, Flag::Three});
{
Flags f1 = Flag::One;
Flags f2 ({Flag::Two, Flag::Three});

auto f3 = f1 | Flag::Two;
EXPECT_EQ (f3.value (), 3);

EXPECT_TRUE (f3 & Flag::One);
EXPECT_TRUE (f3 & Flag::Two);
}
{
FlagMasks f1 = FlagMask::One;
FlagMasks f2 ({FlagMask::Two, FlagMask::Three});

auto f3 = f1 | Flag::Two;
EXPECT_EQ (f3.value (), 3);
auto f3 = f1 | FlagMask::Two;
EXPECT_EQ (f3.value (), 3);

EXPECT_TRUE (f3 & Flag::One);
EXPECT_TRUE (f3 & Flag::Two);
EXPECT_TRUE (f3 & FlagMask::One);
EXPECT_TRUE (f3 & FlagMask::Two);
}
}

//------------------------------------------------------------------------
TEST_CASE (EnumBitsetTest, EqualityTest)
{
Flags f1 ({Flag::One, Flag::Three});
Flags f2 ({Flag::Two, Flag::Three});
Flags f3 ({Flag::Three, Flag::One});
{
Flags f1 ({Flag::One, Flag::Three});
Flags f2 ({Flag::Two, Flag::Three});
Flags f3 ({Flag::Three, Flag::One});

EXPECT_TRUE (f1 != f2);
EXPECT_TRUE (f1 == f3);
EXPECT_TRUE (f1 != f2);
EXPECT_TRUE (f1 == f3);

EXPECT_TRUE (f1.test (Flag::One));
EXPECT_FALSE (f1.test (Flag::Two));
EXPECT_TRUE (f1.test (Flag::One));
EXPECT_FALSE (f1.test (Flag::Two));
}
{
FlagMasks f1 ({FlagMask::One, FlagMask::Three});
FlagMasks f2 ({FlagMask::Two, FlagMask::Three});
FlagMasks f3 ({FlagMask::Three, FlagMask::One});

EXPECT_TRUE (f1 != f2);
EXPECT_TRUE (f1 == f3);

EXPECT_TRUE (f1.test (FlagMask::One));
EXPECT_FALSE (f1.test (FlagMask::Two));
}
}

//------------------------------------------------------------------------
Expand Down

0 comments on commit a2ee09d

Please sign in to comment.