Thread overview
How would the equivalent C type be in D?
Mar 01, 2023
rempas
Mar 01, 2023
FeepingCreature
Mar 01, 2023
FeepingCreature
Mar 01, 2023
rempas
Mar 07, 2023
FeepingCreature
Mar 07, 2023
rempas
Mar 07, 2023
FeepingCreature
March 01, 2023

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?

March 01, 2023

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);
March 01, 2023

On Wednesday, 1 March 2023 at 08:26:07 UTC, FeepingCreature wrote:

>
uint32_t[1] value;
value[0] = screen.black_pixel;
xcb_create_gc(connection, black, win, mask, value.ptr);

To expand on this:

uint32_t[2] value;
uint32_t* value_ptr = value.ptr;
// We are allowed to access the pointer at index 0
// because we declared the value it points to, to be size 2.
value_ptr[0] = 0;
// This is also allowed, because `value_ptr` is
// a pointer to two sequential `uint32_t` in memory.
value_ptr[1] = 0;
// But this access would segfault, because it's trying to write
// to the third element of a two-element array:
value_ptr[2] = 0;

Note: I just lied; value_ptr[2] would not segfault, for somewhat technical reasons; but it would corrupt your program's memory. At any rate, it's an invalid operation.

March 01, 2023

On Wednesday, 1 March 2023 at 08:26:07 UTC, FeepingCreature wrote:

>

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);

Thank you! You are amazing for explaining it! I was so focused on thinking that I'm doing something wrong with the type that I didn't noticed that the pointers, points to nowhere so the function obviously has nowhere to write to. Like... OMG! And I want to make a fully fledged compiler when making stupid mistakes like that. Btw, When I executed the program, I got "Error Program exited with code -11". You said that the code was "11". What about that dash? If it is not a "minus" and it's just the dash symbol, then what's the idea?

March 07, 2023

On Wednesday, 1 March 2023 at 09:37:48 UTC, rempas wrote:

>

Thank you! You are amazing for explaining it! I was so focused on thinking that I'm doing something wrong with the type that I didn't noticed that the pointers, points to nowhere so the function obviously has nowhere to write to. Like... OMG! And I want to make a fully fledged compiler when making stupid mistakes like that. Btw, When I executed the program, I got "Error Program exited with code -11". You said that the code was "11". What about that dash? If it is not a "minus" and it's just the dash symbol, then what's the idea?

Yay!

Yes, that is a bit weird. First of all, the actual signal is 11

$ grep SIGSEGV /usr/include/bits -R
/usr/include/bits/signum-generic.h:#define      SIGSEGV         11      /* Invalid access to storage.  */

As per the POSIX spec https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_21_18. signal exits must be reported with exit codes above 128. Customarily, shells will simply add 128 to the signal:

$ cat test.c; gcc test.c -otestc; ./testc; echo $?
int main() { int* ip = 0; *ip = *ip; return 0; }
Segmentation fault
139

139 being 128 + 11, but POSIX does not specify how the signal code is converted to an exit code. For instance, Python reports a signal 11 exit as -11. Strictly speaking, -11 is "above 128" in two's complement, corresponding to unsigned 245. But I don't know why Python does this. Presumably your shell does it the same way?

March 07, 2023

On Wednesday, 1 March 2023 at 09:37:48 UTC, rempas wrote:

>

Thank you! You are amazing for explaining it! I was so focused on thinking that I'm doing something wrong with the type that I didn't noticed that the pointers, points to nowhere so the function obviously has nowhere to write to. Like... OMG! And I want to make a fully fledged compiler when making stupid mistakes like that. Btw, When I executed the program, I got "Error Program exited with code -11". You said that the code was "11". What about that dash? If it is not a "minus" and it's just the dash symbol, then what's the idea?

Yay!

(Definitely make a compiler, it's a great way to learn.)

Yes, that is a bit weird. First of all, the actual signal is 11

$ grep SIGSEGV /usr/include/bits -R
/usr/include/bits/signum-generic.h:#define      SIGSEGV         11      /* Invalid access to storage.  */

As per the POSIX spec https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_21_18. signal exits must be reported with exit codes above 128. Customarily, shells will simply add 128 to the signal:

$ cat test.c; gcc test.c -otestc; ./testc; echo $?
int main() { int* ip = 0; *ip = *ip; return 0; }
Segmentation fault
139

139 being 128 + 11, but POSIX does not specify how the signal code is converted to an exit code. For instance, Python reports a signal 11 exit as -11. Strictly speaking, -11 is "above 128" in two's complement, corresponding to unsigned 245. But I don't know why Python does this. Presumably your shell does it the same way?

March 07, 2023

On Tuesday, 7 March 2023 at 09:05:45 UTC, FeepingCreature wrote:

>

Yay!

Yes, that is a bit weird. First of all, the actual signal is 11

[...]

Thank you for the info!