4 ms·
(self-reply) One more thing. > I could use _Bool, but I’d rather stick to a natural word size and stay away from its weird semantics. This is even more subjec
by comex 3y ago
(self-reply) One more thing.
> I could use _Bool, but I’d rather stick to a natural word size and stay away from its weird semantics.
This is even more subjective, but personally I like _Bool's semantics. They mean that if an expression works in an `if` statement:
if (flags & FLAG_ALLOCATED)
then you can extract that same expression into a boolean variable:
_Bool need_free = flags & FLAG_ALLOCATED;
The issue is that `flags & FLAG_ALLOCATED` doesn't equal '0 if unset, 1 if set', but '0 if unset, some arbitrary nonzero value if set'. (Specifically it equals FLAG_ALLOCATED if set, which might be 1 by coincidence, but usually isn't.) This kind of punning is fine in an `if` statement, since any nonzero value will make the check pass. And it's fine as written with `_Bool`, since any nonzero integer will be converted to 1 when the expression is implicitly converted to `_Bool`. But if you replace `_Bool` with `int`, then this neither-0-nor-1 value will just stick around in the variable. Which can cause strange consequences. It means that
if (need_free)
will pass, but
if (need_free == true)
will fail. And if you have another pseudo-bool, then
if (need_free == some_other_bool)
might fail even if both variables are considered 'true' (i.e. nonzero), if they happen to have different values.
_Bool solves this problem. Admittedly, the implicitness has downsides. If you're refactoring the code and you decide you don't really need a separate variable, you might try to replace all uses of `need_free` with its definition, not realizing that the implicit conversion to _Bool was doing useful work. So you might end up with incorrect code like:
if ((flags & FLAG_ALLOCATED) == true)
Also, if you are reading a struct from disk or otherwise stuffing it with arbitrary bytes, and the struct has a _Bool, then you risk undefined behavior if the corresponding byte becomes something other than 0 or 1 – because the compiler assumes that the implicit conversion to 0 or 1 has been done already.
- cozzyd 3y agoI always just use !! for this case.
- fullstop 3y agoYes, I do this as well. It looks a little funny if you're not used to it, I suppose.
- billforsternz 3y agoThis is all very good and very, ahem, true. But (and it's a big butt); if (need_free == true) Is such a horrible code smell to me. You have a perfectly good boolean. Why compare it to a second boolean to get a third boolean? if (need_free) or if (!need_free) for the opposite case is so much better. I will admit that in my world this leaves if (need_free == some_other_bool) as something I don't have a particularly comfortable way of doing safely.
- MilanTodorovic 3y agoCouldn't we do it like (!need_free == !some_other_bool)?
- mariusor 3y agoYou can go the php way and do if (!!need_free)
- billforsternz 3y agoClever, I like it.
- hun3 3y agoBetter example: #define FLAG_63 (1ULL << 63) long long flags = FLAG_63; In this case, if (flags & FLAG_63) pass(); will pass, but typedef int BOOL; BOOL set = flags & FLAG_63; if (set) pass(); won't pass, due to truncation. Question: Would you argue that a datatype that holds the smallest (1-bit) datum should be as wide as the largest integer type just to handle such cases? If so, that would be highly inefficient for storage purposes. Note that Win32 has 32-bit BOOL type, but internally NT uses 8-bit BOOLEAN type to store bools in structures.
- bmacho 3y ago> if (need_free == true) > Is such a horrible code smell to me. You have a perfectly good boolean. Why compare it to a second boolean to get a third boolean? > if (need_free) You are probably interested if the `need_free` flag is set to true, and not if `need_free`. It is true that `if (need_free)` has the same behaviour, but it is some steps farther from what you are interested in.