www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - C.h to D conversion (structs)

reply Chris <wendlec tcd.ie> writes:
I've converted a C.h file to D according to this guide:

http://wiki.dlang.org/Converting_C_.h_Files_to_D_Modules

and examples in deimos:

https://github.com/D-Programming-Deimos/

However, I get an error when trying to use a struct that uses 
structs.

struct A
{
   size_t i;
}

struct B
{
   size_t x;
}

struct C
{
   A a;
   B b;
}

The error I get is something like

undefined reference to `_D3test7testmodule13A6__initZ'
undefined reference to `_D3test7testmodule13B6__initZ'

// The C header would look like this:

typedef struct _A
{
   size_t i;
} A;

typedef struct _B
{
   size_t x;
} B;

typedef struct _C
{
   A a;
   B b;
} C;

Also, what do I do with C structs that contain the following:

typedef struct _A
{
   struct _A *next;
   struct _A *prev;
} A;

Thanks.
Mar 15 2016
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Tuesday, 15 March 2016 at 16:32:56 UTC, Chris wrote:
 The error I get is something like

 undefined reference to `_D3test7testmodule13A6__initZ'
 undefined reference to `_D3test7testmodule13B6__initZ'
You still need to compile/link in the module (or in this specific case, void initialize the structs) so any little functions or initializers are present. In C, structs need to be initialized manually, but in D they are automatically set to some initial value for each field. That initial value is the referenced __init symbol and still comes out of the .o file, like a function would.
Mar 15 2016
parent reply Chris <wendlec tcd.ie> writes:
On Tuesday, 15 March 2016 at 16:44:10 UTC, Adam D. Ruppe wrote:
 On Tuesday, 15 March 2016 at 16:32:56 UTC, Chris wrote:
 The error I get is something like

 undefined reference to `_D3test7testmodule13A6__initZ'
 undefined reference to `_D3test7testmodule13B6__initZ'
You still need to compile/link in the module (or in this specific case, void initialize the structs) so any little functions or initializers are present. In C, structs need to be initialized manually, but in D they are automatically set to some initial value for each field. That initial value is the referenced __init symbol and still comes out of the .o file, like a function would.
Do you mean I need to void initialize them in the C code or in D? And if in D, how would I do that, with `static this`?
Mar 15 2016
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Tuesday, 15 March 2016 at 16:56:00 UTC, Chris wrote:
 Do you mean I need to void initialize them in the C code or in 
 D? And if in D, how would I do that, with `static this`?
in D, at the usage point with =void where you declare the variable of that type. So in your code: struct C { A a = void; B b = void; } though I'm pretty sure it wouldn't matter in this specific instance because they would be all zeroes anyway... your real code probably has a char or a float in it, right? I do not recommend trying that though, it is better to actually compile+link in the modules.
Mar 15 2016
parent reply Chris <wendlec tcd.ie> writes:
On Tuesday, 15 March 2016 at 17:10:03 UTC, Adam D. Ruppe wrote:
 On Tuesday, 15 March 2016 at 16:56:00 UTC, Chris wrote:
 Do you mean I need to void initialize them in the C code or in 
 D? And if in D, how would I do that, with `static this`?
in D, at the usage point with =void where you declare the variable of that type. So in your code: struct C { A a = void; B b = void; } though I'm pretty sure it wouldn't matter in this specific instance because they would be all zeroes anyway... your real code probably has a char or a float in it, right? I do not recommend trying that though, it is better to actually compile+link in the modules.
I'm not 100% sure what you mean with compile+link in the modules. The structs are all defined in the original (third party) C header file. It's nothing I added (in which case I would have to recompile the C library). The C structs in the C library should be visible to the linker, shouldn't they? Just as when you define: extern (C): size_t strlen(const char *str); and the linker will find it automatically. The error I get is as if the structs were not defined (or as if the lib weren't linked to).
Mar 15 2016
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Tuesday, 15 March 2016 at 18:04:00 UTC, Chris wrote:
 I'm not 100% sure what you mean with compile+link in the 
 modules.
Like you would another D library.
 The structs are all defined in the original (third party) C 
 header file. It's nothing I added (in which case I would have 
 to recompile the C library). The C structs in the C library 
 should be visible to the linker, shouldn't they?
The problem isn't the struct itself, but the D initializer. Structs in C don't have initalizers but do in D: struct Foo { int a = 10; /* illegal in C, legal in D */ }; Note that extern(C) doesn't actually do anything on a struct, it really only applies to functions and global variables. D realizes this by creating a variable called _DStructName__init which has the initial value of the struct. When you make a variable of that type, it copies the value of this hidden init thing over to your new variable to initialize it. C does nothing of the sort, so the C library will not provide this - you need to link in the one from D. You might be thinking "but I don't use = anything in this struct", but there's a few places where D will put one in automatically: float and double = NaN by default char, wchar, and dchar = -1 (well, 0xff) by default And everything else =0 (or null) by default. If *everything* in a struct is initialized to 0, the __init variable is left out and memset(&struct, struct.sizeof, 0) is called instead. But if *anything* in there is non-zero, the __init variable is created and referenced when you declare the variable (unless you =void it at the usage point). Which causes the undefined reference problem you're seeing - the usage code is trying to initialize, but the C library doesn't provide that (since it isn't a C feature) and the D library isn't linked in. I betcha if you have any float/double or char variables in those structs and set them as =0 in the definition, you will actually be able to make this problem go away.
Mar 15 2016
parent Chris <wendlec tcd.ie> writes:
On Tuesday, 15 March 2016 at 18:47:22 UTC, Adam D. Ruppe wrote:

 Like you would another D library.
Now I get it! Yes, that works as expected.
 The problem isn't the struct itself, but the D initializer. 
 Structs in C don't have initalizers but do in D:

 struct Foo {
    int a = 10; /* illegal in C, legal in D */
 };


 Note that extern(C) doesn't actually do anything on a struct, 
 it really only applies to functions and global variables.
That's where I got it wrong!
 D realizes this by creating a variable called 
 _DStructName__init which has the initial value of the struct. 
 When you make a variable of that type, it copies the value of 
 this hidden init thing over to your new variable to initialize 
 it. C does nothing of the sort, so the C library will not 
 provide this - you need to link in the one from D.


 You might be thinking "but I don't use = anything in this 
 struct", but there's a few places where D will put one in 
 automatically:

 float and double = NaN by default
 char, wchar, and dchar = -1 (well, 0xff) by default

 And everything else =0 (or null) by default.


 If *everything* in a struct is initialized to 0, the __init 
 variable is left out and memset(&struct, struct.sizeof, 0) is 
 called instead.

 But if *anything* in there is non-zero, the __init variable is 
 created and referenced when you declare the variable (unless 
 you =void it at the usage point).


 Which causes the undefined reference problem you're seeing - 
 the usage code is trying to initialize, but the C library 
 doesn't provide that (since it isn't a C feature) and the D 
 library isn't linked in.
Thanks a million! Now I understand why it didn't work with my implementation. [snip]
Mar 15 2016