On Wednesday, 1 March 2023 at 08:12:05 UTC, rempas wrote:
> I'm looking into this tutorial to learn XCB and I'm trying to write the code in D with betterC. In the section 9.1 (sorry, I cannot give a section link, the article does not give us this ability), I'm facing a problem and my program exits with the exit code: "-11". I suspect that this happens because I haven't translated the following code the right way:
uint32_t value[1];
value[0] = screen.black_pixel;
xcb_create_gc(connection, black, win, mask, value);
I originally tried to translate this as:
uint[1] value;
value[0] = screen.black_pixel;
xcb_create_gc(connection, black, win, mask, value);
But then, when I tried to compile the program, I got the following error:
Error: function `headers.xcb_create_gc(xcb_connection_t* c, uint cid, uint drawable, uint value_mask, const(void)* value_list)` is not callable using argument types `(xcb_connection_t*, uint, uint, uint, uint[1])`
src/draw.d(18,16): cannot pass argument `value` of type `uint[1]` to parameter `const(void)* value_list`
So I thought of doing the following:
uint* value;
value[0] = screen.black_pixel;
xcb_create_gc(connection, black, win, mask, value);
Now the program complies but I get the "-11" exit code. Another thing I thought (which is probably the same thing under the hood but done a different way):
const(void)* value;
(cast(ubyte*)value)[0] = screen.black_pixel;
xcb_create_gc(connection, black, win, mask, value);
Same results. Any ideas?
11 is SIGSEGV. A segfault, or access violation, happens when you try to access unallocated memory. In this case, let me annotate your code so it's easier to see what's happening:
// null is the default value for a pointer
uint* value = null;
// because `value` is null, the first index also lies at null.
assert(&value[0] is null);
// So we try to store screen.black_pixel at memory address null, which is unallocated.
value[0] = screen.black_pixel;
xcb_create_gc(connection, black, win, mask, value);
As there is no memory segment allocated at address null, the CPU indicates a segmentation fault, which terminates the program.
So yes, xcb_create_gc
wants a uint*
parameter, but not just any uint*
will do: it has to point to valid memory. Going back to the first snippet, what's happening here is that in C, arrays implicitly convert to pointers, because C doesn't have a notion of array types as distinct from pointer types. So you can have a variable declared as uint[1]
, but pass it to a parameter that expects uint*
, and the value that is passed will just be the address of the first field of the array. However, even in C, if you try to define value
as uint*
, it will segfault in the same way. Instead, in D, you need to tell the compiler to define an array of size 1, and then pass a pointer to the array's first member explicitly:
uint32_t[1] value;
value[0] = screen.black_pixel;
// this is what C does under the hood
xcb_create_gc(connection, black, win, mask, &value[0]);
Or shorter, but with the same effect:
uint32_t[1] value;
value[0] = screen.black_pixel;
xcb_create_gc(connection, black, win, mask, value.ptr);