Thread overview
string[] to char**
Mar 23, 2012
simendsjo
Mar 23, 2012
bearophile
Mar 23, 2012
Andrej Mitrovic
Mar 24, 2012
simendsjo
Mar 24, 2012
simendsjo
Mar 24, 2012
simendsjo
Mar 24, 2012
bearophile
Mar 23, 2012
Ali Çehreli
Mar 23, 2012
Ali Çehreli
Mar 24, 2012
simendsjo
March 23, 2012
What's the best way to convert char** from string[]?
March 23, 2012
On Friday, 23 March 2012 at 15:48:12 UTC, simendsjo wrote:
> What's the best way to convert char** from string[]?

This is one way to do it:


import std.algorithm, std.array, std.string, core.stdc.stdio;
void main() {
    auto data = ["red", "yellow", "green"];
    immutable(char)** p = array(map!toStringz(data)).ptr;
    printf("%s %s %s\n", p[0], p[1], p[2]);
}


Note: the pointer array is managed by the D GC.

With DMD 2.059:

immutable(char)** p = data.map!toStringz().array().ptr;

Bye,
bearophile
March 23, 2012
On 03/23/2012 08:48 AM, simendsjo wrote:
> What's the best way to convert char** from string[]?

In C, char** communicates transfer of ownership. Is that what you are trying to do? Are you going to pass a slice to a C function to be filled in by that C function?

Such functions usually assign to *str. In that case you can use the "make slice from raw pointer" method below. I hope others will answer my concern in the comment below:

import std.stdio;
import std.c.stdlib;
import std.c.string;

void C_func_with_an_out_parameter(char ** str)
{
    *str = cast(char*)calloc(1, 10);
    (*str)[0] = 'A';
    (*str)[1] = '\0';
}

void main()
{
    char * p;
    C_func_with_an_out_parameter(&p);

    char[] slice = p[0..strlen(p)];     // <-- make a D slice

    /*
     * Note: I don't think that the memory that
     * std.stdlib.calloc() returns is managed by the GC. For
     * that reason, I don't think it will be safe to share the
     * element with another slice and expect normal behavior
     * of element-sharing between slices.
     *
     * In other words, this would be risky:
     *
     *     char[] anotherSlice = slice;
     */
    writeln(slice);

    free(p);
}

Ali
March 23, 2012
On 03/23/2012 11:23 AM, Ali Çehreli wrote:
> On 03/23/2012 08:48 AM, simendsjo wrote:
>  > What's the best way to convert char** from string[]?
>
> In C, char** communicates transfer of ownership.

Ok, I once again misunderstood the question. :( My question in the comment remains.

Thank you,
Ali

March 23, 2012
On 3/23/12, bearophile <bearophileHUGS@lycos.com> wrote:
> This is one way to do it:
>      immutable(char)** p = array(map!toStringz(data)).ptr;

This is asked so frequently that I think we could consider adding it to Phobos.
March 24, 2012
On Fri, 23 Mar 2012 19:23:07 +0100, Ali Çehreli <acehreli@yahoo.com> wrote:

> On 03/23/2012 08:48 AM, simendsjo wrote:
>  > What's the best way to convert char** from string[]?
>
> In C, char** communicates transfer of ownership. Is that what you are trying to do? Are you going to pass a slice to a C function to be filled in by that C function?
>
> Such functions usually assign to *str. In that case you can use the "make slice from raw pointer" method below. I hope others will answer my concern in the comment below:
>
> import std.stdio;
> import std.c.stdlib;
> import std.c.string;
>
> void C_func_with_an_out_parameter(char ** str)
> {
>      *str = cast(char*)calloc(1, 10);
>      (*str)[0] = 'A';
>      (*str)[1] = '\0';
> }
>
> void main()
> {
>      char * p;
>      C_func_with_an_out_parameter(&p);
>
>      char[] slice = p[0..strlen(p)];     // <-- make a D slice
>
>      /*
>       * Note: I don't think that the memory that
>       * std.stdlib.calloc() returns is managed by the GC. For
>       * that reason, I don't think it will be safe to share the
>       * element with another slice and expect normal behavior
>       * of element-sharing between slices.
>       *
>       * In other words, this would be risky:
>       *
>       *     char[] anotherSlice = slice;
>       */
>      writeln(slice);
>
>      free(p);
> }
>
> Ali

I'm not sure of the semantics of the function yet. Don't know if it copies the argument or stores it, if it frees it or expects me to free it :|
But it's not filling the array.
March 24, 2012
On Fri, 23 Mar 2012 17:09:18 +0100, bearophile <bearophileHUGS@lycos.com> wrote:

> On Friday, 23 March 2012 at 15:48:12 UTC, simendsjo wrote:
>> What's the best way to convert char** from string[]?
>
> This is one way to do it:
>
>
> import std.algorithm, std.array, std.string, core.stdc.stdio;
> void main() {
>      auto data = ["red", "yellow", "green"];
>      immutable(char)** p = array(map!toStringz(data)).ptr;
>      printf("%s %s %s\n", p[0], p[1], p[2]);
> }
>
>
> Note: the pointer array is managed by the D GC.
>
> With DMD 2.059:
>
> immutable(char)** p = data.map!toStringz().array().ptr;
>
> Bye,
> bearophile

Thanks. A lot shorter and safer than my current approach. I also expect toStringz doesn't make a copy if \0 is at the end.

For reference, my current approach was:

auto strings = ["a", "b"];
auto c_strings = cast(char**)malloc((char*).sizeof * strings.length);
for(int i; i < strings.length; i++)
  c_strings[i] = strings[i].ptr;
March 24, 2012
On Sat, 24 Mar 2012 11:41:48 +0100, simendsjo <simendsjo@gmail.com> wrote:

> On Fri, 23 Mar 2012 17:09:18 +0100, bearophile <bearophileHUGS@lycos.com> wrote:
>
>> On Friday, 23 March 2012 at 15:48:12 UTC, simendsjo wrote:
>>> What's the best way to convert char** from string[]?
>>
>> This is one way to do it:
>>
>>
>> import std.algorithm, std.array, std.string, core.stdc.stdio;
>> void main() {
>>      auto data = ["red", "yellow", "green"];
>>      immutable(char)** p = array(map!toStringz(data)).ptr;
>>      printf("%s %s %s\n", p[0], p[1], p[2]);
>> }
>>
>>
>> Note: the pointer array is managed by the D GC.
>>
>> With DMD 2.059:
>>
>> immutable(char)** p = data.map!toStringz().array().ptr;
>>
>> Bye,
>> bearophile
>
> Thanks. A lot shorter and safer than my current approach. I also expect toStringz doesn't make a copy if \0 is at the end.
>
> For reference, my current approach was:
>
> auto strings = ["a", "b"];
> auto c_strings = cast(char**)malloc((char*).sizeof * strings.length);
> for(int i; i < strings.length; i++)
>    c_strings[i] = strings[i].ptr;

Oh, I didn't find toStringz, so I turned it into:

auto c_strings = strings.map!(toUTFz!(char*)).array().ptr;
March 24, 2012
On Fri, 23 Mar 2012 22:42:08 +0100, Andrej Mitrovic <andrej.mitrovich@gmail.com> wrote:

> On 3/23/12, bearophile <bearophileHUGS@lycos.com> wrote:
>> This is one way to do it:
>>      immutable(char)** p = array(map!toStringz(data)).ptr;
>
> This is asked so frequently that I think we could consider adding it to Phobos.

Yes. The first thing I tried was to!(char**)(strings), but that didn't work
March 24, 2012
simendsjo:

> Oh, I didn't find toStringz, so I turned it into:

It's in std.string.

> auto c_strings = strings.map!(toUTFz!(char*)).array().ptr;

That's wrong syntax, that 2.059head doesn't enforce yet, map needs an ending ().


Bye,
bearophile