Bug 1869 – Semantically returning an array from a funciton is difficult
Status
RESOLVED
Resolution
FIXED
Severity
enhancement
Priority
P2
Component
dmd
Product
D
Version
D1 (retired)
Platform
x86
OS
Linux
Creation time
2008-02-25T15:33:00Z
Last change time
2014-02-24T15:33:35Z
Keywords
spec
Assigned to
nobody
Creator
jason
Comments
Comment #0 by jason — 2008-02-25T15:33:20Z
It is difficult to 'return' an array from a function. Static arrays cannot be returned, presumably because arrays are akin to full class objects. This sometimes is a bit inconvenient when, for example one would like to assign to a static array by calling a function.
In contrast structs can be returned from a function easily, and in fact to return an array one can wrap it in a struct. Other ways may include the use of boxing, or returning a dynamic array, which becomes more complicated if it is multi-dimensional.
Currently this method seems the best solution, but requires a persistent object allocation as opposed to using the stack (as in structs presumably):
/* Assume matrix is a class member, or static */
float[4][4] * toFloatArray4x4()
{
return &matrix;
}
float v[4][4] = *m1.toFloatArray4x4();
Obviously arrays can also be passed in as parameters, but this denies one the functional return like semantic which can often be useful.
Comment #1 by shro8822 — 2008-02-25T15:46:21Z
The reason that static arrays can't be returned is that they are pointers to local variables. That is in reference to this code:
>int[10] a;
>return a;
however IIRC you can have a function return type int[4]:
>int[4] fn(int i, int[4][4] j) { return j[i]; }
I guess I'm not realy understanding what your concern is.
Comment #2 by jason — 2008-02-25T17:03:49Z
(In reply to comment #1)
> The reason that static arrays can't be returned is that they are pointers to
> local variables. That is in reference to this code:
> >int[10] a;
> >return a;
>
> however IIRC you can have a function return type int[4]:
> >int[4] fn(int i, int[4][4] j) { return j[i]; }
>
> I guess I'm not realy understanding what your concern is.
>
Is this new in D 2.x ?
I get main.d(813): Error: functions cannot return static array int[4u]
with your example.
My example is for a two dimensional array, which up until fairly recently was hard to initialise from a static array (until recently dmd 2.0 changelog)
Is it the case therefore that dmd 2.x supports returning of static arrays?
Comment #3 by shro8822 — 2008-02-25T17:21:32Z
Hm. I'm almost sure I've done that before. And no, I use 1.0.
If my examples don't work, I think it should be considered a bug (in the design if not the implementation).
On seconds though, you point out that a struct can be returned. This will actually be done by value (returning a struct with an int[4] inside will copy 4*int.sizeof bytes) D might be interpreting returning int[4] in that way and refusing (that could get real nasty with some template code computing n=1e6 or some such).
If that is the case, why does it treat it as values on return and as references on pass?
Comment #4 by jason — 2008-02-25T17:47:00Z
(In reply to comment #3)
> Hm. I'm almost sure I've done that before. And no, I use 1.0.
>
> If my examples don't work, I think it should be considered a bug (in the design
> if not the implementation).
>
> On seconds though, you point out that a struct can be returned. This will
> actually be done by value (returning a struct with an int[4] inside will copy
> 4*int.sizeof bytes) D might be interpreting returning int[4] in that way and
> refusing (that could get real nasty with some template code computing n=1e6 or
> some such).
>
> If that is the case, why does it treat it as values on return and as references
> on pass?
>
D is a bit like java and c# in that small intrinsic items like ints, floats are passed by value, the same is true of stucts. It's a trade off between efficiency and pass by value semantics I guess.
An array, and a class are considered largeish things and so it's assumed you will want them to go 'though' by reference. In fact you can't pass an array by value even if you use the "in" keyword. In a way arrays work similarly in C and C++ except in those languages they are pointers and loose type information when you pass them in. The problem in D I seem to be encountering is that there is no nice, or convenient way of returning an array (the problem gets worse with multi dimensional arrays because you have to set them up and until recently in dmd 2.0 you couldn't initialize them in one go AFIK)
I don't have to return an array, but it makes what I am trying to do a bit more clumsy. For example I can't pass into (OpenGL) glMatrixMultiply an array of float[16] by simply calling matrix.toFloat16() I must use a temporary variable, which is a slight pain. Or; I can use my 'pointer workaround' which is bit of a hack.
Comment #5 by shro8822 — 2008-02-25T18:06:51Z
That's about like I remembered.
If you don't mind the cast...
int* foo(int[4][4] j, int i) { return &(j[i]); }
cast(int[4])foo(a,i);
Comment #6 by andrei — 2008-02-25T18:59:17Z
The right thing to do is to manipulate fixed-sized arrays by value, but there are a number of consequent issues to solve, e.g. we wouldn't like this:
writeln("Hello, world!");
to copy the actual content of the string on the stack and over to the callee. Walter is considering making all array constants bind to dynamically-sized types by default, and to statically-sized types only when explicitly requested, e.g.:
auto x = "Hello, world!" // x is string
char[$] y = "Hello, world!" // y is char[13]
char[auto] z = "Hello, world!" // z is char[13]
The syntax is not decided between y's and z's. (Only one at most will be chosen.) The former is better because you can then write:
auto[$] w = "Hello, world!" // w is invariant(char)[13]
whereas auto[auto] would look awkward.
Andrei
Comment #7 by shro8822 — 2008-02-25T19:05:54Z
but that still doesn't solve the base issue: I want a function to return a pointer to a chunk of memory that is 4 ints in a row (or it is that as best the compiler can tell).
(Err. I hope that is understandable)
Comment #8 by andrei — 2008-02-25T19:24:05Z
(In reply to comment #7)
> but that still doesn't solve the base issue: I want a function to return a
> pointer to a chunk of memory that is 4 ints in a row (or it is that as best the
> compiler can tell).
>
> (Err. I hope that is understandable)
I don't understand. Doesn't this work?
int[4]* foo() {
static int[4] x;
return &x;
}
?
Comment #9 by shro8822 — 2008-02-25T22:26:56Z
I don't think so; that is a pointer to a pointer to a string of 4 ints (or a string of 4 pointers to ints, I can never parse those by eye) it has one too many levels of indirection.
Comment #10 by andrei — 2008-02-25T22:48:21Z
(In reply to comment #9)
> I don't think so; that is a pointer to a pointer to a string of 4 ints (or a
> string of 4 pointers to ints, I can never parse those by eye) it has one too
> many levels of indirection.
I'm completely lost. float[4]* has exactly one level of indirection.
Comment #11 by shro8822 — 2008-02-25T23:11:47Z
traditionally arrays are worked by pointer math. While the array is a segment of memory, its manifestation in the code is as a pointer. So "i[2]" takes a pointer adds an offset of 2 and does the IO. From this "int[4]*" will be a pointer to something that can be used like a an array, e.i. it will be a pointer to a somewhat hidden pointer.
IIRC back in C, the compiler didn't make any distinction between an array name and a pointer. In fact they don't worry about what is the array and what is the index.
This actually works (or did at one point):
int i[10];
7[i] = 4;
Comment #12 by andrei — 2008-02-26T01:12:59Z
(In reply to comment #11)
> traditionally arrays are worked by pointer math. While the array is a segment
> of memory, its manifestation in the code is as a pointer. So "i[2]" takes a
> pointer adds an offset of 2 and does the IO. From this "int[4]*" will be a
> pointer to something that can be used like a an array, e.i. it will be a
> pointer to a somewhat hidden pointer.
That is incorrect. There is no hidden pointer and no extra indirection. There is a confusion somewhere along the way, so please let me give an example:
void main()
{
int a[4];
int[4]* b = &a;
a[1] = 2;
(*b)[1] = 3;
}
Say that the four ints in a start at address 1000. Then a is an immutable pointer with value 1000. Also, b is a rebindable pointer with value 1000. Dereferencing b takes you straight to the first element of a, not to some hidden pointer that in turn takes you to the first element of a.
Comment #13 by caron800 — 2008-02-26T01:40:30Z
On 26/02/2008, [email protected] <[email protected]> wrote:
> but that still doesn't solve the base issue: I want a function to return a
> pointer to a chunk of memory that is 4 ints in a row (or it is that as best the
> compiler can tell).
int[] f()
{
return [1,2,3,4].dup;
}
or
int[] f()
{
auto r = new int[4];
/* assign r's elements */
return r;
}
If you need to prove that the array is exactly four bytes long, do
int[] f()
out(r)
{
assert(r.length == 4);
}
body
{
...
}
If you really, really need to return by value, do
struct A(T, int N)
{
T[N] a;
}
A!(int,4) f()
{
A!(int,4) r;
/* assign r.a */
return r;
}
Hopefully, one of these will suit your needs
Comment #14 by jason — 2008-02-26T04:26:02Z
Thanks for the comments, it's more or less as I suspected. The ways of returning an arrays pointed out by Janice Caron and others seem to feel like a 'workaround' to me.
I don't quite understand why the code below does not (or cannot work). The pass by reference semantics of arrays don't have to apply for function return do they? It might create a special case though I suppose.
int[4][4] f()
{
int a[4][4];
return a;
}
Comment #15 by andrei — 2008-02-26T10:53:44Z
(In reply to comment #14)
> Thanks for the comments, it's more or less as I suspected. The ways of
> returning an arrays pointed out by Janice Caron and others seem to feel like a
> 'workaround' to me.
>
> I don't quite understand why the code below does not (or cannot work). The pass
> by reference semantics of arrays don't have to apply for function return do
> they? It might create a special case though I suppose.
>
>
> int[4][4] f()
> {
> int a[4][4];
> return a;
> }
This will work. We'll also pass arrays in by value, and will bind literals to dynamically-sized arrays.
In response to Derek: this is the "right thing" because every type in D has the same copy semantics whether it sits inside a struct or not. Every type... except statically-sized arrays.
Comment #16 by jason — 2008-02-26T11:08:45Z
(In reply to comment #15)
...
> This will work. We'll also pass arrays in by value, and will bind literals to
> dynamically-sized arrays.
>
> In response to Derek: this is the "right thing" because every type in D has the
> same copy semantics whether it sits inside a struct or not. Every type...
> except statically-sized arrays.
>
Ok, I understand now. Seems good. This change will be in DMD 2.x at some future point.
I should of posted to digitalmars.d for this sort of thing instead of raising this as a bug shouldn't I?
Comment #17 by shro8822 — 2008-02-26T12:57:12Z
(In reply to comment #12)
> (In reply to comment #11)
>
> That is incorrect. There is no hidden pointer and no extra indirection. There
> is a confusion somewhere along the way, so please let me give an example:
>
are you talking C or D? I could have sworn that in C a "pointer to a fixed size array" (e.i. double indirection) was written as "int[4]*". If it isn't, how is it written?
Oh. I think I just figured it out!:
int[4] a;
int[4]* i = &a;
i points to the same point as a but has a different type *i is the same pointer as i but with the type of a and **i is the content.
OK what I want to do is return *(&a), a type that is the same as "a" but is mutable.
p.s. Darn I should check the by line before I post
Comment #18 by andrei — 2008-02-26T13:09:18Z
(In reply to comment #17)
> are you talking C or D?
Both.
> I could have sworn that in C a "pointer to a fixed size
> array" (e.i. double indirection) was written as "int[4]*". If it isn't, how is
> it written?
It is written like that, and there is no double indirection.
> Oh. I think I just figured it out!:
>
> int[4] a;
> int[4]* i = &a;
>
> i points to the same point as a but has a different type *i is the same pointer
> as i but with the type of a and **i is the content.
That is correct.
> OK what I want to do is return *(&a), a type that is the same as "a" but is
> mutable.
You'd need to return a reference to a fixed-size array, which in C does not exist, in C++ is spelled int[4]&, and in D is spelled ref int[4] but so far only applies to function arguments.
Note that neither would be mutable, so you seem to want something that is physically possible but can't be expressed in either of the three languages.
I'm keeping this bug report as a request for returning arrays by value, but I guess it's duplicated with a couple others.
Comment #19 by lovesyao — 2008-02-26T17:35:21Z
Reply to Janice,
> On 26/02/2008, [email protected] <[email protected]>
> wrote:
>
>> but that still doesn't solve the base issue: I want a function to
>> return a pointer to a chunk of memory that is 4 ints in a row (or it
>> is that as best the compiler can tell).
>>
>
> Hopefully, one of these will suit your needs
>
None of those do quite what I want (see comment #18 down at the bottom)
Comment #20 by clugdbug — 2009-11-26T08:02:23Z
Fixed DMD2.036: fixed-length arrays are now passed by value.