3 ms·
I’ve definitely desired an operator that combines these before. I’ve settled on &== as the best syntax in C-style languages, with a &== b being equivalent to a
by chrismorgan 4y ago
I’ve definitely desired an operator that combines these before. I’ve settled on &== as the best syntax in C-style languages, with a &== b being equivalent to a & b == b (but with a single evaluation of b).
if (mask &== WGPUColorWriteMask_Alpha|WGPUColorWriteMask_Blue) { … }
I find only two very minor problems with it: firstly, that it’s not commutative (that is, a &== b is not equivalent to b &== a). Secondly, that it can be confused with the bitwise-and assignment operator &= (a &= b being equivalent to a = a & b for singly-evaluated lvalue a).
I’d then add |== for consistency and because it is conceivably useful. ^== is tempting, but since a ^== b would be just another spelling of a == 0, I’d skip it.
- magicalhippo 4y ago> I find only two very minor problems with it: firstly, that it’s not commutative Why is that a problem? That is, if a has additional bits set compared to b, then ((a & b) == b) != ((b & a) == a), no?
- chrismorgan 4y agoOf common operators, / and - are the only ones that aren’t commutative. For equality comparisons, there are two opposing conventions: actual == expected (the more popular, in my experience), and expected == actual (by no means rare). Having &== and |== be order-sensitive (though there’s absolutely no question in my mind about what the ordering should be) is mildly unfortunate. It’s very minor.
- magicalhippo 4y agoYeah ok. I'd consider that just a thing you gotta know. Like how for certain floating point values (a / b) * (c / d) can be fine while (a * c) / (b * d) gives you +/- infinity. It's a separate operator after all, one I also miss a lot...
- beeforpork 4y agoFaszinating: I also thought about exactly '&==' in the past. It seems to be natural choice for this. But then, continuing that thinking, if you have '&==', you'd also need '&!=' -- that looks really confusing. Can't we turn that into a '==0' test somehow? Maybe '^&', because '((a ^ b) & b) == 0' is equivalent to '(a & b) == b'. And, as somehow said: '~&' also works: '(~a & b) == 0' is also equivalent to '(a & b) == b'. if ((a ~& b) == 0) { ... } Wait -- we can swap that into '&~' and we're back in C. However, it reverses the logical order, with the test bits first: if ((b & ~a) == 0) { ... } if (((WGPUColorWriteMask_Alpha|WGPUColorWriteMask_Blue) & ~mask) == 0) { ... } So we're back -- this is standard C now. But I find it incomprehensible.
- chrismorgan 4y agoNow I’m trying to decide if you make it better or worse by replacing the == 0 with simple logical negation: if (!(b & ~a)) { … } if (!((WGPUColorWriteMask_Alpha|WGPUColorWriteMask_Blue) & ~mask)) { … } if (!(~a & b)) { … } if (!(~mask & (WGPUColorWriteMask_Alpha|WGPUColorWriteMask_Blue))) { … } Actually, that latter makes a little more intuitive sense to me because of how ! and ~ are both negation¹, so they kinda cancel out and leave just the masking. Kinda. —⁂— ¹ Fun fact: Rust uses ! for both logical and bitwise negation, backed by core::ops::Not, with bool → bool and {integer} → {integer}, since bool is a proper type and there’s no boolean coercion anywhere—so you would have to stick with `== 0` in Rust, though in practice you’d probably go all typey with the bitflags crate’s macro to generate good types with some handy extra methods, and write `mask.contains(WgpuColorWriteMask::Alpha | WgpuColorWriteMask::Blue)`.
- silvestrov 4y agoYou could also play the game of pretending that the scalar is an object and allow for syntactical sugar like: if (mask.contains(WGPUColorWriteMask_Alpha) || mask.contains(WGPUColorWriteMask_Blue)) { ... }
- chrismorgan 4y agoFun fact: Rust’s bitflags macro crate generates just this, https://docs.rs/bitflags/latest/bitflags/example_generated/struct.Flags.html#method.contains https://docs.rs/bitflags/latest/bitflags/example_generated/s.... // To check for either flag: if mask.contains(WgpuColorWriteMask::Alpha) || mask.contains(WgpuColorWriteMask::Blue) { … } if mask.intersects(WgpuColorWriteMask::Alpha | WgpuColorWriteMask::Blue) { … } // To check for both flags: if mask.contains(WgpuColorWriteMask::Alpha | WgpuColorWriteMask::Blue) { … }