www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Passing static array to C variadic function

reply Daniel Gibson <metalcaedes gmail.com> writes:
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
Jul 20 2014
next sibling parent "John Colvin" <john.loughran.colvin gmail.com> writes:
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.
Jul 20 2014
prev sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
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
Jul 20 2014
parent reply Daniel Gibson <metalcaedes gmail.com> writes:
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
Jul 20 2014
parent reply "Daniel Murphy" <yebbliesnospam gmail.com> writes:
"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());
Jul 20 2014
parent reply Daniel Gibson <metalcaedes gmail.com> writes:
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
Jul 22 2014
parent reply "Daniel Murphy" <yebbliesnospam gmail.com> writes:
"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.
Jul 22 2014
next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
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
Jul 22 2014
prev sibling parent reply Daniel Gibson <metalcaedes gmail.com> writes:
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
Jul 22 2014
parent "John Colvin" <john.loughran.colvin gmail.com> writes:
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
Jul 22 2014