Enum class : bitwise operations?

Started by
17 comments, last by Juliean 11 years ago

Hello,

I've recently converted all my enums to strongly typed enums, like this:


enum class WindowBorders : unsigned char {
    None            = 0x00,
    Left            = 0x01,
    Right            = 0x02,
    Bottom            = 0x04,
    LeftBottom        = 0x05,
    RightBottom        = 0x06,
};

Now these work as flags, and previously I could have been checking if a variable contained one of those flags by coding:


unsigned char m_cBorder = LeftBottom;
if( (m_cBorder & Left) == Left )
//left border was clicked

Now this won't work anymore, I have to treat LeftBottom and RightBottom as seperate cases, while they essentially are just an addition of bottom and eigther left or right border, and also work that way. That causes a lot of unecessary code for my window object. The (german) compiler gives me somemessage about "&" only working for enums without a range restriction (?). Is this really impossible to achieve or is there some way to still do this?

Advertisement

You can define a | and & operator for your enum (static_cast'ing them to ints then back to the enum type after applying the operation). To avoid re-typing the same code for each different enum type, you can put it in a macro or something.

Well it should work, did you tried debugging to see if m_cBorder has the right bits set? Im pretty sure the problem isn't in the code posted above.

Well it should work, did you tried debugging to see if m_cBorder has the right bits set? Im pretty sure the problem isn't in the code posted above.

The code included above does work, having just "enum WindowBorders". That is the code that will fail:


WindowBorders m_cBorder = WindowBorders::LeftBottom;
if( m_cBorder & WindowBorders::Left) == WindowBorders::Left) //won't compile here

It simply won't compile, so there is no need/option for debugging.

You can define a | and & operator for your enum (static_cast'ing them to ints then back to the enum type after applying the operation). To avoid re-typing the same code for each different enum type, you can put it in a macro or something.

Since strongly typed enums can't have an operator overloaded, I assume you generally suggest eigther writing a macro or templated global function? How costly are static_casts anyway? Because there could be potentially a lot of checks throughout my code. Given this setting I'd somehow doubt the benefit of strongly typed enums over the overhead of "simulating" a bitwise operator... what would you rather choose?

Well you could solve the issue by using #define's instead

#define WINBORDER_NONE 0x00

... ect

Why would you need strongly typed enums? Using your old code will work regardless of the type used (int, short, char, byte ect), no need to overcomplicate things rolleyes.gif

I'm not a big fan of #defines, as far as there are better alternatives really. It would work for normal enums so thats not the problem. I choose to go with strongly typed enums for multiple reasons:

- It makes code much more clear. Consider this:


void Cursor::ChangeState(CursorState newState)
{
	m_state = newState;
	switch(m_state)
	{
	case CursorState::IDLE:
		m_sFileName = L"CursorIdle.png";
		m_offsetX = 0;
		m_offsetY = 0;
		break;
	case CursorState::DRAG_V:
		m_sFileName = L"CursorDragV.png";
		m_offsetX = 16;
		m_offsetY = 16;
		break;
	case CursorState::DRAG_H:
		m_sFileName = L"CursorDragH.png";
		m_offsetX = 16;
		m_offsetY = 16;
		break;
	case CursorState::DRAG_LD:
		m_sFileName = L"CursorDragLD.png";
		m_offsetX = 16;
		m_offsetY = 16;
		break;
	case CursorState::DRAG_RD:
		m_sFileName = L"CursorDragRD.png";
		m_offsetX = 16;
		m_offsetY = 16;
		break;
	}
}

over this:


void Cursor::ChangeState(UINT newState)
{
	m_state = newState;
	switch(m_state)
	{
	case IDLE:
		m_sFileName = L"CursorIdle.png";
		m_offsetX = 0;
		m_offsetY = 0;
		break;
	case DRAG_V:
		m_sFileName = L"CursorDragV.png";
		m_offsetX = 16;
		m_offsetY = 16;
		break;
	case DRAG_H:
		m_sFileName = L"CursorDragH.png";
		m_offsetX = 16;
		m_offsetY = 16;
		break;
	case DRAG_LD:
		m_sFileName = L"CursorDragLD.png";
		m_offsetX = 16;
		m_offsetY = 16;
		break;
	case DRAG_RD:
		m_sFileName = L"CursorDragRD.png";
		m_offsetX = 16;
		m_offsetY = 16;
		break;
	}
}

Its not something that absolutely needs to be, but I think in the first code it everything is much clearer, extending to how the m_state variable is declared (CursorState instead of UINT) etc...

- I won't possibly ever run in a name clash for these enums.

- There is no possibility to asign a wrong value. Thing is, that like you said, the old code works regardeles of the actual type used, I don't like that very much. I can pass in My NoResize - flag from the WindowStyleFlags to the CursorState, which isn't possible with strongly typed enums.

Over all I just tend to prefern them after I read about them some while ago, I just decided to implement them tody. Surely the old code works, and I wouldn't need them, the "enum class" makes things just more clean, in my opinion, and my goal after getting code to work is now to polish it up... except for that bitwise-thingy, that is :/

Since strongly typed enums can't have an operator overloaded, I assume you generally suggest eigther writing a macro or templated global function? How costly are static_casts anyway? Because there could be potentially a lot of checks throughout my code. Given this setting I'd somehow doubt the benefit of strongly typed enums over the overhead of "simulating" a bitwise operator... what would you rather choose?

There is an example of what I meant here, a few answers down:

http://stackoverflow.com/questions/1448396/how-to-use-enums-as-flags-in-c

I'm pretty sure static_cast from an int to enum (or vice versa) would just be compiled away, so it should be free.

Ah, I see, that worked. Thanks! A lot better again, thankfully. Now I only gotta find a way to automatice this for all enums and, aditionally possibly hide away this:


(border & WindowBorders::Bottom) == WindowBorders::Bottom

And other repetitive bit-checks into some sort of helper functions. Should be easiely doable, though.

Here's what I'm doing to somewhat "automate" the process. You could write a macro to further cut down code.


enum ENUM_DrawElements : int {
GD_DRAW_ELEMENT_NONE = 0,
GD_DRAW_ELEMENT_STATIC_GEOMETRY = 1,
GD_DRAW_ELEMENT_DYNAMIC_GEOMETRY = 2,
GD_DRAW_ELEMENT_ENTITIES = 4,
GD_DRAW_ELEMENT_EDITORFUNCTIONS = 8,
//...
};


static inline ENUM_DrawElements operator|(ENUM_DrawElements a, ENUM_DrawElements b) { return static_cast<ENUM_DrawElements>(static_cast<int>(a) | static_cast<int>(b)); }

 

You can now use bitwise or on members of ENUM_DrawElements: GD_DRAW_ELEMENT_STATIC_GEOMETRY | GD_DRAW_ELEMENT_ENTITIES | ...
NOTE: you do have to mind possible precision issues, though - casting to int is limiting the precision to 32 bits.
How is casting to int a problem if the enum is defined to use int as the underlying type?

This topic is closed to new replies.

Advertisement