digitalmars.D.learn - Ref parameter: intended behavior or bug?
- Mike Parker (42/42) Aug 21 2007 I knocked up a sample program to demonstrate reference parameters, then
- BCS (4/51) Aug 21 2007 I think the compiler is playing games with the perceived type of ref arg...
- Bill Baxter (5/58) Aug 21 2007 'ref' is not a type constructor in D, it is a storage class.
- Mike Parker (9/66) Aug 22 2007 But it's a bit inconsistent, is it not? Consider this function:
- Regan Heath (41/112) Aug 22 2007 Of course, you're asking for the size of a pointer here and a pointer on...
- Jarrett Billingsley (4/15) Aug 22 2007 If D required 'ref' and 'out' at call site like C#, this wouldn't be an
- Regan Heath (19/40) Aug 22 2007 True. I think I'd prefer just to use pointers.
- Mike Parker (8/15) Aug 22 2007 Yeah, I get it, but it still /feels/ inconsistent to me. If the size of
- Bill Baxter (12/29) Aug 22 2007 Consider the sizeof to mean how much space you would have to reserve if
- Jarrett Billingsley (10/15) Aug 22 2007 Right, which is more of how I've understood Walter's intentions for 'ref...
- Frits van Bommel (11/26) Aug 22 2007 Because 'ref' also adds extra '*'s in front of every place the variable
I knocked up a sample program to demonstrate reference parameters, then got a result that wasn't at all what I expected: ==================================== import std.stdio; struct MyStruct { ubyte[] contents; } class MyClass { ubyte[] contents; } void structDefault(MyStruct ms) { writefln("Struct default: %d", ms.sizeof); } void structRef(ref MyStruct ms) { writefln("Struct ref: %d", ms.sizeof); } void classDefault(MyClass mc) { writefln("Class default: %d", mc.sizeof); } void main() { MyStruct ms; MyClass mc = new MyClass; structDefault(ms); structRef(ms); classDefault(mc); } ================================== Here's the output: Struct default: 8 Struct ref: 8 Class default: 4 The first and last are what I thought they would be, but I expected the Struct ref line to output 4, since it's supposed to be a ref parameter. I assume this to be a bug, but want to make sure there's not something intended going on.
Aug 21 2007
Reply to Mike,I knocked up a sample program to demonstrate reference parameters, then got a result that wasn't at all what I expected: ==================================== import std.stdio; struct MyStruct { ubyte[] contents; } class MyClass { ubyte[] contents; } void structDefault(MyStruct ms) { writefln("Struct default: %d", ms.sizeof); } void structRef(ref MyStruct ms) { writefln("Struct ref: %d", ms.sizeof); } void classDefault(MyClass mc) { writefln("Class default: %d", mc.sizeof); } void main() { MyStruct ms; MyClass mc = new MyClass; structDefault(ms); structRef(ms); classDefault(mc); } ================================== Here's the output: Struct default: 8 Struct ref: 8 Class default: 4 The first and last are what I thought they would be, but I expected the Struct ref line to output 4, since it's supposed to be a ref parameter. I assume this to be a bug, but want to make sure there's not something intended going on.I think the compiler is playing games with the perceived type of ref arguments. I would guess that the type of ms in strutRef would end up being the same as the type in structDefault.
Aug 21 2007
Mike Parker wrote:I knocked up a sample program to demonstrate reference parameters, then got a result that wasn't at all what I expected: ==================================== import std.stdio; struct MyStruct { ubyte[] contents; } class MyClass { ubyte[] contents; } void structDefault(MyStruct ms) { writefln("Struct default: %d", ms.sizeof); } void structRef(ref MyStruct ms) { writefln("Struct ref: %d", ms.sizeof); } void classDefault(MyClass mc) { writefln("Class default: %d", mc.sizeof); } void main() { MyStruct ms; MyClass mc = new MyClass; structDefault(ms); structRef(ms); classDefault(mc); } ================================== Here's the output: Struct default: 8 Struct ref: 8 Class default: 4 The first and last are what I thought they would be, but I expected the Struct ref line to output 4, since it's supposed to be a ref parameter. I assume this to be a bug, but want to make sure there's not something intended going on.'ref' is not a type constructor in D, it is a storage class. .sizeof gives the size of the type. The storage class shouldn't affect that. --bb
Aug 21 2007
Bill Baxter wrote:Mike Parker wrote:But it's a bit inconsistent, is it not? Consider this function: void structPtr(MyStruct* ms) { writefln("Struct ptr: %d", ms.sizeof); } This will print 4. Why should the behavior of 'ref' be any different than that of '*'? If I want the size of the type, I would use MyStruct.sizeof.I knocked up a sample program to demonstrate reference parameters, then got a result that wasn't at all what I expected: ==================================== import std.stdio; struct MyStruct { ubyte[] contents; } class MyClass { ubyte[] contents; } void structDefault(MyStruct ms) { writefln("Struct default: %d", ms.sizeof); } void structRef(ref MyStruct ms) { writefln("Struct ref: %d", ms.sizeof); } void classDefault(MyClass mc) { writefln("Class default: %d", mc.sizeof); } void main() { MyStruct ms; MyClass mc = new MyClass; structDefault(ms); structRef(ms); classDefault(mc); } ================================== Here's the output: Struct default: 8 Struct ref: 8 Class default: 4 The first and last are what I thought they would be, but I expected the Struct ref line to output 4, since it's supposed to be a ref parameter. I assume this to be a bug, but want to make sure there's not something intended going on.'ref' is not a type constructor in D, it is a storage class. .sizeof gives the size of the type. The storage class shouldn't affect that.
Aug 22 2007
Mike Parker wrote:Bill Baxter wrote:Of course, you're asking for the size of a pointer here and a pointer on a 32 bit system is _always_ 32 bits or 4 bytes.Mike Parker wrote:But it's a bit inconsistent, is it not? Consider this function: void structPtr(MyStruct* ms) { writefln("Struct ptr: %d", ms.sizeof); } This will print 4.I knocked up a sample program to demonstrate reference parameters, then got a result that wasn't at all what I expected: ==================================== import std.stdio; struct MyStruct { ubyte[] contents; } class MyClass { ubyte[] contents; } void structDefault(MyStruct ms) { writefln("Struct default: %d", ms.sizeof); } void structRef(ref MyStruct ms) { writefln("Struct ref: %d", ms.sizeof); } void classDefault(MyClass mc) { writefln("Class default: %d", mc.sizeof); } void main() { MyStruct ms; MyClass mc = new MyClass; structDefault(ms); structRef(ms); classDefault(mc); } ================================== Here's the output: Struct default: 8 Struct ref: 8 Class default: 4 The first and last are what I thought they would be, but I expected the Struct ref line to output 4, since it's supposed to be a ref parameter. I assume this to be a bug, but want to make sure there's not something intended going on.'ref' is not a type constructor in D, it is a storage class. .sizeof gives the size of the type. The storage class shouldn't affect that.Why should the behavior of 'ref' be any different than that of '*'?Because 'ref' is not '*'. To turn it around; Why should the behaviour of 'ref' be the same as '*'? Is the only point of having 'ref' to allow you to pass variables without & at the call site? I believe the main reason for 'ref' was/is to allow you to modify the variable being passed. So what advantage does 'ref' have over '*'? If you ask me, in D, 'ref' has perhaps 1 small advantage, and 1 large liability. The advantage: void foo(ref int a) { a = 5; } //cleaner syntax void foo(int* a) { *a = 5; } Consider a struct however: struct Foo { int a; } void foo(ref Foo f) { f.a = 5; } void foo(Foo* f) { f.a = 5; } //identical syntax Granted you still have: struct Foo { int a; } void foo(ref Foo f) { f = <some other Foo>; } //cleaner syntax void foo(Foo* f) { *f = <some other Foo>; } However, the liability (IMO): Which of these functions modifies 'foo'? Foo foo; bar(foo); baz(foo); You can't tell without inspecting the function signatures. Whereas with pointers: Foo foo; bar(&foo); baz(foo); It's quite clear, unless of course baz is passing by reference ;)If I want the size of the type, I would use MyStruct.sizeof.I think the concept of passing by reference is that when you pass by ref the parameter _is_ the original variable in every possible way. The compiler might be using a proxy object to implement this, it might not be, IANACW. As such when you ask for the size of a ref parameter you're asking for the size of the original variable, which in this case is the size of the struct itself. Regan
Aug 22 2007
"Regan Heath" <regan netmail.co.nz> wrote in message news:fah2ic$2e20$1 digitalmars.com...However, the liability (IMO): Which of these functions modifies 'foo'? Foo foo; bar(foo); baz(foo); You can't tell without inspecting the function signatures. Whereas with pointers: Foo foo; bar(&foo); baz(foo); It's quite clear, unless of course baz is passing by reference ;)issue.
Aug 22 2007
Jarrett Billingsley wrote:"Regan Heath" <regan netmail.co.nz> wrote in message news:fah2ic$2e20$1 digitalmars.com...True. I think I'd prefer just to use pointers. Then, you're writing & at call site and *param when you're modifying the parameter itself, both of which are explicit and easy to see. D automatically dereferences member operations for pointers to structs so the syntax is already fine there. The one problem you have are operator overloads, eg. struct Foo { ... } Foo foo(Foo* a, Foo* b) { return a * b; } has to be written: Foo foo(Foo* a, Foo* b) { return *a * *b; } which is a bit ick. ReganHowever, the liability (IMO): Which of these functions modifies 'foo'? Foo foo; bar(foo); baz(foo); You can't tell without inspecting the function signatures. Whereas with pointers: Foo foo; bar(&foo); baz(foo); It's quite clear, unless of course baz is passing by reference ;)issue.
Aug 22 2007
Regan Heath wrote:I think the concept of passing by reference is that when you pass by ref the parameter _is_ the original variable in every possible way. The compiler might be using a proxy object to implement this, it might not be, IANACW. As such when you ask for the size of a ref parameter you're asking for the size of the original variable, which in this case is the size of the struct itself.Yeah, I get it, but it still /feels/ inconsistent to me. If the size of a class reference is 4 bytes, then it seems to me that the size of a ref parameter should be the same and not the size of the object to which it refers. But I suppose we aren't supposed to think of a ref parameter as a true reference, but rather as a mutable view of the original data. In which case, 'inout' seems more appropriate, in retrospect, than 'ref'. OK, misconception cleared, situation grokked.
Aug 22 2007
Mike Parker wrote:Regan Heath wrote:Consider the sizeof to mean how much space you would have to reserve if you were going to copy the thing into opaque memory somewhere. If you try to copy a class reference then you need exactly 4 bytes. (You cannot make a copy of the thing referred to.) But if you tried to make a copy of a ref-passed struct{float x,y,z} then you would indeed need 12 bytes because trying to copy the ref parameter will actually copy the thing referred to. You can't make a copy of the reference itself. At least not without dipping into pointer shenanigans.I think the concept of passing by reference is that when you pass by ref the parameter _is_ the original variable in every possible way. The compiler might be using a proxy object to implement this, it might not be, IANACW. As such when you ask for the size of a ref parameter you're asking for the size of the original variable, which in this case is the size of the struct itself.Yeah, I get it, but it still /feels/ inconsistent to me. If the size of a class reference is 4 bytes, then it seems to me that the size of a ref parameter should be the same and not the size of the object to which it refers.But I suppose we aren't supposed to think of a ref parameter as a true reference, but rather as a mutable view of the original data. In which case, 'inout' seems more appropriate, in retrospect, than 'ref'.Except someday soon we'll probably have working 'const ref' parameters, and 'const inout' makes no sense. --bb
Aug 22 2007
"Mike Parker" <aldacron71 yahoo.com> wrote in message news:faiicj$2hms$1 digitalmars.com...Yeah, I get it, but it still /feels/ inconsistent to me. If the size of a class reference is 4 bytes, then it seems to me that the size of a ref parameter should be the same and not the size of the object to which it refers. But I suppose we aren't supposed to think of a ref parameter as a true reference, but rather as a mutable view of the original data.Right, which is more of how I've understood Walter's intentions for 'ref': the semantics imply a mutable view of a piece of data, but the actual implementation should not be revealed. The compiler could, for example, pass the parameter by value into the function, and then when the function returns, copy that parameter back into where it came from, in which case no actual pointers are being used. Of course this is a roundabout and ineffecient way of implementing 'ref', but according to the semantics, a valid one.
Aug 22 2007
Mike Parker wrote:Bill Baxter wrote:Because 'ref' also adds extra '*'s in front of every place the variable occurs, so your code with 'ref' is more like ----- void structPtr(ref MyStruct ms) { writefln("Struct ptr: %d", (*ms).sizeof); } ----- so you print the size of the struct referred to, not the size of the reference itself.'ref' is not a type constructor in D, it is a storage class. .sizeof gives the size of the type. The storage class shouldn't affect that.But it's a bit inconsistent, is it not? Consider this function: void structPtr(MyStruct* ms) { writefln("Struct ptr: %d", ms.sizeof); } This will print 4. Why should the behavior of 'ref' be any different than that of '*'? If I want the size of the type, I would use MyStruct.sizeof.
Aug 22 2007