Dealing with booleans in a C struct

I’m in the process of converting a C struct for use in Zig. A number of fields are boolean but are defined as c_int as it’s often the case (_Bool sees little love for some reason). How safe is it to replace c_int with bool within an extern struct definition?

If I understand your goal (a struct which can be passed to C, not an idiomatic translation), then I don’t think that’s likely to work:

const ExtStruct = extern struct {
    hmm: bool,
};

test "hmm" {
    std.debug.print("This prints 1: {d}, \n", .{@sizeOf(ExtStruct)});
}

c_int is nearly always i32 these days. You could pad out the bool if you’re willing to only support little-endian, which is a defensible choice. But that seems delicate.

Also, I’ve thought I had a “boolean int” in C a couple times, only to later discover that -1 is also getting used as a null-alike. So, yeah. A bit fraught.

Substituting c_int for bool is very much not safe or portable. Instead, consider declaring a non-exhaustive enum of the right size, like how std.os.windows did for its boolean types:

pub const BOOL = Bool(c_int);
pub const BOOLEAN = Bool(u8);

pub fn Bool(comptime BackingInteger: type) type {
    return enum(Backing) {
        /// false
        FALSE = 0,
        /// true
        _,

        /// This is not the only truthy value, comparisons against this value are always a bug.
        pub const TRUE: @This() = @enumFromInt(1);

        pub const Backing = BackingInteger;

        pub fn toBool(b: @This()) bool {
            return b != .FALSE;
        }

        pub fn fromBool(b: bool) @This() {
            return @enumFromInt(@intFromBool(b));
        }
    };
}
3 Likes

I would argue that these are not booleans then but integers, and I would keep them on the Zig side as such since C code which still uses the C89 custom ‘MY_BOOL’ macros should really be rare these days (the internal _Bool type had been added in C99).

AFAIK, technically even the size of _Bool (or bool in C23) isn’t guaranteed to be one byte, but I’ve never seen it any differently across GCC, Clang and MSVC).

FWIW in my automatic bindings generator via clang ast-dump I replace ‘C booleans’ (reported as _Bool in the ast-dump JSON) with ‘Zig booleans’, and that’s safe across at least Clang, GCC and MSVC.

Any ‘C89 custom boolean’ macro definitions would be reported as the underlying type (e.g. int), but as I said above, I would keep it that way in the Zig extern struct and instead add a separate ‘Zig idiomatic’ struct with toExtern() and fromExtern() helpers. That way the C-compatible struct declarations could even be private and the Zig API functions only expose the Zig-idiomatic structs (which would require a set of Zig shims though and a conversion step)… question is though if such a 1:1 Zig shim even makes sense… depends a lot of the project I guess.

1 Like

The boolean fields are interperse among ints and pointers, so I think field alignment will ensure byte layout is maintained.

It will also be endian-specific (only works on little-endian). Might not be an issue of course.

E.g.

c_int=1 stored as little endian (bytes):
+---+---+---+---+
| 1 | 0 | 0 | 0 |
+---+---+---+---+
...and as big endian:
+---+---+---+---+
| 0 | 0 | 0 | 1 |
+---+---+---+---+

If you map a Zig boolean to the same start address, it will always be false on big endian :wink:

2 Likes

Setting endianness aside, can you get away with it? Possibly/probably, should you? No. I absolutely wouldn’t do this without defining the padding, because unmentioned portions of a struct’s layout have unspecified contents: the low eight bits might be right, but that leaves another 24, and without insisting those be zero as well, anything is allowed to happen.

I think @castholm has a good idea here, that’s how I would do it.

1 Like

Yeah, forgot about endianness. This is more trouble than it’s worth. I’m just going to use an enum. A boolean isn’t always clearer anyway. Sometimes it’s unclear what the opposite of something means. One of the flags in question is true_color. If you’re unfamiliar with computing lingo of the 90’s you wouldn’t know “not true color” means using a color palette.

1 Like