www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - string from C function

reply Andy Valencia <dont spam.me> writes:
I feel dumb, because this can't be as elusive as it feels.

I'm calling a C library function, and getting a char * (null 
terminated).

Now I want it to be a D string.  What's the right way to do this? 
  Performance and memory use are not important; I just want a 
simple and idiomatic way to get from point A to point B.

TIA,
Andy
May 07
parent reply Dejan Lekic <dejan.lekic gmail.com> writes:
On Wednesday, 7 May 2025 at 22:23:36 UTC, Andy Valencia wrote:
 Now I want it to be a D string.  What's the right way to do 
 this?  Performance and memory use are not important; I just 
 want a simple and idiomatic way to get from point A to point B.
https://dlang.org/library/std/string/from_stringz.html for the opposite see https://dlang.org/library/std/string/to_stringz.html
May 07
next sibling parent Mike Parker <aldacron gmail.com> writes:
On Wednesday, 7 May 2025 at 22:28:32 UTC, Dejan Lekic wrote:

 https://dlang.org/library/std/string/from_stringz.html
Given that `fromStringz` returns a slice of the original string, you can use `std.conv.to` to do it with an allocation when needed: `to!string(str)`.
May 07
prev sibling parent reply Andy Valencia <dont spam.me> writes:
On Wednesday, 7 May 2025 at 22:28:32 UTC, Dejan Lekic wrote:
 https://dlang.org/library/std/string/from_stringz.html
So about fromStringz... ```d import std.string : fromStringz; import core.stdc.time : ctime; void main() { string s = fromStringz(ctime(null)); } ``` Like that? tst44.d(6): Error: cannot implicitly convert expression `fromStringz(ctime(null))` of type `char[]` to `string` Still seeking...
May 07
parent reply Mike Parker <aldacron gmail.com> writes:
On Wednesday, 7 May 2025 at 22:47:35 UTC, Andy Valencia wrote:

 So about fromStringz...

 ```d
 import std.string : fromStringz;
 import core.stdc.time : ctime;

 void
 main() {
     string s = fromStringz(ctime(null));
 }
 ```

 Like that?

 tst44.d(6): Error: cannot implicitly convert expression 
 `fromStringz(ctime(null))` of type `char[]` to `string`

 Still seeking...
`fromStringz` is giving you a slice of a `char*`, typed `char[]`. `string` is `immutable(char)[]`, so you can't assign `char[]` to it. You could: * change the type of `s` to `char[]` * call `.idup` on the array returned from `fromStringz` to allocate a `string` * use `std.conv.to`
May 07
parent reply Andy Valencia <dont spam.me> writes:
On Thursday, 8 May 2025 at 00:53:20 UTC, Mike Parker wrote:
 tst44.d(6): Error: cannot implicitly convert expression 
 `fromStringz(ctime(null))` of type `char[]` to `string`
`fromStringz` is giving you a slice of a `char*`, typed `char[]`. `string` is `immutable(char)[]`, so you can't assign `char[]` to it. You could: * change the type of `s` to `char[]` * call `.idup` on the array returned from `fromStringz` to allocate a `string` * use `std.conv.to`
Thank you. I want to work in strings, so the first one's not an option. But both the second and third do the trick. Would you say the to!string would be the most idiomatic? It worked as "to!string(ctime(&t))", but is it safe to assume it's reliably dealing with null-terminated strings? It's the pithiest if so. Andy
May 07
next sibling parent Python <python python.com> writes:
On Thursday, 8 May 2025 at 04:51:27 UTC, Andy Valencia wrote:
 Thank you.  I want to work in strings, so the first one's not 
 an option.  But both the second and third do the trick.  Would 
 you say the to!string would be the most idiomatic?  It worked 
 as "to!string(ctime(&t))", but is it safe to assume it's 
 reliably dealing with null-terminated strings?  It's the 
 pithiest if so.

 Andy
Yes, it'safe to assume: https://github.com/dlang/phobos/blob/12bcacca2a590942e1b917daaa8732a509b2ef30/std/conv.d#L1087
May 08
prev sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Wednesday, May 7, 2025 10:51:27 PM Mountain Daylight Time Andy Valencia
via Digitalmars-d-learn wrote:
 On Thursday, 8 May 2025 at 00:53:20 UTC, Mike Parker wrote:
 tst44.d(6): Error: cannot implicitly convert expression
 `fromStringz(ctime(null))` of type `char[]` to `string`
`fromStringz` is giving you a slice of a `char*`, typed `char[]`. `string` is `immutable(char)[]`, so you can't assign `char[]` to it. You could: * change the type of `s` to `char[]` * call `.idup` on the array returned from `fromStringz` to allocate a `string` * use `std.conv.to`
Thank you. I want to work in strings, so the first one's not an option. But both the second and third do the trick. Would you say the to!string would be the most idiomatic? It worked as "to!string(ctime(&t))", but is it safe to assume it's reliably dealing with null-terminated strings? It's the pithiest if so.
to!string definitely deals with null-terminated strings, or it wouldn't work at all. It's not the kind of thing that would work by accident. So, the only question is whether it allocates a new string or not, since that could matter depending on what kind of memory the pointer points to (e.g. you don't want a slice of malloc-ed memory outliving when it would be freed). If the mutability of the argument was compatible with that of the requested type (e.g. converting char* to char[] or char* to const(char)[]), then std.conv.to could just slice the pointer from the start of the string up to the null character (which is what fromStringz does), so whether you got a newly allocated array or not would depend on the current implementation, and relying on it allocating or not arguably wouldn't be wise. However, what you're doing is converting a char* to string, and you can't implicitly convert char* to immutable char*, so it's not possible to slice a char* and get a string in safe code. This means that if you pass a char* (or a const(char)*) to to!string, you're guaranteed to get a newly allocated string. Now, fromStringz(ptr).idup is guaranteed to allocate even if ptr is immutable(char)* or immutable(char*), so if there were any concern that you might ever have an immutable(char)* and wanted to guarantee that you were going to allocate a new array, then using fromStringz(ptr).idup would be better, but realistically, you're not going to get immutable(char)* unless you got a ptr from a string, and those are almost always allocated by D's GC (in which case, slicing would be fine). You're not going to get anything that's immutable from C code. And it could be argued that fromStringz(ptr).idup is more idiomatic, because it is clearly allocating without having to even think about whether the implementation could be slicing instead of allocating a copy. But ultimately, whether you use to!string(ptr) or fromStringz(ptr).idup is really a matter of personal preference - especially if you're not dealing with generic code. I suspect that more folks would think that using to!string(ptr) looked better, but I don't know. Personally, I'd probably use fromStringz(ptr).idup just to make the operation explicit, but that's simply my preference. Do whichever you prefer. They both work just fine. - Jonathan M Davis
May 13
parent reply Nick Treleaven <nick geany.org> writes:
On Wednesday, 14 May 2025 at 03:36:40 UTC, Jonathan M Davis wrote:
 to!string definitely deals with null-terminated strings, or it 
 wouldn't work at all. It's not the kind of thing that would 
 work by accident.
I don't think it's good API design:
 Pointer to string conversions convert the pointer to a size_t 
 value. If pointer is char*, treat it as C-style strings. In 
 that case, this function is  system.
So `to!string` is bad for generic code. Want to represent the address of a byte* as hex in a string, fine. Oh now the pointer's element type is char - wait, why isn't the result hex any more? I really dislike overloads having different behaviour like this and it would be nice if Phobos 3 could take a much stricter stance on that. On the docs: This special behaviour is buried as *part* of a single bullet point in long documentation that is not easy to find. (And there's currently no anchor link for that section - each 'Examples:' section should probably have one).
May 14
next sibling parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Wednesday, May 14, 2025 5:38:59 AM Mountain Daylight Time Nick Treleaven
via Digitalmars-d-learn wrote:
 On Wednesday, 14 May 2025 at 03:36:40 UTC, Jonathan M Davis wrote:
 to!string definitely deals with null-terminated strings, or it
 wouldn't work at all. It's not the kind of thing that would
 work by accident.
I don't think it's good API design:
 Pointer to string conversions convert the pointer to a size_t
 value. If pointer is char*, treat it as C-style strings. In
 that case, this function is  system.
So `to!string` is bad for generic code. Want to represent the address of a byte* as hex in a string, fine. Oh now the pointer's element type is char - wait, why isn't the result hex any more? I really dislike overloads having different behaviour like this and it would be nice if Phobos 3 could take a much stricter stance on that. On the docs: This special behaviour is buried as *part* of a single bullet point in long documentation that is not easy to find. (And there's currently no anchor link for that section - each 'Examples:' section should probably have one).
There is certainly an argument that treating char* differently from other pointer types is bad design due to it being inconsistent, but it's also true that how any particular conversion works exactly is going to depend on the types involved. And to an extent, relying on specific conversion behaviors in generic code is going to be error-prone regardless, because at the end of the day, the conversions aren't generic. There are always going to be inconsistencies of some kind. It's just a question of which ones are ultimately more beneficial. There's also an argument to be had that if you want specific conversion behavior for pointers, you should be casting to void*, because then you're no longer dealing with a generic conversion (and it reduces the number of template instantiations to boot). In any case, I'm certainly not going to say what we're going to do with Phobos v3 and std.conv.to with regards to null-terminated strings at this point, since we're not that far along. It will need to be examined when it's ported / reimplemented for Phobos v3, and we'll make decisions then. You make a good point, but there are also good arguments that the current behavior is more user-friendly. So, we'll see. Either way, there's no question that std.conv.to will need to be examined in detail when creating the Phobos v3 version, and we'll be trying to make it cleaner and simpler than what's currently there, since what's currently there is a bit of a maze of templates. However, pretty much the only clear decision at this point is that the new code (whether it's a port of the current code or fully new code) will have at least two variants - one which is just like to and throws on failure (and which will probably still be called to) and another which returns the value wrapped in Phobos v3's optional type rather than throwing an exception (and that overload will probably be called tryTo). That way there will be a conversion function that can be used in code where it's reasonable to assume that the conversion succeeds while the code that can't reasonably make that assumption doesn't have to choose between catching an exception and validating the input to avoid an exception just to have it validated again by the conversion function. - Jonathan M Davis
May 14
prev sibling next sibling parent Dom DiSc <dominikus scherkl.de> writes:
On Wednesday, 14 May 2025 at 11:38:59 UTC, Nick Treleaven wrote:
 On Wednesday, 14 May 2025 at 03:36:40 UTC, Jonathan M Davis 
 wrote:
 to!string definitely deals with null-terminated strings, or it 
 wouldn't work at all. It's not the kind of thing that would 
 work by accident.
I don't think it's good API design:
 Pointer to string conversions convert the pointer to a size_t 
 value. If pointer is char*, treat it as C-style strings. In 
 that case, this function is  system.
So `to!string` is bad for generic code.
Yup. to!T is like a swiss army knife - it can do everything, but it is never as good as a tool dedicated to a specific purpose. So, to!T is good if T can be anything, but if you know beforehand that T will always be string, use something else like ptr.fromStringz.idup
May 14
prev sibling parent reply Kagamin <spam here.lot> writes:
On Wednesday, 14 May 2025 at 11:38:59 UTC, Nick Treleaven wrote:
 So `to!string` is bad for generic code. Want to represent the 
 address of a byte* as hex in a string, fine. Oh now the 
 pointer's element type is char - wait, why isn't the result hex 
 any more?
try `to!(string,void*)`?
May 16
parent Nick Treleaven <nick geany.org> writes:
On Friday, 16 May 2025 at 09:42:20 UTC, Kagamin wrote:
 On Wednesday, 14 May 2025 at 11:38:59 UTC, Nick Treleaven wrote:
 So `to!string` is bad for generic code. Want to represent the 
 address of a byte* as hex in a string, fine. Oh now the 
 pointer's element type is char - wait, why isn't the result 
 hex any more?
try `to!(string,void*)`?
This works, but it relies on an undocumented template parameter AFAICS: ```d char c; alias tos = to!string; tos!(void*)(&c).writeln; ```
May 16