Thread overview
Convert string[] to char**
Jun 04, 2013
Bruce Smith
Jun 04, 2013
Jacob Carlborg
Jun 04, 2013
bearophile
Jun 04, 2013
Jacob Carlborg
Jun 04, 2013
Bruce Smith
Jun 04, 2013
David
Jun 04, 2013
bearophile
Jun 04, 2013
Peter Alexander
Jun 04, 2013
Bruce Smith
June 04, 2013
Hello

/[I'll prefix this question by stating that I am not a strong C programmer; my long experience is in a wide range of languages other than C//]/

I wish to call some functions in a complex C library from D. Since this a large & complex library, I've wrapped a bunch of the functions using SWIG (swig -d -d2 ...). One of the C functions has a signature like:

void foo(const char * const *keys);

SWIG has given me a D function signature like:

     void foo(char** keys);

In my D program it is natural to represent some keys as an array of strings, for example:

     string[] mykeys;

How should I convert mykeys from string[] to char** so that I can call foo with mykeys as the actual parameter?

Regards




June 04, 2013
On 2013-06-04 09:18, Bruce Smith wrote:
> Hello
>
> /[I'll prefix this question by stating that I am not a strong C
> programmer; my long experience is in a wide range of languages other
> than C//]/
>
> I wish to call some functions in a complex C library from D. Since this
> a large & complex library, I've wrapped a bunch of the functions using
> SWIG (swig -d -d2 ...). One of the C functions has a signature like:
>
> void foo(const char * const *keys);
>
> SWIG has given me a D function signature like:
>
>      void foo(char** keys);
>
> In my D program it is natural to represent some keys as an array of
> strings, for example:
>
>      string[] mykeys;
>
> How should I convert mykeys from string[] to char** so that I can call
> foo with mykeys as the actual parameter?

I think you need to do something like:

import std.conv;

string[] mykeys;
char*[] ckeys;
ckeys.reserve(mykeys.length);

foreach (key ; mykeys)
    ckeys ~= to!(char*)(key);

foo(ckeys.ptr);

-- 
/Jacob Carlborg
June 04, 2013
Jacob Carlborg:

> I think you need to do something like:
>
> import std.conv;
>
> string[] mykeys;
> char*[] ckeys;
> ckeys.reserve(mykeys.length);
>
> foreach (key ; mykeys)
>     ckeys ~= to!(char*)(key);
>
> foo(ckeys.ptr);

An alternative is something like (untested):

auto ckeys = mykeys.map!toStringz.array;

Bye,
bearophile
June 04, 2013
On 2013-06-04 12:41, bearophile wrote:

> An alternative is something like (untested):
>
> auto ckeys = mykeys.map!toStringz.array;

Right, that should work.

-- 
/Jacob Carlborg
June 04, 2013
Thank you Jacob and bearophile ... just had to add a cast to get rid of immutable from your suggestion. For completeness, this compiles under 2.063:

    void foo(char** k)
    {
        // body
    }

    string[] mykeys;
    auto ckeys = cast(char**)mykeys.map!toStringz.array;

    foo(ckeys);

Bruce

On 04/06/13 20:59, Jacob Carlborg wrote:
> On 2013-06-04 12:41, bearophile wrote:
>
>> An alternative is something like (untested):
>>
>> auto ckeys = mykeys.map!toStringz.array;
>
> Right, that should work.
>

June 04, 2013
Am 04.06.2013 13:49, schrieb Bruce Smith:
> Thank you Jacob and bearophile ... just had to add a cast to get rid of immutable from your suggestion. For completeness, this compiles under 2.063:
> 
>     void foo(char** k)
>     {
>         // body
>     }
> 
>     string[] mykeys;
>     auto ckeys = cast(char**)mykeys.map!toStringz.array;
> 
>     foo(ckeys);
> 
> Bruce
> 
> On 04/06/13 20:59, Jacob Carlborg wrote:
>> On 2013-06-04 12:41, bearophile wrote:
>>
>>> An alternative is something like (untested):
>>>
>>> auto ckeys = mykeys.map!toStringz.array;
>>
>> Right, that should work.
>>
> 

Casting it isn't the best way here, you shouldn't cast immutability away, make sure your function does not mutate it (it must not!) then you could change the function signature to const(void*)*, otherwise use char[] instead of string, this should do it:

mykeys.map!(x => x.dup.toStringz).array
June 04, 2013
David:

> mykeys.map!(x => x.dup.toStringz).array

Is toStringz already dup-ping the buffer?

Bye,
bearophile
June 04, 2013
On Tuesday, 4 June 2013 at 12:20:24 UTC, bearophile wrote:
> David:
>
>> mykeys.map!(x => x.dup.toStringz).array
>
> Is toStringz already dup-ping the buffer?

Not necessarily. IIRC, if the string already has a null terminator then it just returns the string ptr. Only dups if it needs to.
June 04, 2013
The C function's signature is determined by the library from which it originates; the D signature created by SWIG is tied to the C original; therefore I can't alter the fact that the parameter is of type char** (i.e. it is mutable).

OTOH, toStringz produces an immutable string.

Bruce

On 04/06/13 22:07, David wrote:
> Am 04.06.2013 13:49, schrieb Bruce Smith:
>> Thank you Jacob and bearophile ... just had to add a cast to get rid of
>> immutable from your suggestion. For completeness, this compiles under
>> 2.063:
>>
>>      void foo(char** k)
>>      {
>>          // body
>>      }
>>
>>      string[] mykeys;
>>      auto ckeys = cast(char**)mykeys.map!toStringz.array;
>>
>>      foo(ckeys);
>>
>> Bruce
>>
>> On 04/06/13 20:59, Jacob Carlborg wrote:
>>> On 2013-06-04 12:41, bearophile wrote:
>>>
>>>> An alternative is something like (untested):
>>>>
>>>> auto ckeys = mykeys.map!toStringz.array;
>>> Right, that should work.
>>>
> Casting it isn't the best way here, you shouldn't cast immutability
> away, make sure your function does not mutate it (it must not!) then you
> could change the function signature to const(void*)*, otherwise use
> char[] instead of string, this should do it:
>
> mykeys.map!(x => x.dup.toStringz).array
>
>