Sign in to follow this  
3DModelerMan

Convert bit flags to indices?

Recommended Posts

Is there any way to iterate over a bit flag using a for loop? I'm using a collection of bit flags:

EVE_POSITION = 0x01,
			
EVE_TEXCOORD0 = 0x02,
EVE_TEXCOORD1 = 0x04,
EVE_TEXCOORD2 = 0x08,
EVE_TEXCOORD3 = 0x10,
EVE_TEXCOORD4 = 0x20,
EVE_TEXCOORD5 = 0x40,
EVE_TEXCOORD6 = 0x80,
EVE_TEXCOORD7 = 0x100,

EVE_NORMAL = 0x200
//Etc.

These work great as bit flags to combine vertex formats, but now I want to be able to iterate over an array using each of the elements as an index. I don't want to waste the memory to make the array large enough to hold 0x200 elements. Are there any bit shifting tricks to convert these?

Share this post


Link to post
Share on other sites
I thought you were asking to iterate over the bits set: 
for (unsigned x = flags; x != 0u; x &= x - 1u)
  std::cout << (x & -x) << '\n';
 
This type of code is very common in computer chess programming when using bitboards. Edited by Álvaro

Share this post


Link to post
Share on other sites
I usually prefer to set bitflags as indices rather than bitmasks and then converting them when needed, e.g.

enum class EFlags : uint32_t {
  Flag1,   // 0
  Flag2,   // 1
  Flag3,   // 2
  MaxFlags,// 3
};
These can be trivially converted to bitmasks via:

template <typename T, typename R = uint32_t>
constexpr R bit(T index) {
  return static_cast<R>(1u << std::underlying_t<T>{index});
}
With this bit(2) == 1 << 2 == 4, so Flag3 is 4 (Flag1 is 1 and Flag2 is 2).

This doesn't work for flag sets where you need to have combinations in the set itself, though. One alternative would be to have two different enums that you'd have to keep in sync, something like:

enum class EFlagIndex {
  Flag1, // 0
  Flag2, // 1
  Flag3, // 2
  MaxFlags // 3
};

enum EFlagMask : uint32_t {
  None = 0,
  Flag1 = bit(EFlagIndex::Flag1), // 1
  Flag2 = bit(EFlagIndex::Flag2), // 2
  Flag3 = bit(EFlagIndex::Flag3), // 4
If you don't really care about the indices or maximum index, you can just get straight to the masks:

enum class EFlags : uint32_t {
  None = 0,
  Flag1 = bit(0), // 1
  Flag2 = bit(1), // 2
  Flag3 = bit(2), // 4
  Flags = Flag1 | bit(4), // 17
  MaxPlusOne,
};
Note that in direct answer to one of your questions, in this setup you have to count how many flags there are. You won't be able to tell automatically. Some proposals for C++17 will make it easier to write tools that automatically generate that data, but as of today, you just can't.

There are several work-arounds, but selecting one (or more than one) depends on the data that you want. Do you want to know how many flag enums there are or what the maximum flag is? Using the index-based approach you get the number of flags _and_ the maximum flag with that single MaxFlags entry. Using the second mask-based approach above you can get the maximum value with the MaxPlusOne (subtract one from it to get the actual maximum, naturally).

You might note I do prefer the C++11 strong enums. By default, they're a pain to use for flags since you can't perform most basic bitwise operations on them. A simple macro can add those by defining operator overloads like:

#define MAKE_FLAGS(E) \
  static inline E operator|(E lhs, E rhs) { return static_cast<E>(static_cast<std::underlying_type_t<E>>(lhs) | static_cast<std::underlying_type_t<E>>(rhs); } \
  /* also do &, ~, &=, |= */ \
  static inline bool contains(E haystack, E needle) { return (haystack&needle) == needle; } \
  static inline E set(E source, E mask) { return source|mask; } \
  static inline E reset(E source, E mask) { return source&~mask; }
Obnoxious that you have to use a macro, but there you go.

You could also try some variations I've thought about but never experimented with, like using index-based flags combined with operators that do the conversion automatically and use some helper types. They'd require more work to get all the various overloads in place, though, and may easily end up being more of a pain in the butt than helpful.

You might find that adding an introspection feature to your enums (again, C++17 will make this way easier, but for now you have to be far more repetitive and manual) will help a ton. Something like:

template <typename E>
class enum_traits {
  using entry_type = std::pair<E, std::string>;
  using registry_type = std::vector<entry_type>;

  static auto& registry() {
    static registry_type instance;
    return instance;
  }

public:
  static std::string name_of(E value) {
    for (auto const& entry : registry())
      if (entry.first == value)
        return entry.second;
     return {};
  }

  static E value_of(std::string const& name) {
    for (auto const& entry : registry())
      if (entry.second == name)
        return entry.second;
     return E{}; // will be 0
  }

  static void _register(std::initializer_list<entry_type > mapping) {
    registry() = mapping_type(mapping);
  }
};

template <typename E>
E enum_value_of(std::string const& name) {
  return enum_traits<E>::value_of(name);
}

template <typename E>
std::string enum_name_of(E value) {
  return enum_traits<E>::name_of(value);
}

#define REGISTER_ENUM(E, ...) template<> enum_traits<E>::_registry(__VA_ARGS__);
enum class ETest {
  Flag1,
  Flag2,
  Flag3,
};

REGISTER_ENUM(ETest, {
  { ETest::Flag1, "Flag1" },
  { ETest::Flag2, "Flag2" ],
  { ETest::Flag3, "CustomFlagName" },
});
ETest value = enum_value_of<ETest>("CustomFlagName"); // returns ETest{4}
std::string name = enum_name_of(ETest::Flag3); // returns "CustomFlagName"
That's all typed mostly from memory so don't be surprised if it doens't compile without tweaking. In fact, be surprised if it _does_ compile without tweaking.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this