12 ms·
This might be fun to look through; first thing I saw that I never thought of before was how to define flag enums. From https://github.com/CRYTEK-CRYENGINE/CRYE
by NKCSS 10y ago
This might be fun to look through; first thing I saw that I never thought of before was how to define flag enums.
From https://github.com/CRYTEK-CRYENGINE/CRYENGINE/blob/release/Code/CryEngine/Cry3DEngine/BreakableGlassRenderNode.cpp https://github.com/CRYTEK-CRYENGINE/CRYENGINE/blob/release/C...:
// State flags
enum EGlassRNState
{
EGlassRNState_Initial = 0,
EGlassRNState_Weakened = 1 << 0,
EGlassRNState_Shattering = 1 << 1,
EGlassRNState_Shattered = 1 << 2,
EGlassRNState_ActiveFrags = 1 << 3
};
Als a C# dev, I write Enum flags from time to time, but I always just write out the values; never thought of using bit-shifting to prevent typos :)
- cjbprime 10y agoThe Linux kernel takes it a step farther: #define BIT(nr) (1UL << (nr)) #define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) #define BIT_WORD(nr) ((nr) / BITS_PER_LONG) #define BITS_TO_TYPE(nr, t) (((nr)+(t)-1)/(t))
- darkxanthos 10y agoHow does bit shifting prevent typos as opposed to just explicit numbering?
- Benjamin_Dobell 10y agoIt may or may not. However, what's more important is the semantic significance. You're making it clear these are flags and you're concerned with individual bits. 2, 4, 8 etc. are decimal numbers and mean something else i.e. "two", "four", "eight". Does bitwise ORing the decimal numbers 2 and 4 really make sense? Not particularly, but combining bits at certain offset does.
- NKCSS 10y agoBit off-topic, but in C# at least you can mark your enums with the 'Flags' attribute to indicate it's a flags enum.
- revelation 10y agoThe odd thing here is that these don't seem to be flags.. can a glass be both shattered and shattering?
- Benjamin_Dobell 10y agoTime for a pull request? ;)
- NKCSS 10y agoMaybe you read it wrong :) See http://csharppad.com/ http://csharppad.com/ // State flags enum EGlassRNState { EGlassRNState_Initial = 0, EGlassRNState_Weakened = 1 << 0, EGlassRNState_Shattering = 1 << 1, EGlassRNState_Shattered = 1 << 2, EGlassRNState_ActiveFrags = 1 << 3 }; foreach(var val in Enum.GetValues(typeof(EGlassRNState ))) { Console.WriteLine($"{val} -> {(int)val}"); } Output: EGlassRNState_Initial -> 0 EGlassRNState_Weakened -> 1 EGlassRNState_Shattering -> 2 EGlassRNState_Shattered -> 4 EGlassRNState_ActiveFrags -> 8
- Kurtz79 10y agoI think he meant that in this context does not make sense for more than one value to be true at the same time , which is the reason you define enums this way, usually.
- NKCSS 10y agoSpot the error: ... [DayOfMonth(28)] D28 = 134217728L, [DayOfMonth(29)] D29 = 268435456L, [DayOfMonth(30)] D30 = 536870902L, [DayOfMonth(31)] D31 = 1073741824L, [DayOfMonth(DayOfMonthAttribute.LastDayOfMonth)] Last = 2147483648L Vs ... [DayOfMonth(28)] D28 = 1 << 28, [DayOfMonth(29)] D29 = 1 << 29, [DayOfMonth(30)] D30 = 1 << 31, [DayOfMonth(31)] D31 = 1 << 31, [DayOfMonth(DayOfMonthAttribute.LastDayOfMonth)] Last = 1 << 32 Hint: It's with D30 in both cases...
- olalonde 10y agoWait, so you used to compute the bit shifted value in your calculator and type it out literally (in base 10) in your code? I could understand typing out the binary literal but this is weird. Or am I misunderstanding?
- NKCSS 10y agoIt's how all MS Press books that I have read have explained it in the past; same goes for .NET focused websites back in 2001-2004 (when I got started in .NET
- to3m 10y agoIt's the same number in decimal and binary :) I might be a bit weird, but I know the powers of 2 up to 2^20 in decimal. They've just snuck in there over the years, along with 2^24 and 2^31. So if I need some powers of two I might well just type them out like that. I can recognise a few common combinations too... it is all much easier in hex though. I should really get the full set up to 2^64 memorised...
- olalonde 10y agoThat's an impressive feat but now that you know about the bit shifting trick, I suggest you start using it because most of us mortals don't instantly recognise large powers of 2 when they're written out in decimal :) I'm still kind of amazed that you learned how to do bit arithmetic without coming across that pattern.
- Benjamin_Dobell 10y agoThis is pretty standard practice, in C# too. However, it's still somewhat prone to typos. If you don't care about backward compatibility (i.e. the flags are used internally and never written anywhere), then a better method is actually to grab the value one line above and bit shift it by one. That way you can insert values in the middle and you won't have to change every single line (just two).
- Pxtl 10y agoYeah, but still there's possibility of failure there as you could paste in and forget to update the one below. Honestly, flags should be a type. IIRC, the C# designers have admitted this was a mistake on their parts to continue C's ambiguity between flag enums and choice enums.
- haar 10y agoCrystal has a Flag attribute for enums (Flag enums): http://crystal-lang.org/docs/syntax_and_semantics/enum.html http://crystal-lang.org/docs/syntax_and_semantics/enum.html
- dom96 10y agoNim supports sets which IMO is a much better solution: http://nim-lang.org/docs/manual.html#types-set-type http://nim-lang.org/docs/manual.html#types-set-type
- PeCaN 10y agoTaken from Pascal and other Wirthian languages. Nim actually looks vaguely Wirthish in some areas. Interesting.
- lobster_johnson 10y agoNim's author was originally a Turbo Pascal/Delphi programmer. A lot of Nim and its conventions (things like prefixing type names with T and members with F) come directly from Borland's Pascal dialect.
- doppioandante 10y agoIt's usually done to combine multiple flags using bitwise or, but I don't think it's the case here(unless glass can be shattered and not shattered at the same time). Or are you saying that you usually write 0, 1, 2, 4 and so on?
- fsaintjacques 10y agohttps://github.com/CRYTEK-CRYENGINE/CRYENGINE/blob/63418e7c9e998272ba79c4a6693f61c382f88e15/Code/Sandbox/Plugins/EditorAudioControlsEditor/EditorWwise/AudioSystemControl_wwise.h#L10-L21 https://github.com/CRYTEK-CRYENGINE/CRYENGINE/blob/63418e7c9...
- eterm 10y agoC# even has a (deprecated?) [Flags] attribute that will auto-number in this way. Edit: I was mistaken, it still requires manually setting the numbers, it just adds neater ToString support: https://msdn.microsoft.com/en-us/library/system.flagsattribute(v=vs.110).aspx https://msdn.microsoft.com/en-us/library/system.flagsattribu...
- int_19h 10y agoIt also enables Enum.HasFlag.
- shmerl 10y agoC++14 supports binary literals, which makes it way more readable.
- pkaye 10y agoIn the Go language, there is something called iota which is like a iterator that counts up from 0: type EGlassRNState int const ( EGlassRNState_Initial EGlassRNState = 1 << iota EGlassRNState_Weakened EGlassRNState_Shattering EGlassRNState_Shattered EGlassRNState_ActiveFrags )
- 50CNT 10y agoIn python you could probably do def makeflag(): flag = 1 while True: yield flag flag = flag << 1 flag = makeflag() E_GLASS_RN_STATE_INITIAL = next(flag) E_GLASS_RN_STATE_WEAKENED = next(flag) E_GLASS_RN_STATE_SHATTERING = next(flag) E_GLASS_RN_STATE_ACTIVE_FRAGS = next(flag) flag2 = makeflag() UNRELATED_FLAG = next(flag2) UNRELATED_FLAG = next(flag2) Also no chance of fatfingering a number.
- deleted 10y ago[deleted]
- dalke 10y agoAnother option is to assume at most 128 bits then do something like: >>> _bitflags = [1<<i for i in range(128)] >>> (E_GLASS_RN_STATE_INITIAL, ... E_GLASS_RN_STATE_WEAKENED, ... E_GLASS_RN_STATE_SHATTERING, ... E_GLASS_RN_STATE_ACTIVE_FRAGS, ... *rest) = _bitflags >>> E_GLASS_RN_STATE_ACTIVE_FRAGS 8
- lifthrasiir 10y agoFor the sake of correctness, the first const should be 0 and the second should be `1 << (iota - 1)`.
- azernik 10y agoGo has a great idiom for this, with the 'iota' keyword, which increments by one for each line in a block of declarations. From the language spec: const ( // iota is reset to 0 a = 1 << iota // a == 1 b = 1 << iota // b == 2 c = 3 // c == 3 (iota is not used but still incremented) d = 1 << iota // d == 8 )
- bpicolo 10y ago"(iota is not used but still incremented)" Seems bizarre. That's actually a pretty confusing idiom in that context.
- spoiler 10y agoWhile it is a bit odd, it is documented (and I've seen it used a few times in code).
- Ericson2314 10y agoYeah this is classic shitty design from Go. One-off compiler hack that doesn't actually address any of the actual safety problems. org/bitflags/bitflags/macro.bitflags!.html is much better.
- desdiv 10y agohttps://doc.rust-lang.org/bitflags/bitflags/macro.bitflags!.html https://doc.rust-lang.org/bitflags/bitflags/macro.bitflags!.... To save you guys two clicks.
- Ericson2314 10y agoThanks. Phone clipboard failure on my part.
- smrq 10y agoJust don't fall into old habits without thinking and end up writing... enum MyEnum { MyEnum_Foo = 1 << 0, MyEnum_Bar = 1 << 1, MyEnum_Baz = 1 << 2, MyEnum_Quux = 1 << 4, MyEnum_Xyzzy = 1 << 8 }; :)
- fyolnish 10y agoShifting is used to define masks. Did you mean you actually type out mask values in base 10?
- cstrahan 10y ago> Did you mean you actually type out mask values in base 10? I believe that's what they meant. I worked in C# for many years, and many, many places wrote out bit masts in base 10 literals as no one realized they could just left-shift. Similarly, whenever I would use the ?? (null-coalescing) operator, the reviewer would inevitably point out my "syntax error", at which point I'd have to point them at the ~10th page of the nearest C# book to convince them that, yes, that is one of the valid operators -- and it's quite useful too! Man, do I miss those first years of my career working in .NET -- dunno why I ever switched to OSS stuff.
- readams 10y agoHex literals work pretty well for defining masks.