Thread overview
Linking a C program with D library
Aug 14, 2018
Joe
Aug 15, 2018
Mike Parker
Aug 15, 2018
Joe
Aug 15, 2018
Mike Parker
Aug 15, 2018
Joe
August 14, 2018
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?
August 15, 2018
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.
August 15, 2018
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.
August 15, 2018
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?

August 15, 2018
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.