Advertisement

Enum bit flags

Started by March 20, 2021 12:52 PM
5 comments, last by Miss 3 years, 8 months ago

This has happened to me a few times, and the solution is always kind of a weird workaround, so figured I'd ask if this could be implemented properly.

If I have an enum like this:

enum Things
{
  None = 0,
  Foo = (1 << 0),
  Bar = (1 << 1),
}

And a function definition that accepts the enum:

void DoSomething(Things things);

I can't combine multiple flags together like this:

DoSomething(Things::Foo | Things::Bar);

Because it casts the result of the OR into an integer, and throws a “No matching signatures" compiler error. The workaround is to just make the parameter an int instead, but that's not great for documentation. Alternatively, I can cast it back to a “Things” enum type:

DoSomething(Things(Things::Foo | Things::Bar));

Is there something that can be done about this in Angelscript itself or would that be too complex?

(I might have made a thread asking about this before, sorry if I have :D I wasn't able to search only the Angelcode forum here.)

Of course it can be done. The compiler is currently prohibiting the implicit cast from int to enum out of design, but I could potentially add an engine property to allow application developers permit the implicit cast if desired.

However, I wonder if this is actually accurate. After all, (Things::Foo | Things::Bar) equals 3, and there is no enum value in Things that matches this.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

Advertisement

Strongly typed bit field enums are a thing in C++. You define them like this:

enum class Things : uint8_t {
  None = 0,
  Foo = (1 << 0),
  Bar = (1 << 1),
};
Things operator|(Things a, Things b) {
  return static_cast<Things>(static_cast<uint8_t>(a) | static_cast<uint8_t>(b));
}
// TODO: operators &, ^, ~.

This works because for an enum with a specified underlying type, the valid range of values for that enum is the same as that of the underlying type, even if those values are not enumerated in the enum definition. Yes, this language feature is confusing and unlike enums in other languages and a potential source of bugs, but it does have valid uses and bit field enums is one of them. Whether you want to copy this design in AngelScript is another question entirely, of course.

Note that there is no implicit conversion in the above example, either to or from int. The type of Things::Foo | Things::Bar is Things.

That's kinda what I had in mind as well, except not having to declare the operator overload manually, but that it would automatically do that so you only need the enum values. I feel like it's common enough that it could be nice to have built-in.

Casting from int implicitly could have other consequences if for example you're passing an actual integer to something that's actually an enum of bit flags. (If for example the script developer made a mistake, or some parameters for a function has changed.)

a light breeze said:

Strongly typed bit field enums are a thing in C++. You define them like this:

This gave me the idea of having something like this:

enum bitfield Things
{
  None = 0,
  Foo = (1 << 0),
  Bar = (1 << 1),
}

The bitfield attribute would tell the compiler that the enum values should be treated as a bit field and allow the bit operators to return the enum type rather than an int. It's not as flexible as C++, but I think it would cover the most common use case.

I agree that this is common enough to have as a built-in feature, and it is certainly much better than allowing implicit cast from int to enums.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

I really like this idea!

This topic is closed to new replies.

Advertisement