C++, FTL, Programming

Debugging Type Safe Flags

In the previous post I described how I implemented a type safe, template driven, system for declaring and using flags in C++. But one of the main issues with the solution was not being able to see what flags were set when debugging.

Which Flags Are Set?
When setting bits to store flags there are varying levels of output you can get from them in the debugger.

One of them absolutely sucks – you don’t want to be doing bit arithmetic to know whats been set

The next one is better, it gives you some idea of what’s been set, but not much.

Finally, there is the following, which is much better.

A normal way of getting this behaviour out of the debugger when using base type flags is declaring them alongside a structure containing the individual flag entries inside a union.

struct  StateFlags
{
  union
  {
    uint m_allFlags;
    struct
    {
      uint HasTarget:1;
      uint CarryingWeapon:1;
      uint Drugged:1;
    } m_names;
  };
};

This provides you with the best possible output in the debugger so it’s something I wanted to add to the flag set to make debugging much easier.

Flag Set ‘Names’
Since this is an interface driven implementation the ideal syntax starts out as the following

// The enums used to define the flag set
enum StateFlags
{
  HasTarget,
  CarryingWeapon,
  Drugged,
};

// The ‘names’ we’d like to be displayed in the debugger
struct StateNames
{
  uint HasTarget;
  uint CarryingWeapon;
  uint Drugged;
};

We need to be able to pass these names through to the container, and since we want the type defined on a per flag basis we can add these names to the flag set definition.

template <typename TEnum, int TMaxFlags, typename TNames>
class flag_set

Since we already have our own defined type to store the flags being set and we know that we can use a union to combine the storage type and the flag names, we simply append our name type along side the storage type so when a flag is set the name is automatically set alongside it.

template< int TMaxFlags, typename TNames >
struct bit_union
{
  // Container type with enough bits to store TMaxFlags flags
  typedef bit_set<TMaxFlags> container_type;
  typedef TNames name_type;

  union
  {
    container_type m_type;
    name_type m_name;
  };
};

I’ve extracted the bit_set into it’s own structure rather than including it directly inside the union for two reasons

  • It allows the bit set to be specialised based on the number of flags without worrying about the union. If we had a large number of specilised storage structures, we’d have to duplicate the union inside each one.
  • It allows us to strip out the union and the names in builds that don’t need it, without affecting the speclialised bit sets.

So the flag set now contains user defined names without having to worry about how they are implemented.  The templated utility functions used to set, remove and check the flags also don’t need to know anything about it so aren’t effected.

template < typename TEnum, int TMaxFlags, typename TNames >
class flag_set
{
public:
  void set(const TEnum flag)
  {
    // The utility functions still take the same
    // type regardless of the name union
    set_bit_entry(m_unionFlags.m_type.m_bitArray, flag, true)
  }

private:
  static const uint BitSetSize = ((( TMaxFlags + 7 ) & ~7 ) >> 3)*8;
  bit_union< BitSetSize, TNames > m_unionFlags;
};

Now we can simply define our flag set with a unique set of names and in the debugger we get exactly what we we’re looking for (annoyingly we can’t get around the need to define the integers in the structure as one bit entries if we’re going to add them to the union).

struct MyFlags
{
  enum Enum
  {
    UnderAttack,
    InCover,
    // etc.

    Noof,
  };

  struct Names
  {
    uint UnderAttack:1;
    uint InCover:1;
    // etc.
  };
};

flag_set<MyFlags::Enum, MyFlags::Noof, MyFlags::Names> flagSet;

flagSet.set(MyFlags::InFormation);
flagSet.set(MyFlags::KnockedDown);

Default Names
But we have one problem.  We don’t want to force people to have flag names if they don’t want them. Depending on the type and volatility of the flags they might not need the names in the debugger and it can be a lot of code to add for every single flag set in use.  And since we have a default template parameter for the number of flags, we need to provide a similar default parameter for the names.

Defining the default parameters isn’t much of a problem.  We can quickly define union name companions for 8+ flags (a few small macros make this much easier) but how to drop them into the flag set template as a multiple of 8?

Since we already calculate our multiple of 8 flag count when defining the bit_union type, we can do the same when declaring the default name structure.  But we certainly don’t want such an over-complicated calculation twice in the class.

Since we need the multiple of 8 value outside the class in the template declaration, we can’t just declare it inside the class, so by storing it in it’s own type we can use it where-ever we need it.

template<uint TSize>
struct bit_set_size
{
  enum
  {
    Value = ((( TSize + 7 ) & ~7 ) >> 3)*8,
  };
};

We can now use this to declare our default flag set name where union names is correctly specialised depending on how many flags the user has declared.

template <
           typename TEnum,
           int TMaxFlags = 8,
           typename TNames = union_names<bit_set_size<TMaxFlags>::Value>
         >
class flag_set
{
private:
  bit_union< bit_set_size<TMaxFlags>::Value > m_unionFlags;
};

So now a user can simply declare their flag set with the enum type and by default they will get something out of the debugger that’s more useful than just showing the single base type value.

flag_set<MyFlags::Enum> flagSet;

flagSet.set(MyFlags::InFormation);
flagSet.set(MyFlags::KnockedDown);

Flag Set Drawbacks
In adding custom names to the flag set, we’ve added a number of new templated structures.  The default name types, the bit_set_size and the bit_union and all this comes with a cost.  Obviously we’re generating a number of structures for all the flags that use this system and it certainly does effect compilation speed.

Based on compilation speeds of a couple of projects that use the flag set heavily over a few months, I’d estimate that adding the flag set (before the debug names were added) put an extra 20-30 seconds on a full rebuild.  After adding the debug names, it probably added another 30 seconds or more, bringing a rebuild of the entire project up from 3 minutes to around 4.

But I can live with that for a number of reasons

  • The flag set has introduced a more type safe environment for developers.  On a project that has a number of developers dipping into it, this is vital and does reduce the possibility of human errors causing simple but difficult to track down bugs
  • The code is clearer and easier to read, maintain and debug.  This is a bonus for all the same reasons above.
  • I rarely rebuild the entire project.  A well structured project reduces the number of rebuilds greatly and as such the longer rebuilds actually don’t effect me that much (an average build usually takes <10 seconds)

There’s one more thing I want to look in regards to the flag set, and that’s how we can take the basic template declaration and simplify it so users can quickly and easily declare a flag set with a large number of flags and their own flag names within a single structure.

But I’ll cover that in another post.

4 thoughts on “Debugging Type Safe Flags”

  1. Thank you for those explanations on type safe flags. I tried to implement your technique (from previous post and this one), but there are two things that I can’t seem to do correctly :

    1 – the m_bitArray member from the struct bit_set on my compiler(MSVC10) is not initialized to 0 automatically, and I can’t put a constructor for doing it as the union is used in another union. How did you initialize it to 0 at initialization ?

    2 – I’m not sure about the part with the “union_names” in this post. Where this come from ? I think that you forgot to put it and I don’t see how you did it.

  2. Hi Skenizen. Sorry for not replying sooner it’s been a very hectic few weeks at work!

    I had to strip back a lot of the code in these examples so it was easier to read in a blog post which is probably where some of your questions come from.

    Regarding the initialisation of the m_bitArray. You’re correct it cannot be initialised to 0 on creation due to the use of Unions. I simply get around that by having additional utility functions (in the same scope as set_bit_entry) which clear the arrays as needed.

    flag_set(void)
    {
      reset_bit_array(m_unionFlags.m_type.m_bitArray);
    }
    

    It does mean we have to make explicit calls to this utility in any constructors we have in the flag_set but it’s the only way to get around the fact we’re using quite a few unions in the implementation.

    The union_name structure is a (large number) of default structures which are used if you don’t pass in your own name structure (this allows the m_names.Flags1, m_names.Flag2 entries to work automatically).

    Generating these structures is not pretty which is why I didn’t add much information about them to the blog post. But they look something like the following

    template
    struct bit_union_names
    {
    };
    
    template
    struct union_names
    {
      FTL_DECLARE_BIT_ENTRIES_8( 1, 2, 3, 4, 5, 6, 7, 8 )
    };
    
    template
    struct union_names
    {
      FTL_DECLARE_BIT_ENTRIES_8( 1, 2, 3, 4, 5, 6, 7, 8 )
      FTL_DECLARE_BIT_ENTRIES_8( 9, 10, 11, 12, 13, 14, 15, 16 )
    };
    

    I generate these all the way down to union_names. Since we generate the SizeT value in the flag_set template parameters we only have to generate entries for multiples of 8 and not every value between 1 to 128.

    The DECLARE_BIT macro looks something like this

    #define FTL_DECLARE_BIT_ENTRIES_8(b1, b2, b3, b4, b5, b6, b7, b8)
    	unsigned int	m_bitEntry##b1	:1;
    	unsigned int	m_bitEntry##b2	:1;
    	unsigned int	m_bitEntry##b3	:1;
    	unsigned int	m_bitEntry##b4	:1;
    	unsigned int	m_bitEntry##b5	:1;
    	unsigned int	m_bitEntry##b6	:1;
    	unsigned int	m_bitEntry##b7	:1;
    	unsigned int	m_bitEntry##b8	:1;
    

    It’s not pretty but it’s a quick way to generate a large number of structures quickly. I could have reduced the amount of code using a combination of macros and template metaprogramming, but simplicity is always better if you can have it imo.

    Hope that all makes sense! Let me know how you get on 🙂

  3. hello, thanks for the answer. That’s what I was thinking for the initialization. I was just hoping there would be a solution I missed for initializing it another way as the array initialization require a loop or a memset at runtime.

    I just have a remark to add about this function :
    // Specilised for a bit count of 8 – no arrays, no additional calculations
    template
    void set_bit_entry< bit_set >(bit_set& flags, const uint entry)
    {
    flags.m_bitArray |= 1 << entry;
    }

    If I'm not wrong this should be an overloaded function instead of a template specialization as I don't think that it respect c++ standard and would works outside of Visual Studio.

    void set_bit_entry(bit_set& flags, const uint entry)
    {
    flags.m_bitArray |= 1 << entry;
    }

  4. The utility functions (set_bit_entry etc,) are not member functions of the flag_set, they are just normal functions in another namespace meaning they can be template specialisations rather than overloaded functions.

    That’s inline with the C++ standard and works on any standards compliant compiler.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s