www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Request assistance initializing struct instance at global scope

reply Andrew Edwards <edwards.ac gmail.com> writes:
Given:

===============
extern(C):
char*[] hldr;
enum I = (1<<0);
struct S { char* ft; char** fm; int f; }

void main(){}
===============

How do I initialize an instance of S at global scope?

// Not sure how to do this... so try to keep as close to original as 
possible
// Nope, does not work
S[] s = [
     {
         cast(char*)"c",
         &hldr[0],
         I
     },
]

// Error: static variable C_HL_extensions cannot be read at compile time
S[] s = [ S(cast(char*)"c", &hldr[0], I) ];

// Error Deprecation: static constructor can only be of D linkage
S[] s;
static this() {
     s = [ S(cast(char*)"c", &hldr[0], I) ];
}

Thanks,
Andrew
Dec 06 2020
next sibling parent reply user1234 <user1234 12.fr> writes:
On Monday, 7 December 2020 at 04:13:16 UTC, Andrew Edwards wrote:
 Given:

 ===============
 extern(C):
 char*[] hldr;
 enum I = (1<<0);
 struct S { char* ft; char** fm; int f; }

 void main(){}
 ===============

 How do I initialize an instance of S at global scope?
You cant. At the global scope the initializers must be runnable at compile time, i.e using CTFE. I've tried to simplify what would be required: --- extern(C): char*[] hldr; enum I = (1<<0); struct S { char* ft; char** fm; int f; } void main(){} enum char[8] FirstLevel = ['0']; enum DoubleLevel = &FirstLevel[0]; // here S[] s = [ S( FirstLevel.ptr, DoubleLevel, // error is here actually, interesting 0), ]; --- D is not able of that : /tmp/temp_7F835402B0F0.d:9:28: Error: cannot use non-constant CTFE pointer in an initializer `&['0', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff'][0]`
Dec 06 2020
parent user1234 <user1234 12.fr> writes:
On Monday, 7 December 2020 at 05:28:41 UTC, user1234 wrote:
 On Monday, 7 December 2020 at 04:13:16 UTC, Andrew Edwards 
 wrote:
 Given:

 ===============
 extern(C):
 char*[] hldr;
 enum I = (1<<0);
 struct S { char* ft; char** fm; int f; }

 void main(){}
 ===============

 How do I initialize an instance of S at global scope?
You cant. At the global scope the initializers must be runnable at compile time, i.e using CTFE. I've tried to simplify what would be required: --- extern(C): char*[] hldr; enum I = (1<<0); struct S { char* ft; char** fm; int f; } void main(){} enum char[8] FirstLevel = ['0']; enum DoubleLevel = &FirstLevel[0]; // here S[] s = [ S( FirstLevel.ptr, DoubleLevel, // error is here actually, interesting 0), ]; --- D is not able of that : /tmp/temp_7F835402B0F0.d:9:28: Error: cannot use non-constant CTFE pointer in an initializer `&['0', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff'][0]`
sorry my example was bad, better one --- extern(C): char*[] hldr; enum I = (1<<0); struct S { char* ft; char** fm; int f; } void main(){} enum char[8] Stuff = ['0']; enum FirstLevel = Stuff.ptr; // &Suffn so char* OK S[] s = [ S( FirstLevel, // char* OK &FirstLevel, // error here char** NG at CTFE 0), ]; ---
Dec 06 2020
prev sibling next sibling parent reply Jacob Carlborg <doob me.com> writes:
On Monday, 7 December 2020 at 04:13:16 UTC, Andrew Edwards wrote:
 Given:

 ===============
 extern(C):
 char*[] hldr;
 enum I = (1<<0);
 struct S { char* ft; char** fm; int f; }

 void main(){}
 ===============

 // Error Deprecation: static constructor can only be of D 
 linkage
 S[] s;
 static this() {
     s = [ S(cast(char*)"c", &hldr[0], I) ];
 }
This is the correct way to do it. The problem is that you have added `extern(C):` at the top. This means that ever symbol below will have C linkage. As the error message says, `static this` can only have D linkage. `static this` is transformed into a function which is automatically called by the runtime. When you add `extern(C):` at the top, this function will also get the C linkage. A simple way to make sure that there are not multiple C functions with the same name (caused by multiple `static this`, in the same file or another file) is to mangle the function name the same way as a D function. You can either use `extern(C) char*[] hldr` to make only `hldr` have C linkage. Use `extern(C) {}` to group several symbols which should have C linkage or rearrange the code so that `static this` is above `extern(C):`. -- /Jacob Carlborg
Dec 07 2020
parent reply Andrew Edwards <edwards.ac gmail.com> writes:
On 12/7/20 10:12 PM, Jacob Carlborg wrote:
 On Monday, 7 December 2020 at 04:13:16 UTC, Andrew Edwards wrote:
 
 You can either use `extern(C) char*[] hldr` to make only `hldr` have C 
 linkage. Use `extern(C) {}` to group several symbols which should have C 
 linkage or rearrange the code so that `static this` is above `extern(C):`.
 
 -- 
 /Jacob Carlborg
Thanks Jacob. The extern(C) is temporary. I'm doing a direct port of the code to D using -betterC. Wanted to keep it as close to the original as possible until everything compiles. I noticed that static this() does not work if either it or main() (or both) is marked extern(C). To fix the issue, I simply moved the static this() and main() above the extern(C) and the deprecation error disappears. Andrew
Dec 07 2020
parent Jacob Carlborg <doob me.com> writes:
On Tuesday, 8 December 2020 at 01:47:51 UTC, Andrew Edwards wrote:

 Thanks Jacob. The extern(C) is temporary. I'm doing a direct 
 port of the code to D using -betterC. Wanted to keep it as 
 close to the original as possible until everything compiles.
Yes, that's always a good idea. Do the initial port as close as possible to the original code. Then refactor and start adding D specific features.
 I noticed that static this() does not work if either it or 
 main() (or both) is marked extern(C).
Yes, that's expected. If you have a regular D main function, the compiler will recognize that and output an undefined symbol to the C main function. The C main function is implemented in the D runtime. The C runtime will call the C main function in the D runtime. The C main function is responsible for initializing the D runtime. This includes (not a complete list): * Converting the arguments passed to the C main function to `string[]` * Initializing the garbage collector * Calling `shared static this()` in all modules * Calling `static this()` in all modules * Running the any unit tests * Finally calling the D main function When you define a C main function the compiler will recognize that and not output an undefined symbol. That means the C main function in the D runtime will never be called. That means the D runtime is never initialized. This is to allow you to use D without its runtime or get more control of the startup phase. So either either you define a D main function and everything works as expected or you define a C main function and then you need to manually initialize and deinitialize the D runtime, if you want to use it. This can be done with [1] and [2]. [1] https://dlang.org/phobos/core_runtime.html#.rt_init [2] https://dlang.org/phobos/core_runtime.html#.rt_term -- /Jacob Carlborg
Dec 08 2020
prev sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Monday, 7 December 2020 at 04:13:16 UTC, Andrew Edwards wrote:
 Given:

 ===============
 extern(C):
 char*[] hldr;
Why is this extern(C)? A D array ere is probably wrong. In C, a `char*[] hldr = {...}` is actually represented in D as a static length array, like `char*[1] = [null];` It should also generally be __gshared if it was declared in C (C defaults to global, D defaults to thread-local), and if declared in C, an additional extern may be required. So, if you are trying to *link* to C code that has this: --- // C char* hldr[] = {0}; --- You'd bind to it in D with --- // D extern(C) extern __gshared char*[1] hldr; // matches the C. Make sure the length matches too // rest for your code the same: enum I = (1<<0); struct S { char* ft; char** fm; int f; } S[] s = [S(cast(char*) "c", &hldr[0])]; // that cast is super iffy though. Should S have `const(char)* ft` instead? void main(){} --- In that thing: extern(C) - tells it it has a C name. extern - tells it the actual variable is declared externally. This second one makes the linker pull the variable's address from the C code instead of declaring a new variable in D that can be used from C. __gshared - turn off D's thread local default to match C's global. Now if you are translating C code to D, that might be different, certainly the second extern is wrong then, and you need to copy over the initial value but the rest still works: ``` // no more second extern, D is now creating a new variable, then it needs to know the value too extern(C) __gshared char*[1] hldr = [null]; // rest is the same // including S[] s = [S(cast(char*) "c", &hldr[0])]; // since it is a gshared static array, this still works ``` But there's a few other contexts that might change this, then you probably do want the static constructor, or maybe you can make some things immutable and make it work that way. All depends on a bit more information than your code gives on its own....
Dec 07 2020
parent Andrew Edwards <edwards.ac gmail.com> writes:
On 12/7/20 10:56 PM, Adam D. Ruppe wrote:
 On Monday, 7 December 2020 at 04:13:16 UTC, Andrew Edwards wrote:
 Given:

 ===============
 extern(C):
 char*[] hldr;
Why is this extern(C)? A D array ere is probably wrong.
To stay as close to the original implementation as possible. This will all change when the entire code base compiles in D.
 In C, a `char*[] hldr = {...}` is actually represented in D as a static 
 length array, like `char*[1] = [null];`
Thanks. Because I was having issues I didn't know how to resolve, I actually defined it as `string ft` instead and passed around `ft.ptr` when calling
 It should also generally be __gshared if it was declared in C (C 
 defaults to global, D defaults to thread-local), and if declared in C, 
 an additional extern may be required.
 
Noted. I'll keep that in mind when trying to link to C in the future.
 
 So, if you are trying to *link* to C code that has this:
 
I'm porting it. All of the code is available from the D source file.
 
 You'd bind to it in D with
 
 ---
 // D
 extern(C) extern __gshared char*[1] hldr; // matches the C. Make sure 
 the length matches too
Ok, got it.
 S[] s = [S(cast(char*) "c", &hldr[0])]; // that cast is super iffy 
 though. Should S have `const(char)* ft` instead?
 
Probably. I just did a direct port. They had char* so I just kept it that way. I'll change it to string once the initial port is complete. But I'll remember that when I come across it in the future.
 
 In that thing:
 
 extern(C) - tells it it has a C name.
 extern - tells it the actual variable is declared externally. This 
 second one makes the linker pull the variable's address from the C code 
 instead of declaring a new variable in D that can be used from C.
 __gshared - turn off D's thread local default to match C's global.
 
Learning has occurred. Thanks.
 
 Now if you are translating C code to D, that might be different, 
 certainly the second extern is wrong then, and you need to copy over the 
 initial value but the rest still works:
This.
 ```
 // no more second extern, D is now creating a new variable, then it 
 needs to know the value too
 extern(C) __gshared char*[1] hldr = [null];
Okay. I'll need to be mindful of this in the future. But to resolve the issue this time around, I just used a string[] since that's the end state anyway.
 // rest is the same
 // including
 S[] s = [S(cast(char*) "c", &hldr[0])]; // since it is a gshared static 
 array, this still works
 ```
Yup... got this to compile with main() and static this() in the correct location.
 
 But there's a few other contexts that might change this, then you 
 probably do want the static constructor, or maybe you can make some 
 things immutable and make it work that way. All depends on a bit more 
 information than your code gives on its own....
Thanks for the lesson... I've learned quite a lot.
Dec 07 2020