www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Translating C "static arrays" into D?

reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
What's the correct translation of the following C declarations into D?

	typedef double[1] mytype;

	void someFunc(mytype x, mytype *y, mytype **z);

	struct SomeStruct {
		mytype x;
		mytype *y;
		mytype **z;
	}

I need this to interface with an external C library.  Currently, I just
wrapped the above as-is inside an extern(C) block.  But I suspect it may
be wrong, because:

1) In C, declaring a function parameter of type double[1] is, IIRC, the
same thing as declaring it as double*.  But in D, double[1] passes one
double by value as a static array. So there may be an API mismatch here.

2) In C, declaring a *variable* or struct field as double[1] has
essentially the same semantics as D's static arrrays.  Meaning that I
cannot just change the declaration of mytype in order to get the correct
behaviour of function parameters.

3) I'm getting a segfault at runtime of some C++ code into D, that calls
the library via this C API, and I suspect it's probably due to (1).


T

-- 
Amateurs built the Ark; professionals built the Titanic.
Feb 26 2018
next sibling parent reply ketmar <ketmar ketmar.no-ip.org> writes:
H. S. Teoh wrote:

 What's the correct translation of the following C declarations into D?

 	typedef double[1] mytype;

 	void someFunc(mytype x, mytype *y, mytype **z);

 	struct SomeStruct {
 		mytype x;
 		mytype *y;
 		mytype **z;
 	}

 I need this to interface with an external C library.  Currently, I just
 wrapped the above as-is inside an extern(C) block.  But I suspect it may
 be wrong, because:

 1) In C, declaring a function parameter of type double[1] is, IIRC, the
 same thing as declaring it as double*.  But in D, double[1] passes one
 double by value as a static array. So there may be an API mismatch here.

 2) In C, declaring a *variable* or struct field as double[1] has
 essentially the same semantics as D's static arrrays.  Meaning that I
 cannot just change the declaration of mytype in order to get the correct
 behaviour of function parameters.

 3) I'm getting a segfault at runtime of some C++ code into D, that calls
 the library via this C API, and I suspect it's probably due to (1).


 T
in C, arrays are *always* decaying to pointers. so void foo (int x[2]) is the same as void foo (int* x) `[2]` is purely informational. that is, in D it will be: alias mytype = double*;
Feb 26 2018
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Mon, Feb 26, 2018 at 08:07:11PM +0200, ketmar via Digitalmars-d wrote:
[...]
 in C, arrays are *always* decaying to pointers. so
 
 	void foo (int x[2])
 
 is the same as
 
 	void foo (int* x)
 
 `[2]` is purely informational.
 
 that is, in D it will be:
 
 	alias mytype = double*;
Actually, that doesn't work, because in the struct declaration it will be wrong: // C struct S { double[5] x; // actually occupies the space of 5 doubles } // D struct S { double* x; // occupies the space of 1 pointer (wrong) } Furthermore, declaring it as `double*` breaks existing code ported from C: // Original C code: void foo(mytype x); mytype z; foo(z); // D code: void foo(double* x); // OK mytype z; // NG foo(z); // NG: passes uninitialized pointer // alternatively: double[1] z; foo(z); // NG: need to insert `&` to compile Eventually I figured out a (hackish) solution to make it work without silently breaking transliterated C code: declare all function parameters that take `mytype` as ref. This causes the D compiler to simulate the array -> pointer degradation semantics but still retain "static array" semantics in structs and variable declarations, without requiring a change in syntax. T -- Food and laptops don't mix.
Feb 26 2018
parent ketmar <ketmar ketmar.no-ip.org> writes:
H. S. Teoh wrote:

 On Mon, Feb 26, 2018 at 08:07:11PM +0200, ketmar via Digitalmars-d wrote:
 [...]
 in C, arrays are *always* decaying to pointers. so
 	void foo (int x[2])
 is the same as
 	void foo (int* x)
 `[2]` is purely informational.
 that is, in D it will be:
 	alias mytype = double*;
Actually, that doesn't work, because in the struct declaration it will be wrong: // C struct S { double[5] x; // actually occupies the space of 5 doubles } // D struct S { double* x; // occupies the space of 1 pointer (wrong) }
yeah, sorry. somehow i completely missed structs.
Feb 26 2018
prev sibling next sibling parent Atila Neves <atila.neves gmail.com> writes:
On Monday, 26 February 2018 at 17:54:12 UTC, H. S. Teoh wrote:
 What's the correct translation of the following C declarations 
 into D?

 	typedef double[1] mytype;
This isn't valid C, but `typedef double mytype[1];` is.
 	void someFunc(mytype x, mytype *y, mytype **z);

 	struct SomeStruct {
 		mytype x;
 		mytype *y;
 		mytype **z;
 	}

 I need this to interface with an external C library.  
 Currently, I just wrapped the above as-is inside an extern(C) 
 block.  But I suspect it may be wrong, because:

 1) In C, declaring a function parameter of type double[1] is, 
 IIRC, the same thing as declaring it as double*.  But in D, 
 double[1] passes one double by value as a static array. So 
 there may be an API mismatch here.
Yes, a `double[1]` parameter in C and `double*` are the same thing. However, that's not valid for the other two parameters (y and z). There's a common misconception in C that arrays and pointers are the same thing - they're not. This comes about because of the dubious C feature whereby arrays decay into pointers in function calls. Somewhat related to this, a little known feature of C is pointers to arrays. In your example, that's what y and z are. They have funky syntax and look like function pointers, unless they're obscured with a typedef as in your example. You can pass a double array of any size to x, or a pointer to double, but y and z are constrained to be pointers to arrays of size 1. Exemplified: typedef double mytype[1]; void func1(mytype x); void func2(mytype* x); int main() { double arr1[1]; double arr2[2]; double* ptr; func1(arr1); // fine func1(arr2); // fine func1(ptr); // fine func2(&arr1); // fine func2(&arr2); // oops - won't compile }
 2) In C, declaring a *variable* or struct field as double[1] 
 has essentially the same semantics as D's static arrrays.  
 Meaning that I cannot just change the declaration of mytype in 
 order to get the correct behaviour of function parameters.

 3) I'm getting a segfault at runtime of some C++ code into D, 
 that calls
 the library via this C API, and I suspect it's probably due to 
 (1).
The correct translation is: extern(C) void someFunc(double* x, double[1]* y, double[1]** z); Atila
Feb 26 2018
prev sibling next sibling parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 2/26/18 12:54 PM, H. S. Teoh wrote:
 What's the correct translation of the following C declarations into D?
 
 	typedef double[1] mytype;
 
 	void someFunc(mytype x, mytype *y, mytype **z);
 
 	struct SomeStruct {
 		mytype x;
 		mytype *y;
 		mytype **z;
 	}
 
 I need this to interface with an external C library.  Currently, I just
 wrapped the above as-is inside an extern(C) block.  But I suspect it may
 be wrong, because:
 
 1) In C, declaring a function parameter of type double[1] is, IIRC, the
 same thing as declaring it as double*.  But in D, double[1] passes one
 double by value as a static array. So there may be an API mismatch here.
If you declare mytype as: alias mytype = double[1]; Then you can use it pretty much anywhere. The only exception is when it's the exact type of a parameter. In that case, use ref: extern(C) void someFunc(ref mytype x, mytype *y, mytype **z); -Steve
Feb 26 2018
prev sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Mon, Feb 26, 2018 at 06:25:42PM +0000, Atila Neves via Digitalmars-d wrote:
[...]
 There's a common misconception in C that arrays and pointers are the
 same thing - they're not. This comes about because of the dubious C
 feature whereby arrays decay into pointers in function calls. Somewhat
 related to this, a little known feature of C is pointers to arrays. In
 your example, that's what y and z are. They have funky syntax and look
 like function pointers, unless they're obscured with a typedef as in
 your example.
 
 You can pass a double array of any size to x, or a pointer to double,
 but y and z are constrained to be pointers to arrays of size 1.
 Exemplified:
 
     typedef double mytype[1];
     void func1(mytype x);
     void func2(mytype* x);
 
     int main() {
         double arr1[1];
         double arr2[2];
         double* ptr;
 
         func1(arr1); // fine
         func1(arr2); // fine
         func1(ptr);  // fine
 
         func2(&arr1); // fine
         func2(&arr2); // oops - won't compile
     }
Ouch. After working with C for more than 20 years, this one still escaped me. :-( How I hate C array semantics... On Mon, Feb 26, 2018 at 01:33:58PM -0500, Steven Schveighoffer via Digitalmars-d wrote: [...]
 If you declare mytype as:
 
 alias mytype = double[1];
 
 Then you can use it pretty much anywhere. The only exception is when
 it's the exact type of a parameter. In that case, use ref:
 
 extern(C) void someFunc(ref mytype x, mytype *y, mytype **z);
[...] Nice, that's also the solution I eventually converged on. T -- Let's eat some disquits while we format the biskettes.
Feb 26 2018