Enum Madness!

Last time I wrote a class for a bitfield and a macro that allowed us to automatically size the bitfield storage based on size of flag set and platform storage sizes.  And that’s great and all, but we only went from something like Bitflag<unsigned int> m_flags to something like Bitflag<BITFLAG_SIZE(28)> m_flags.  It’s better from the perspective of what I was writing about in that last post, but you still have a hardcoded 28 sitting there, completely detached from the flag set it is trying to describe.  Wouldn’t it be great if you could feed your flag set directly into the BITFLAG_SIZE macro, and then changes to the flag set automatically change the macro, which changes the type?  Why, yes, yes it would.

So, can we do it?  Absolutely!  But, before we get to that answer, we’re going to take a detour through enums.

I love enums.  Especially unscoped enums wrapped inside of their own namespaces.  I use them all the time.  Most of that usage is to describe entries in a statically sized array, along the lines of this:

namespace CommandListLayers
{
  enum Layer
  {
    Read,
    Pivot,
    Write,
    NumberOf
  };
}

I have a system that uses an array of 3 command lists to move render data between the game thread and the render thread; the read layer, the write layer, and the pivot layer.  I can initialize the array with CommandListLayers::NumberOf, and any code I write can deal with each of the layers via the associated enumerator.  It’s clean and it’s easy.  I really like the NumberOf trick, but it’s also kind of tedious to always insert it at the end.  Or, being the stupid human that I am, maybe I forget to do it.  Who knows!  So, one day I came up with this dumb macro.

#define ARRAY_ENUM_BUILDER(name1, name2, ...) \
  namespace name1 { \
    enum name2 { \
      __VA_ARGS__, \
      NumberOf \
    }; \
  }

And that allows the enum declaration to look like this.

ARRAY_ENUM_BUILDER(
  CommandListLayers, Layer, 
  Read,
  Pivot,
  Write
)

So, I declared the namespace and enum name, all enumerator entries, and the NumberOf gets tacked onto the end with the correct value.  Great!  So, what does this have to do with my Bitflag class and the BITFLAG_SIZE macro?  Well, given the enum stuff I was already doing in my code, the next logical step was to do something like this.

namespace ProcessToggleFlags
{
  enum Flags
  {
    Lighting = (1 << 0),
    DepthOfField = (1 << 1),
    Wireframe = (1 << 2),
    DebugLines = (1 << 3),
    DebugText = (1 << 4),
    NumberOf = 5
  };
}

So, this is a little less great.  I have to manually assign a value to NumberOf since it would otherwise take the next integral value beyond (1 << 4), which isn’t 5, which is what I would want NumberOf to be in this case.  So, the macro as it exists for building enums that describe arrays isn’t going to cut it.  That’s where this template code comes in!

namespace enum_helpers
{
  template <unsigned long long T>
  struct enum_size
  {
    static const unsigned long long count = enum_size<(T >> 1)>::count + 1;
  };

  template <>
  struct enum_size<0>
  {
    static const unsigned long long count = 0;
  };
}

Using that template code, I can now create a new macro specifically to build enums for bitfields.  And that macro looks like this.

#define BITFLAG_ENUM_BUILDER(name1, name2, ...) \
  namespace name1 { \
    enum name2 : unsigned long long { \
      __VA_ARGS__, \
      Last, \
      NumberOf = enum_helpers::enum_size<Last - 1>::count \
    }; \
  }

Which allows my enum to becomes this.

BITFLAG_ENUM_BUILDER(
  ProcessToggleFlags, Flags, 
  Lighting = (1 << 0),
  DepthOfField = (1 << 1),
  Wireframe = (1 << 2),
  DebugLines = (1 << 3),
  DebugText = (1 << 4)
)

It works more or less the same as the ARRAY_ENUM_BUILDER macro, but it tacks on a Last entry with the sole purpose of being the parameter into the enum_size template, giving us the correct value for NumberOf when dealing with bit flags.  So, that’s great, right?  Now we can move from Bitflag<BITFLAG_SIZE(28)> m_flags to Bitflag<BITFLAG_SIZE(ProcessToggleFlags::NumberOf)> m_flags.  And that’s definitely better.  More or less where I want to be with this.

However, there are still some things I’d like to improve.  It would be nice if I could get the element count from __VA_ARGS__ or iterate over the contents.  Then I could generate the type of the enum, insert the shift values, and any necessary suffixes on the 1’s from inside the macro.  And it isn’t like it’s completely undoable, but it moves into the realm of really gross macro code.  Code I will probably end up writing for myself, but it’s not overly scaleable so I didn’t want to include it here.

But, that’s it.  This template and 2 macros make my life a lot easier.  Hopefully it will do the same for someone else.  Here is the download link if you want all of the source: EnumHelpers.hpp.  And if anyone has suggestions for improvements (especially for doing the things I mentioned in the previous paragraph!), I’d love to hear those.

Until next time!

Oh, Hey There: The Return (Also, I Do Things With Templates And Bitfields That May Or May Not Be Dumb)

Going long periods between blog posts has always been fairly par for the course for me, but this last break was pretty excessive even by my standards.  Two years is really just too long!  Of course, shortly after that last post I ended up getting a job at 343 Industries (which is, incidentally, amazing!) and I can’t really blog about what I do at work, which lead to said hiatus.  What are you going to do?  Go two years between blog posts, apparently.

Anyway, the last two years have predominantly been writing HLSL, and that’s been great and I love my job, but recently I’ve been starting to worry that my C++ is getting rusty.  Can’t let that happen, how would I feel superior to other programmers if I’m not pro at C++?  Right?  Right!  So, with Halo 5 shipped I’ve had a little more free time and a thought occurred to me that got me jump started back into writing some “real” code.  It’s a little bit of utility around a bitfield class, and I think that it’s useful, but I also couldn’t find any real reference to anyone else having done it.  That either means that I’m an absolute genius or there are very legitimate reasons why no one does what I’m about to show and I’m just not seeing them.  Which feels pretty likely, but you tell me!

So, I started with a bitfield class that just used an unsigned int as storage and had pretty basic bit and bulk getters and setters.  Nothing super fancy, but effective for what I was doing with it.  The next logical step was to template the storage type, and that was easy, which gave me the following code.

template <typename t_field>
class Bitflag
{
public:
  //Default Constructor
  Bitflag() : m_flags(0) {}

  //Initial Value Constructor
  Bitflag(t_field pFlags) : m_flags(pFlags) {}

  //Bit Get
  bool Get(t_field pIndex) const {
    return ((m_flags & pIndex) == 0) ? false : true;
  }

  //Bulk Get
  t_field Get() const {
    return m_flags;
  }

  //Bit Set
  void Set(t_field pIndex, bool pState) {
    // Optimized based on information found at 
    // https://graphics.stanford.edu/~seander/bithacks.html#ConditionalSetOrClearBitsWithoutBranching
    // Safe to squelch this warning

    #pragma warning(push)
    #pragma warning(disable : 4804)

    m_flags = (m_flags & ~pIndex) | (-pState & pIndex);

    #pragma warning(pop)
  }

  //Bulk Set
  void Set(t_field pFlags) {
    m_flags = pFlags;
  }
private:
  t_field   m_flags;
};

And for the purpose of what I’m actually blogging about, the Bitflag class never actually gets any fancier or more complicated.  Instead, I looked at what I had, and I realized that rather than really using the flexibility of the templated type to optimize storage size for the class, I just got lazy and slapped every usage with unsigned int.  Which just took me back to where I was before I even templated the class.  The hell, right?  This lead me to the question, “Could I write code that, given the size of the flag set I want to be able to store in a Bitflag, would always set the templated type to the smallest appropriate type?”  And if the answer was yes, then it could serve a few purposes; actually optimize my storage size, automatically change if necessary as the size of the represented flag set changed, automatically change if necessary as I compiled on other platforms where storage sizes might be different.  That sounded great, so I dove into it, and it turns out that the answer is indeed yes.

I’ll start with the code that I ended up writing, and then I’ll explain what it’s doing, why I had it do that, and where it might go next.

#define BITFLAG_SIZE(val) BitflagHelpers::bitflag_type_selector<val>::value_type

namespace BitflagHelpers
{
  static const int g_bits_per_byte        = 8;

  static const int g_undefined_ushort     = -1;
  static const int g_undefined_uint       = -2;
  static const int g_undefined_ulong      = -3;
  static const int g_undefined_ulonglong  = -4;

  template <int t>
  struct bitflag_type
  {
    typedef int type;
  };

  // Be careful with this case when it comes to serialization
  template <>
  struct bitflag_type<sizeof(unsigned char) * g_bits_per_byte>
  {
    typedef unsigned char type;
  };

  // We protect from doubled specialization in the case that a type is 
  // the same size as the previous type by setting that instantiation 
  // to a negative global value.  Remove the warning for specing a 
  // signed value into an unsigned type.  Do something better later?
  #pragma warning(push)
  #pragma warning(disable : 4309)

  template <>
  struct bitflag_type<(sizeof(unsigned short) != sizeof(unsigned char)) 
    ? (sizeof(unsigned short) * g_bits_per_byte) : (g_undefined_ushort)>
  {
    typedef unsigned short type;
  };

  template <>
  struct bitflag_type<(sizeof(unsigned int) != sizeof(unsigned short))
    ? (sizeof(unsigned int) * g_bits_per_byte) : (g_undefined_uint)>
  {
    typedef unsigned int type;
  };

  template <>
  struct bitflag_type<(sizeof(unsigned long) != sizeof(unsigned int))
    ? (sizeof(unsigned long) * g_bits_per_byte) : (g_undefined_ulong)>
  {
    typedef unsigned long type;
  };

  template <>
  struct bitflag_type<(sizeof(unsigned long long) != sizeof(unsigned long))
    ? (sizeof(unsigned long long) * g_bits_per_byte) : (g_undefined_ulonglong)>
  {
    typedef unsigned long long type;
  };

  #pragma warning(pop)

  template <int t>
  struct bitflag_type_selector
  {
    typedef 
      typename std::conditional<(t <= sizeof(unsigned char) * g_bits_per_byte), 
        bitflag_type<sizeof(unsigned char) * g_bits_per_byte>::type,
      typename std::conditional<(t <= sizeof(unsigned short) * g_bits_per_byte), 
        bitflag_type<sizeof(unsigned short) * g_bits_per_byte>::type,
      typename std::conditional<(t <= sizeof(unsigned int) * g_bits_per_byte), 
        bitflag_type<sizeof(unsigned int) * g_bits_per_byte>::type,
      typename std::conditional<(t <= sizeof(unsigned long) * g_bits_per_byte), 
        bitflag_type<sizeof(unsigned long) * g_bits_per_byte>::type,
      bitflag_type<sizeof(unsigned long long) * g_bits_per_byte>::type>::type>::type>::type>::type value_type;
  };
}

So, the idea is that when you have a Bitflag variable, rather than specifying a type, you give it the BITFLAG_SIZE macro with the size of the flag set you want to be able to store.  So, instead of something like Bitflag<unsigned int> flags, you’d write something like Bitflag<BITFLAG_SIZE(28)> flags.  Under the hood, the macro uses a set of templates that take advantage of the C/C++ language standard that doesn’t define specific sizes for unsigned integral types, just relations; it says that unsigned char <= unsigned short <= unsigned int <= unsigned long <= unsigned long long.  Everything else works because of those relationships.

The bitflag_type specializations all check to make sure that any two adjacent types don’t have the same size, and set the specialization to a special value in that case to prevent double specialization, which would cause a compile failure.  In the case of Win64, unsigned int and unsigned long are both 32 bits, so the unsigned long spec ends up being -3 instead of 32.  And then it never gets used as a result, which is perfectly fine.

The final piece was the bitflag_type_selector, which makes use of std::conditional to allow the template specializations to be assigned to ranges.  Without that, it’d be pretty tedious to write all the code that’d allow anything but exact size matches to the types to be paired to the proper specialization of bitflag_type.  So, yay for std::conditional!

One thing to watch out for here is data serialization for a Bitflag that’s using an unsigned char for its storage.  Take the case of a flag mask of 33.  That will get serialized as 33 by any basic serialization scheme for an unsigned short, unsigned int, unsigned long, or unsigned long long, which is great.  But, for an unsigned char, it will see 3 and 3, which probably isn’t what you wanted.  It’s solvable for sure, but I feel it’s worth mentioning.  I did look into using uint8_t, but it turns out that this is very implementation specific, and a lot of implementations are just typedef’s of unsigned char anyway.

While not a requirement of this setup by any means, I like to store my flag sets in enumerations.  So, for me, the next step was to be able to feed the enumeration into the BITFLAG_SIZE macro and always get the right size.  I ended up doing that (with more templates), and that will be the subject of the next blog post.  One that hopefully comes sooner than this one did!  I guess we’ll see, I tend to have a problem keeping up with my desired posting schedule.

But that is the end of this post.  Hopefully you found this useful, and hopefully I’m not insane and/or stupid.  I welcome any feedback, and you are certainly welcome to use the code provided in whatever project you want.  If you do, I’d love to know about it!  Here’s the file if you just want to download it instead of copy/pasting the various blocks I posted above: Bitflag.hpp.

I’d also like to thank Brennan Conroy and Robert Francis for dealing with a full day of my inane ramblings and providing useful insight while I worked on this.  I probably wouldn’t be making this post if it wasn’t for their help.