inout/out static array parameter

Regan Heath regan at netwin.co.nz
Wed Feb 22 02:05:37 PST 2006


On Wed, 22 Feb 2006 16:09:47 +1100, Derek Parnell <derek at psych.ward> wrote:
> On Wed, 22 Feb 2006 17:16:30 +1300, Regan Heath wrote:
>
>> On Wed, 22 Feb 2006 14:15:32 +1100, Derek Parnell <derek at psych.ward>  
>> wrote:
>>> On Wed, 22 Feb 2006 03:02:22 +0000 (UTC), Tom wrote:
>>>
>>>> Why is this illegal? Where does the documentation states this?
>>>> Does it have something to do with the matter that int[10] is allocated
>>>> onto the
>>>> stack? Sometimes I think D losses its abstraction capability when one
>>>> has to be
>>>> concerned about this kind of differences.
>>>>
>>>> 5# void f(inout int[10] x)
>>>> 6# {
>>>> 7# 	x[0..$] = 99;
>>>> 8# }
>>>>
>>>> test.d(5): cannot have out or inout parameter of type int[10]
>>>
>>> I cannot see why D should be upset with this. The implementation would  
>>> be
>>> just be passing the address of the int[10] array, just as it does for
>>> 'in' parameters.
>>
>> Not quite, when you pass something as 'in' you always get a copy of it,
>> this is true even for arrays.
>
> I don't believe that to be entirely accurate. For example, if you pass an
> 'int' as an 'in' parameter you get a 'mov EAX,<var>' generated, but if  
> you pass the same variable as an 'inout' you get a 'lea EAX,<var>'  
> generated.

My knowledge of assembler is practically non existant. Does the 'mov' copy  
the var into EAX? Does 'lea' copy the address of the var into EAX? Is EAX  
the parameter to the function?

Due to my lack of assembler I can't really back any of my thoughts up with  
evidence but I'll try and explain what I think is going on...

> To me this is saying that whenever an 'inout' parameter is passed, it is
> the actual address of that parameter passed. Even when the variable is a
> class instance.

I agree. The key issue is in fact what <var> is in each case. When you  
pass an int <var> is an int, when you pass a class reference <var> is a  
class reference, when you pass an array reference <var> is an array  
reference.

So as not to confuse things I'm using the word "variable" to describe 'a'  
and "parameter" to describe 'b' below.

void function(inout int[10] b) {}
int[10] a;
function(a);

What I believe 'inout' does is alias the variable with the parameter. In  
the case of an array reference <var> is an array reference and the  
parameter 'b' is therefore simply another name for the same array  
reference variable 'a'. Modifying the value of 'b' modifies the value of  
'a' (and vice-versa. I wonder if I can proove this by using a thread to  
modify the variable...)

The same is true for any inout variable.

> In that case an 'in' parameter gets the value of the object reference

I agree.

In all cases an 'in' parameter gets the value of the <var> which is  
passed. If <var> is an int you get a new int with the same value, if <var>  
is a class reference you get a new class reference with the same value, if  
var is an array reference you get a new array reference with the same  
value. In all cases 'in' results in a parameter with the same value as the  
variable. eg.

void function(int[10] b) {}
int[10] a;
function(a);

So, 'b' is a brand new array reference with the same value as 'a'. 'b' is  
a copy of 'a'.

The value of a fixed-length array reference is the address of the first  
item in the array. The value of a variable length array reference is  
different. It is the address of a struct with a length and data pointer.  
In other words both array references are pointers (to different things)  
and their behaviour ends up being the same for both 'in' and 'inout'.

> and an 'inout' gets the address of the object reference. The same with  
> variable-length arrays. However when we get to fixed length arrays,
> the 'in/inout/out' regime is gets fubar'ed.

I don't agree, my reasoning follows..

> You can pass a fixed-length
> array with an 'in' parameter and what get's passed is the address of the
> first item in the array

Or rather, a new array reference which has the same value as the original  
array reference. That value being the memory location of the first item in  
the array.

> - which means that the function is free to modify
> the contents (yes it is an 'in' parameter) but of course the address of  
> the data is not changed (that's what 'in' is really protecting).

Correct.

> So in effect, for fixed-length arrays, 'in' and 'inout' are synonymous.

I don't agree. 'inout' aliases the original array reference. 'in' gives  
you a new array reference with the same value. That is the difference.

> Which is not what
> a normal person would guess. So why not allow 'inout' with fixed-length
> arrays? Isn't this how a coder would tell the reader that he *intends* to
> change the passed array's data and 'in' is telling the reader that the
> coder does NOT intend to change the data.

WRT array/class references/pointers 'in' promises only 1 thing, that the  
function will not modify the original array reference. It can promise this  
because the function always gets a copy of the reference and not the  
original reference itself.

It makes no promise about the content of the array, just like it makes no  
promise about the content of a class.

>> The important thing to realise is exactly
>> what it's passing/copying.
>>
>> In this case:
>>
>> int[10] data;
>> void foo(int[10] a) {}
>> foo(data);
>>
>> You're passing a pointer to the array data, 'in' copies that pointer and
>> passes the copy.
>
> It doesn't actually copy the pointer, it just passes the address of the
> data  ("lea EAX,-8[EBP]").

The "address of the data" is the value of the fixed-length array reference.
Copying that value to a new location creates a new array reference, to the  
same data, a copy of the array reference.
In other words, 'in' copies the array reference.

>> In this case:
>>
>> int[10] data;
>> void foo(inout int[10] a) {}
>> foo(data);
>>
>> You're not passing a copy of the pointer to the array data, you're  
>> passing
>> the actual pointer.
>
> No, that's what 'in' does.

I think we're disagreeing to the meaning of the terms we're using :)  
specifically what "passing" means.

>> As we all know static/fixed arrays have a fixed data
>> pointer which cannot be assigned to or changed in any way. Therefore  
>> it's
>> illegal to pass that pointer as 'inout'.
>
> Yes, I can see the logic of this, given that 'in' is designed to protect
> the reference to the data and not the data itself.

Agreed.

> To have 'inout' in this
> context would imply that the coder is trying to change the address of the
> data and that is obviously not allowed.

Agreed.

> This fundamental principle of D is not explained well enough in the
> documentation.
>
> 'in' protects that data passed to the function such that any change that
> the function performs on the passed data, is not passed back to the  
> calling
> code.
>    For fixed-length arrays the data is the address of first element
>    For variable-length arrays the data is the length of the array
>          and address of the the first element
>    For objects the data is the object reference
>    For structs thee data is the address of the struct contents
>    For intrinsic items the data is the value of the variable.

I didn't realise this documentation even existed :)

I think it does a good job of explaining how it works. I especially like  
that it tells us what the 2 types of array reference actually are.

Regan



More information about the Digitalmars-d mailing list