Jump to page: 1 2
Thread overview
string from C function
May 08
Python
Re: string from C function - to!string
3 days ago
Nick Treleaven
2 days ago
Dom DiSc
1 day ago
Kagamin
May 07

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

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

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

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...

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 08

On Wednesday, 7 May 2025 at 22:47:35 UTC, Andy Valencia wrote:

>

So about fromStringz...

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 08

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 08

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

3 days ago
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




3 days ago

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).

3 days ago
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




« First   ‹ Prev
1 2