www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Linking a C program with D library

reply Joe <jma freedomcircle.com> writes:
I'm attempting a piecemeal conversion of some C programs. I've 
converted a few that depend on C modules that are in a library 
and now I'm sort of in the middle of converting those C modules. 
One of them has an array of strings, i.e., array of char*, which 
most easily translated to D's string[]. However, the array is 
used by a function that expects a const char*, so I had to use 
toStringz to pass those values.

Now there are some C programs that haven't been converted yet, 
but they depend on the function above. When linking those 
programs, the linker complains about an undefined reference to 
the mangled name of std.string.toStringz.

What is the most appropriate way around this? Is there a way to 
declare an array of const char* and initialize it to literal 
strings? Or can the mangled name (and Phobos library) somehow be 
made visible to the linker?
Aug 14 2018
parent reply Mike Parker <aldacron gmail.com> writes:
On Tuesday, 14 August 2018 at 23:05:26 UTC, Joe wrote:
 I'm attempting a piecemeal conversion of some C programs. I've 
 converted a few that depend on C modules that are in a library 
 and now I'm sort of in the middle of converting those C 
 modules. One of them has an array of strings, i.e., array of 
 char*, which most easily translated to D's string[]. However, 
 the array is used by a function that expects a const char*, so 
 I had to use toStringz to pass those values.

 Now there are some C programs that haven't been converted yet, 
 but they depend on the function above. When linking those 
 programs, the linker complains about an undefined reference to 
 the mangled name of std.string.toStringz.

 What is the most appropriate way around this? Is there a way to 
 declare an array of const char* and initialize it to literal 
 strings? Or can the mangled name (and Phobos library) somehow 
 be made visible to the linker?
The correct thing to do is to keep the original C function signatures in the converted code, i.e. don't change char* to string[]. And don't use anything from Phobos internally that requires linking. In other words, treat your converted code as C code in a D module as much as possible. Only when the conversion is complete and everything is in D do you start pulling in the D features.
Aug 14 2018
parent reply Joe <jma freedomcircle.com> writes:
On Wednesday, 15 August 2018 at 01:56:34 UTC, Mike Parker wrote:
 The correct thing to do is to keep the original C function 
 signatures in the converted code, i.e. don't change char* to 
 string[]. And don't use anything from Phobos internally that 
 requires linking. In other words, treat your converted code as 
 C code in a D module as much as possible. Only when the 
 conversion is complete and everything is in D do you start 
 pulling in the D features.
I understand that, Mike. However if I'm not mistaken given something in C like char* strs[] = { "This", "is a", "test"}; AFAIK, even with -betterC and an extern (C), the literals will still be understood by D as type "string", and there is no other way around it, right? I could put the array and the function in its own C file for the time being, but instead chose to replace the toStringz by a small hack: use memcpy to copy the string to a stack fixed, big enough array and a NUL terminator.
Aug 14 2018
parent reply Mike Parker <aldacron gmail.com> writes:
On Wednesday, 15 August 2018 at 02:40:22 UTC, Joe wrote:

 I understand that, Mike. However if I'm not mistaken given 
 something in C like

 char* strs[] = { "This", "is a", "test"};

 AFAIK, even with -betterC and an extern (C), the literals will 
 still be understood by D as type "string", and there is no 
 other way around it, right?

 I could put the array and the function in its own C file for 
 the time being, but instead chose to replace the toStringz by a 
 small hack: use memcpy to copy the string to a stack fixed, big 
 enough array and a NUL terminator.
String literals are implicitly convertible to const(char)* and are guaranteed to be nul-terminated like a C string, so this works: import core.stdc.stdio; extern(C) void main() { const(char)* foo = "foo"; puts(foo); } https://run.dlang.io/is/FZSXc3 For plain char*, a simple cast works: char* foo = cast(char*)"foo"; The problem is with the array initializer, as the C-style {x, y, z} only works on structs in D, and D's [x, y, z] requires TypeInfo, which isn't available in -betterC, a problem you run into even when your array is string[]. However, the initializer works fine with static arrays in -betterC, so this works: import core.stdc.stdio; extern(C): void some_c_func(const char** strs, size_t numStrs) { for(size_t i=0; i<numStrs; ++i) { puts(strs[i]); } } void main() { const(char)*[3] strs = ["one", "two", "three"]; some_c_func(strs.ptr, strs.length); } https://run.dlang.io/is/pxmGyh Does that help?
Aug 14 2018
parent Joe <jma freedomcircle.com> writes:
On Wednesday, 15 August 2018 at 06:39:50 UTC, Mike Parker wrote:
 String literals are implicitly convertible to const(char)* and 
 are guaranteed to be nul-terminated like a C string, so this 
 works:

 [...]

 Does that help?
Yes, indeed. I think I possibly read about literal strings being nul-terminated somewhere but it must've slipped my mind.
Aug 15 2018