As was pointed out in the last post, I… ahem… forgot to actually put down some working examples of the flag set, so I ended up describing something but not actually showing off the final product.
So I thought a good start would be to include a few of the flag set unit tests (since unit tested code is known to be good ‘working’ documentation in itself) followed by a simple working example based on the examples used at the start of the last post.
So, here’s a load of code that you can feast you eyes on (the unit test library being used here is UnitTest++)
struct TestFlagSet_Small { enum Enum { Flag1, Flag2, Flag3, Flag4, Noof, }; }; struct TestFlagSet_Large { enum Enum { Flag1, Flag2, Flag3, Flag4, Flag5, Flag6, Flag7, Flag8, Flag9, Flag10, Flag11, Flag12, Noof, }; }; class ExampleFixture { public: // Define a couple of flags, one defaulting to 8 flags flag_set<TestFlagSet_Small::Enum> smallFlagSet; flag_set<TestFlagSet_Large::Enum, TestFlagSet_Large::Noof> largeFlagSet; ExampleFixture(void) { } ~ExampleFixture(void) { } }; // Checks that the flag set starts in an empty state TEST_FIXTURE(ExampleFixture, InitialFlagSet_Empty) { CHECK_EQUAL(smallFlagSet.is_clear(), TRUE); CHECK_EQUAL(largeFlagSet.is_clear(), TRUE); } // Tests that compilation works when defining flags with sizes < 8 TEST(DefaultTemplateSet) { flag_set<TestFlagSet_Small::Enum, TestFlagSet_Small::Noof> smallFlagSet; flag_set<TestFlagSet_Large::Enum, TestFlagSet_Large::Noof> largeFlagSet; } // Checks if the flag set is clear when flag set removed TEST_FIXTURE(ExampleFixture, SetThenRemoveIsClear) { smallFlagSet.set(TestFlagSet_Small::Flag1); smallFlagSet.remove(TestFlagSet_Small::Flag1); CHECK_EQUAL(smallFlagSet.is_clear(), TRUE); } // Checks if the flag set is clear when cleared or no flags are set TEST_FIXTURE(ExampleFixture, ClearSetIsClear) { smallFlagSet.set(TestFlagSet_Small::Flag1); smallFlagSet.clear(); CHECK_EQUAL(smallFlagSet.is_clear(), TRUE); } // Checks if the set function correctly sets the right flags TEST_FIXTURE(ExampleFixture, SetFlagSetIsSet_IsSet) { smallFlagSet.set(TestFlagSet_Small::Flag1); smallFlagSet.set(TestFlagSet_Small::Flag4); CHECK_EQUAL(smallFlagSet.is_set(TestFlagSet_Small::Flag1), TRUE); CHECK_EQUAL(smallFlagSet.is_set(TestFlagSet_Small::Flag4), TRUE); } // Check that returned C type is correctly set TEST_FIXTURE(ExampleFixture, AsCTypeFunction) { smallFlagSet.set(TestFlagSet_Small::Flag1); smallFlagSet.set(TestFlagSet_Small::Flag2); smallFlagSet.set(TestFlagSet_Small::Flag4); // as_c_type allows the flag set to be explicitly passed // through to legacy functions and for the underlying // datatype to be stored as a base type is needed const uint8* flagSetBits = smallFlagSet.as_c_type(); CHECK( (*flagSetBits) & (1<<TestFlagSet_Small::Flag1) ); CHECK( (*flagSetBits) & (1<<TestFlagSet_Small::Flag2) ); CHECK( (*flagSetBits) & (1<<TestFlagSet_Small::Flag3) == FALSE ); CHECK( (*flagSetBits) & (1<<TestFlagSet_Small::Flag4) ); } // Test that setting from a C type correctly sets the flags TEST_FIXTURE(ExampleFixture, FromCTypeFunction) { smallFlagSet.set(TestFlagSet_Small::Flag1); smallFlagSet.set(TestFlagSet_Small::Flag2); smallFlagSet.set(TestFlagSet_Small::Flag4); const uint8 tempStorage = *(smallFlagSet.as_c_type()); smallFlagSet.clear(); smallFlagSet.from_c_type(&tempStorage); CHECK_EQUAL(smallFlagSet.is_set(TestFlagSet_Small::Flag1), FALSE); CHECK_EQUAL(smallFlagSet.is_set(TestFlagSet_Small::Flag2), FALSE); CHECK_EQUAL(smallFlagSet.is_set(TestFlagSet_Small::Flag4), FALSE); } // And a (very) simple real world example // Define a couple of flag types struct BattleProperties { enum Enum { HasTarget, CarryingWeapon, }; }; struct CharacterState { enum Enum { IsStunned, IsPoisoned, }; }; // Create a flag set for the character states typedef flag_set<CharacterState::Enum> StateFlags; StateFlags m_currentStateFlags; void UpdateCharacter(void) { // We’ve been stunned m_currentStateFlags.set(CharacterState::IsStunned); // ... do some more stuff // Whoops, I would have just made a mistake, but I can’t do this // m_currentStateFlags.set(BattleProperties::CarryingWeapon); // Update what ever has happened UpdateState(m_currentStateFlags); // We’ve dealt with our states now m_currentStateFlags.clear(); } void UpdateState(const StateFlags& state) { // Are we stunned? if (state.is_set(CharacterState::IsStunned) { } // Another mistake I can no longer do // if (state.is_set(BattleProperties::CarryingWeapon) // { // } }