Thread overview
Passing static array to C variadic function
Jul 20, 2014
Daniel Gibson
Jul 20, 2014
John Colvin
Jul 20, 2014
bearophile
Jul 20, 2014
Daniel Gibson
Jul 21, 2014
Daniel Murphy
Jul 22, 2014
Daniel Gibson
Jul 22, 2014
Daniel Murphy
Jul 22, 2014
bearophile
Jul 22, 2014
Daniel Gibson
Jul 22, 2014
John Colvin
July 20, 2014
Hi,
I have a C variadic function (passed from C code into my D code via function pointer) that I need to call with a static array.

So according to the D documentation, static arrays are passed by value in D2 and by reference in C and D1.
(Even though http://dlang.org/abi.html claims "Static arrays are passed as pointers to their first element." - I guess this just wasn't updated for D2)

For "normal" functions http://dlang.org/interfaceToC.html tells me to add a "ref" in the function signature, to tell D to pass it by reference (couldn't this be implicit for extern (C) functions?) - But I obviously can't to this for vararg function arguments.

So let's imagine the following code:

  extern (C) alias funPtr_t = ptrdiff_t function( ptrdiff_t arg, ... );

  funPtr_t fun = ...; // is assigned somewhere..

  void bla( float[3] v ) {
      fun( 42, v );
  }

This produces the following compiler error (DMD 2.065 linux amd64):
"Error: cannot pass static arrays to extern(C) vararg functions"

/However/, if I typedef a float[3] type, the compiler does not complain (not sure if the code behaves like expected, though, or if it's still passed by value instead of by reference as expected by the C code):

  typedef float[3] vec3_t;

  void bla( vec3_t v ) {
      fun( 42, v );
  }

Then again, if I use alias instead of the deprecated typedef:
  alias vec3_t = float[3];
I again get "Error: cannot pass static arrays to extern(C) vararg functions".


Is there a "proper" way to make this work?
If not, any ideas for a viable workaround?

Cheers,
Daniel
July 20, 2014
On Sunday, 20 July 2014 at 16:00:41 UTC, Daniel Gibson wrote:
> Hi,
> I have a C variadic function (passed from C code into my D code via function pointer) that I need to call with a static array.
>
> So according to the D documentation, static arrays are passed by value in D2 and by reference in C and D1.
> (Even though http://dlang.org/abi.html claims "Static arrays are passed as pointers to their first element." - I guess this just wasn't updated for D2)
>
> For "normal" functions http://dlang.org/interfaceToC.html tells me to add a "ref" in the function signature, to tell D to pass it by reference (couldn't this be implicit for extern (C) functions?) - But I obviously can't to this for vararg function arguments.
>
> So let's imagine the following code:
>
>   extern (C) alias funPtr_t = ptrdiff_t function( ptrdiff_t arg, ... );
>
>   funPtr_t fun = ...; // is assigned somewhere..
>
>   void bla( float[3] v ) {
>       fun( 42, v );
>   }
>
> This produces the following compiler error (DMD 2.065 linux amd64):
> "Error: cannot pass static arrays to extern(C) vararg functions"
>
> /However/, if I typedef a float[3] type, the compiler does not complain (not sure if the code behaves like expected, though, or if it's still passed by value instead of by reference as expected by the C code):
>
>   typedef float[3] vec3_t;
>
>   void bla( vec3_t v ) {
>       fun( 42, v );
>   }
>
> Then again, if I use alias instead of the deprecated typedef:
>   alias vec3_t = float[3];
> I again get "Error: cannot pass static arrays to extern(C) vararg functions".
>
>
> Is there a "proper" way to make this work?
> If not, any ideas for a viable workaround?
>
> Cheers,
> Daniel

C functions takes arrays by pointer to first element. fun(42, v.ptr) should work.
July 20, 2014
Daniel Gibson:

> For "normal" functions http://dlang.org/interfaceToC.html tells me to add a "ref" in the function signature, to tell D to pass it by reference (couldn't this be implicit for extern (C) functions?)

I don't know why D isn't adapting such things to the needs of C.

Bye,
bearophile
July 20, 2014
Am 20.07.2014 18:37, schrieb bearophile:
> Daniel Gibson:
>
>> For "normal" functions http://dlang.org/interfaceToC.html tells me to
>> add a "ref" in the function signature, to tell D to pass it by
>> reference (couldn't this be implicit for extern (C) functions?)
>
> I don't know why D isn't adapting such things to the needs of C.
>
> Bye,
> bearophile

Yeah, IMHO it would indeed make sense to make extern (C) functions behave more like C.
They already are different to D functions (different name mangling and, more visibly to the user, varargs behave differently), so why not do it properly?
As far as I see it, passing static arrays by value to C functions is never what you want because C just doesn't support it, so why not make it implicit? This also goes the other way round: if I implement an extern (C) function in D (vs just having a function pointer or a declaration of a function implemented in C), D "thinks" it's getting a static array by value when it's really just getting a ref..
And for the varargs case (where I can't just add "ref"), it's totally non-obvious that I have to pass staticArr.ptr instead, so here it would be even more desirable to have that done implicitly.

So what I (naive as I am) would expect extern (C) to do to functions is:
* C name mangling (that's done)
* C-style varargs (also done)
* passing stuff to the function is done as C expects it (not done,
  also: are there other cases than the static array one that are
  different?)
* Disallowing template arguments (because how would I declare and call
  that function in C?) (not done)
* /Maybe/ disallowing types as arguments/return types that are not
  supported by C (D classes; not done)?
  (OTOH, one the C side one could just handle them as void* and pass
  instances around opaquely)

Cheers,
Daniel
July 21, 2014

"Daniel Gibson"  wrote in message news:lqh3vb$c2b$1@digitalmars.com...

> * passing stuff to the function is done as C expects it (not done,
>    also: are there other cases than the static array one that are
>    different?)

Dynamic arrays.

D used to allow passing static and dynamic arrays to C varargs with no problems.  This lead to nasty segfaults, especially with this code:

printf("Hello %s\n", "segfault");

which looks perfectly valid in D but certainly isn't.  What you actually meant to pass is up to you to specify.  It would not make a lot of sense for C functions to have a different set of implicit conversion rules to other functions.

> * Disallowing template arguments (because how would I declare and call
>    that function in C?) (not done)

This is probably worth reporting, I doubt it works correctly.

> * /Maybe/ disallowing types as arguments/return types that are not
>    supported by C (D classes; not done)?
>    (OTOH, one the C side one could just handle them as void* and pass
>    instances around opaquely)

Yes, this is why they are not disallowed.  This is just too damn useful.

printf("Pointer: %p\n", new Class()); 

July 22, 2014
Am 21.07.2014 06:07, schrieb Daniel Murphy:
>
>
> "Daniel Gibson"  wrote in message news:lqh3vb$c2b$1@digitalmars.com...
>
>> * passing stuff to the function is done as C expects it (not done,
>>    also: are there other cases than the static array one that are
>>    different?)
>
> Dynamic arrays.
>
> D used to allow passing static and dynamic arrays to C varargs with no
> problems.  This lead to nasty segfaults, especially with this code:
>
> printf("Hello %s\n", "segfault");

If the compiler did the right thing for extern (C) functions (i.e. implicitly passing "segfault" by reference), this shouldn't cause a segfault.

>
> which looks perfectly valid in D but certainly isn't.  What you actually
> meant to pass is up to you to specify.  It would not make a lot of sense
> for C functions to have a different set of implicit conversion rules to
> other functions.
>
>> * Disallowing template arguments (because how would I declare and call
>>    that function in C?) (not done)
>
> This is probably worth reporting, I doubt it works correctly.
>
>> * /Maybe/ disallowing types as arguments/return types that are not
>>    supported by C (D classes; not done)?
>>    (OTOH, one the C side one could just handle them as void* and pass
>>    instances around opaquely)
>
> Yes, this is why they are not disallowed.  This is just too damn useful.
>
> printf("Pointer: %p\n", new Class());

Well, that printf("%s", "asdf"); doesn't work (really?) and that I have to pass static arrays  arr.ptr in varargs, is much more surprising/unexpected than having to casts class objects to void* when passing them to a C function.

But still, it probably is useful to have type checking on the D side even when just treating them as void* on the C side.

Cheers,
Daniel

July 22, 2014
"Daniel Gibson"  wrote in message news:lql6ec$1rqk$1@digitalmars.com...

> >
> > printf("Hello %s\n", "segfault");
>
> If the compiler did the right thing for extern (C) functions (i.e. implicitly passing "segfault" by reference), this shouldn't cause a segfault.

Whether or not passing the point to the C function is the right thing depends on your perspective.

Old D code (from the 32-bit only days) used to do this successfully:

printf("Hello %.*s\n", "segfault");

So it relied on both the length and pointer being passed.  Unfortunately this was done quite a lot, so simply changing the rules so string literals get passed to C varargs as pointers would silently (and horribly) break this code.

Giving an error and forcing you to be explicit about what exactly you wanted is the best that's possible.

> Well, that printf("%s", "asdf"); doesn't work (really?) and that I have to pass static arrays  arr.ptr in varargs, is much more surprising/unexpected than having to casts class objects to void* when passing them to a C function.

That is an error because it was causing bugs, usually when porting C++ code which looks exactly the same.  I've never seen a bug with passing a class handle to printf. 

July 22, 2014
Daniel Murphy:

> Giving an error and forcing you to be explicit about what exactly you wanted is the best that's possible.

OK. This is far better than silent bugs :-)

Bye,
bearophile
July 22, 2014
Am 22.07.2014 11:01, schrieb Daniel Murphy:
>
> Old D code (from the 32-bit only days) used to do this successfully:
>
> printf("Hello %.*s\n", "segfault");
>
> So it relied on both the length and pointer being passed.  Unfortunately
> this was done quite a lot, so simply changing the rules so string
> literals get passed to C varargs as pointers would silently (and
> horribly) break this code.

ok, I didn't know about that hack.. which shouldn't have been used in the first place :-P
So at least for strings it kinda makes sense.. and if strings are considered char arrays (isn't there a proper string type in D2?) for consistency it should be the same for other arrays.
Maybe at least the "Interfacing to C" and/or "C-style Variadic Functions" documentation could supply a bit more information about how to pass arrays and especially static arrays as vararg.

Anyway, thanks for the clarification!

Cheers,
Daniel
July 22, 2014
On Tuesday, 22 July 2014 at 13:28:27 UTC, Daniel Gibson wrote:
> Am 22.07.2014 11:01, schrieb Daniel Murphy:
>>
>> Old D code (from the 32-bit only days) used to do this successfully:
>>
>> printf("Hello %.*s\n", "segfault");
>>
>> So it relied on both the length and pointer being passed.  Unfortunately
>> this was done quite a lot, so simply changing the rules so string
>> literals get passed to C varargs as pointers would silently (and
>> horribly) break this code.

> (isn't there a proper string type in D2?)

nope, it's just an ordinary slice (although the fact that it's a slice of char does make it a bit special w.r.t. unicode)

The compiler doesn't even know about the name string, it's just defined here:
https://github.com/D-Programming-Language/druntime/blob/master/src/object.di#L28