Thread overview
static arrays in C functions
Dec 08, 2009
Lutger
Dec 08, 2009
Bill Baxter
Dec 08, 2009
Lutger
December 08, 2009
Since a while some extern(C) functions which take arrays seem to be broken. Can anybody clarify /confirm how they should be declared?

For example I stumbled upon this:

import core.sys.posix.unistd, std.stdio;

void main()
{
    int[2] fd;
    writeln( pipe(fd) ); // failes with errno == EFAULT
}

In core.sys.posix.unistd, pipe is declared as: int pipe(int[2]);


This works though:

extern (C) { int pipe(int*); }

void main()
{
    int[2] fd;
    writeln( pipe(fd.ptr) );
}


December 08, 2009
On Tue, Dec 8, 2009 at 2:08 AM, Lutger <lutger.blijdestijn@gmail.com> wrote:
> Since a while some extern(C) functions which take arrays seem to be broken. Can anybody clarify /confirm how they should be declared?
>
> For example I stumbled upon this:
>
> import core.sys.posix.unistd, std.stdio;
>
> void main()
> {
>    int[2] fd;
>    writeln( pipe(fd) ); // failes with errno == EFAULT
> }
>
> In core.sys.posix.unistd, pipe is declared as: int pipe(int[2]);
>
>
> This works though:
>
> extern (C) { int pipe(int*); }
>
> void main()
> {
>    int[2] fd;
>    writeln( pipe(fd.ptr) );
> }

(Assuming you're talking about D2 here...)
A few releases ago fixed-size arrays changed to be pass-by-value.
But I guess there's still some logic in there to interpret int[2] as
int* when inside an extern(C) block.

It does seem like there's a bug there, though.  I think pipe(fd) in the first case should fail to compile because it's attempting to pass by value where a pointer is expected.

--bb
December 08, 2009
On Tue, 08 Dec 2009 11:53:12 -0500, Bill Baxter <wbaxter@gmail.com> wrote:

> On Tue, Dec 8, 2009 at 2:08 AM, Lutger <lutger.blijdestijn@gmail.com> wrote:
>> Since a while some extern(C) functions which take arrays seem to be broken.
>> Can anybody clarify /confirm how they should be declared?
>>
>> For example I stumbled upon this:
>>
>> import core.sys.posix.unistd, std.stdio;
>>
>> void main()
>> {
>>    int[2] fd;
>>    writeln( pipe(fd) ); // failes with errno == EFAULT
>> }
>>
>> In core.sys.posix.unistd, pipe is declared as: int pipe(int[2]);
>>
>>
>> This works though:
>>
>> extern (C) { int pipe(int*); }
>>
>> void main()
>> {
>>    int[2] fd;
>>    writeln( pipe(fd.ptr) );
>> }
>
> (Assuming you're talking about D2 here...)
> A few releases ago fixed-size arrays changed to be pass-by-value.
> But I guess there's still some logic in there to interpret int[2] as
> int* when inside an extern(C) block.

No it compiles *because* that logic is not there.  It now thinks int[2] is a pass-by-value entity.  It links because you are using C linkage which does not do name-mangling.  I could define pipe as:

extern (C) int pipe(char c, int x, float y);

and it will still link :)

> It does seem like there's a bug there, though.  I think pipe(fd) in
> the first case should fail to compile because it's attempting to pass
> by value where a pointer is expected.

The error is either:

a) you now need to declare C functions that were declared in C taking an array to taking a pointer, so core.sys.posix.unistd (and likely others) needs to be fixed.
b) as you suggested, inside a C block, int[2] should be interpreted as int *.

I'd prefer a, because I don't care much about direct translation of C headers :)  If b is chosen as a solution, I'd also like to have the compiler automatically pass the pointer when calling a C function.

-Steve
December 08, 2009
Steven Schveighoffer wrote:

> On Tue, 08 Dec 2009 11:53:12 -0500, Bill Baxter <wbaxter@gmail.com> wrote:
> 
>> On Tue, Dec 8, 2009 at 2:08 AM, Lutger <lutger.blijdestijn@gmail.com> wrote:
>>> Since a while some extern(C) functions which take arrays seem to be
>>> broken.
>>> Can anybody clarify /confirm how they should be declared?
>>>
>>> For example I stumbled upon this:
>>>
>>> import core.sys.posix.unistd, std.stdio;
>>>
>>> void main()
>>> {
>>>    int[2] fd;
>>>    writeln( pipe(fd) ); // failes with errno == EFAULT
>>> }
>>>
>>> In core.sys.posix.unistd, pipe is declared as: int pipe(int[2]);
>>>
>>>
>>> This works though:
>>>
>>> extern (C) { int pipe(int*); }
>>>
>>> void main()
>>> {
>>>    int[2] fd;
>>>    writeln( pipe(fd.ptr) );
>>> }
>>
>> (Assuming you're talking about D2 here...)
>> A few releases ago fixed-size arrays changed to be pass-by-value.
>> But I guess there's still some logic in there to interpret int[2] as
>> int* when inside an extern(C) block.
> 
> No it compiles *because* that logic is not there.  It now thinks int[2] is a pass-by-value entity.  It links because you are using C linkage which does not do name-mangling.  I could define pipe as:
> 
> extern (C) int pipe(char c, int x, float y);
> 
> and it will still link :)

Interesting, I didn't realize that but it makes sense!

> 
>> It does seem like there's a bug there, though.  I think pipe(fd) in the first case should fail to compile because it's attempting to pass by value where a pointer is expected.
> 
> The error is either:
> 
> a) you now need to declare C functions that were declared in C taking an
> array to taking a pointer, so core.sys.posix.unistd (and likely others)
> needs to be fixed.
> b) as you suggested, inside a C block, int[2] should be interpreted as int
> *.
> 
> I'd prefer a, because I don't care much about direct translation of C headers :)  If b is chosen as a solution, I'd also like to have the compiler automatically pass the pointer when calling a C function.
> 
> -Steve

Thanks for the explanation, looks like there is some work to do in the binding department. This looks like a case where a piece of C code silently does something different in D.