www.digitalmars.com         C & C++   DMDScript  

D - function parameter passing broken?

reply Sean Kelly <sean ffwd.cx> writes:
 From the spec, it sounds like "in" parameters are supposed to be passed 
by value and "inout" parameters are passed by reference.  I suppose 
"out" parameters are passed by reference as well, but I don't care about 
those for the moment.  Here's a simple test:

class A
{
	int v;
}


A copy_1( A a )
{
	return a;
}


A copy_2( A a )
{
	a.v++;
	return a;
}


void test_ref()
{
	A a = new A();
	a.v = 5;
	A b = copy_1( a );
	b.v = 10;
	printf( "%i %i\n", a.v, b.v );
	A c = copy_2( a );
	printf( "%i %i\n", a.v, c.v );
}

Which (to my surprise) prints:

10 10
11 11

My first assumption was that "in" parameters were copied and those 
copies were pushed onto the stack.  Were this true, copy_1 should have 
returned a valid copy of the passed object and the first line should 
have printed:

5 10

My second assumption was that perhaps a copy was only generated if the 
passed object was actually modified.  Were this true, copy_2 should have 
returned a valid copy of the passed object and the second line should 
have printed (assuming copy_1 failed):

10 11

Again not the case.  With "const" specifiers missing from function 
declarations, is there any way to pass object by value or ensure they 
aren't the victim of side-effects?  Is this just a feature that hasn't 
been completed yet?


Sean
Feb 11 2004
next sibling parent reply J Anderson <REMOVEanderson badmama.com.au> writes:
Nar, it's by-design.  You see objects in D are references (pointers) 
themselves. Structs however aren't.

Sean Kelly wrote:

 From the spec, it sounds like "in" parameters are supposed to be 
 passed by value and "inout" parameters are passed by reference.  I 
 suppose "out" parameters are passed by reference as well, but I don't 
 care about those for the moment.  Here's a simple test:

 class A
 {
     int v;
 }


 A copy_1( A a )
 {
     return a;
 }


 A copy_2( A a )
 {
     a.v++;
     return a;
 }


 void test_ref()
 {
     A a = new A();
     a.v = 5;
     A b = copy_1( a );
     b.v = 10;
     printf( "%i %i\n", a.v, b.v );
     A c = copy_2( a );
     printf( "%i %i\n", a.v, c.v );
 }

struct A { int v; } A copy_1( A a ) { return a; } A copy_2( A a ) { a.v++; return a; } void test_ref() { A a; // = new A(); a.v = 5; A b = copy_1( a ); b.v = 10; printf( "%i %i\n", a.v, b.v ); A c = copy_2( a ); printf( "%i %i\n", a.v, c.v ); }
 Which (to my surprise) prints:

 10 10
 11 11

 My first assumption was that "in" parameters were copied and those 
 copies were pushed onto the stack.  Were this true, copy_1 should have 
 returned a valid copy of the passed object and the first line should 
 have printed:

 5 10

 My second assumption was that perhaps a copy was only generated if the 
 passed object was actually modified.  Were this true, copy_2 should 
 have returned a valid copy of the passed object and the second line 
 should have printed (assuming copy_1 failed):

 10 11

 Again not the case.  With "const" specifiers missing from function 
 declarations, is there any way to pass object by value or ensure they 
 aren't the victim of side-effects?  Is this just a feature that hasn't 
 been completed yet?


 Sean

-- -Anderson: http://badmama.com.au/~anderson/
Feb 11 2004
parent reply Sean Kelly <sean ffwd.cx> writes:
J Anderson wrote:
 Nar, it's by-design.  You see objects in D are references (pointers) 
 themselves. Structs however aren't.

So there's no way for a caller to ensure he's safe from side-effects when passing a class to a function? I didn't think this was typical even for GC languages. Sean
Feb 11 2004
next sibling parent reply J Anderson <REMOVEanderson badmama.com.au> writes:
Sean Kelly wrote:

 J Anderson wrote:

 Nar, it's by-design.  You see objects in D are references (pointers) 
 themselves. Structs however aren't.

So there's no way for a caller to ensure he's safe from side-effects when passing a class to a function? I didn't think this was typical even for GC languages.

I'm not 100% sure, but I think it's the same in Java. Although it would be nice to have a read only in D. You could always make a copy of A before you pass it (or after) if you want to modify without side effect. You can also use design by contracts.
 Sean

-- -Anderson: http://badmama.com.au/~anderson/
Feb 11 2004
next sibling parent reply Sean Kelly <sean ffwd.cx> writes:
J Anderson wrote:
 You can also use design by contracts.

Definately. And perhaps that's the out. If so, it makes DBC even more important than I had first considered it. Sean
Feb 11 2004
next sibling parent J Anderson <REMOVEanderson badmama.com.au> writes:
Sean Kelly wrote:

 J Anderson wrote:
 You can also use design by contracts.

Definately. And perhaps that's the out. If so, it makes DBC even more important than I had first considered it. Sean

read-only (const or whatever). -- -Anderson: http://badmama.com.au/~anderson/
Feb 11 2004
prev sibling parent reply Sean Kelly <sean ffwd.cx> writes:
J Anderson wrote:
 IMHO, for such a task DBC is more work with marking it read-only.

And how would DBC work in this case anyway? Say I have a function I want to gurantee will not alter its parameters: class A {} void func( int i, A a ) in { A ain = new A( a ) } out { assert( a == ain ); } body { ... } This doesn't work for two reasons. First, A doesn't have a copy ctor, so I can't create a new A in this way. Second, ain expires when the in block completes. To make matters worse, if func is a template function and A might be either a primitive or object type, I need to be more careful about how I generate the copy. And all this just to produce a "const" equivalent. There has to be a better way.
Feb 11 2004
next sibling parent reply J Anderson <REMOVEanderson badmama.com.au> writes:
Sean Kelly wrote:

 J Anderson wrote:

 IMHO, for such a task DBC is more work with marking it read-only.

And how would DBC work in this case anyway?

You would need to test that the class members don't change. Like I said, it's more work then it's worth. ie template stack (T) //Re-usable { T [] stack; void push(T val) { stack ~= val; } T pop() { T val = stack[stack.length - 1]; stack.length = stack.length - 1; return val; } } class A { int v; char c; void pushCheck() { //Reusable to an extent (nice if we had RTTI for this then we could simply loop through them), or sizeof could be used stack!(int).push(v); stack!(char).push(c); } bool popCheck() //Reusable to an extent { return (stack!(int).pop() == v)?true:false & (stack!(char).pop() == c)?true:false; } } A copy_1( A a ) { return a; } A copy_2( A a ) { a.pushCheck(); a.v++; assert(a.popCheck()); //Make sure it doesn't change return a; } void test_ref() { A a = new A(); a.v = 5; A b = copy_1( a ); b.v = 10; printf( "%i %i\n", a.v, b.v ); A c = copy_2( a ); printf( "%i %i\n", a.v, c.v ); } That's still ugly + it's run-time.
 This doesn't work for two reasons.  First, A doesn't have a copy ctor, 
 so I can't create a new A in this way.  Second, ain expires when the 
 in block completes.  To make matters worse, if func is a template 
 function and A might be either a primitive or object type, I need to 
 be more careful about how I generate the copy.  And all this just to 
 produce a "const" equivalent.  There has to be a better way.

-- -Anderson: http://badmama.com.au/~anderson/
Feb 11 2004
parent J Anderson <REMOVEanderson badmama.com.au> writes:
J Anderson wrote:

 Sean Kelly wrote:

 J Anderson wrote:

 IMHO, for such a task DBC is more work with marking it read-only.

And how would DBC work in this case anyway?

You would need to test that the class members don't change. Like I said, it's more work then it's worth. ie template stack (T) //Re-usable { T [] stack; void push(T val) { stack ~= val; } T pop() { T val = stack[stack.length - 1]; stack.length = stack.length - 1; return val; } } class A { int v; char c; void pushCheck() { //Reusable to an extent (nice if we had RTTI for this then we could simply loop through them), or sizeof could be used

By sizeof I mean taking the entire class and turning it into a memory block, and passing that to the stack. Of couse you wouldn't be able to do deep checks that way. With property RTTI you could write it into the base class.
        stack!(int).push(v);
        stack!(char).push(c);
    }
      bool popCheck() //Reusable to an extent
    {        return (stack!(int).pop() == v)?true:false &
                   (stack!(char).pop() == c)?true:false;
    }
 }



 A copy_1( A a )
 {
    return a;
 }


 A copy_2( A a )
 {
    a.pushCheck();
    a.v++;
    assert(a.popCheck()); //Make sure it doesn't change
    return a;
 }


 void test_ref()
 {
    A a = new A();
    a.v = 5;
    A b = copy_1( a );
    b.v = 10;
    printf( "%i %i\n", a.v, b.v );
    A c = copy_2( a );
    printf( "%i %i\n", a.v, c.v );
 }

 That's still ugly + it's run-time.

 This doesn't work for two reasons.  First, A doesn't have a copy 
 ctor, so I can't create a new A in this way.  Second, ain expires 
 when the in block completes.  To make matters worse, if func is a 
 template function and A might be either a primitive or object type, I 
 need to be more careful about how I generate the copy.  And all this 
 just to produce a "const" equivalent.  There has to be a better way.


-- -Anderson: http://badmama.com.au/~anderson/
Feb 11 2004
prev sibling parent "Matthew" <matthew.hat stlsoft.dot.org> writes:
"Sean Kelly" <sean ffwd.cx> wrote in message
news:c0ea34$136o$1 digitaldaemon.com...
 J Anderson wrote:
  >
 IMHO, for such a task DBC is more work with marking it read-only.

And how would DBC work in this case anyway? Say I have a function I want to gurantee will not alter its parameters: class A {} void func( int i, A a ) in { A ain = new A( a ) } out { assert( a == ain ); } body { ... } This doesn't work for two reasons. First, A doesn't have a copy ctor, so I can't create a new A in this way. Second, ain expires when the in block completes. To make matters worse, if func is a template function and A might be either a primitive or object type, I need to be more careful about how I generate the copy. And all this just to produce a "const" equivalent. There has to be a better way.

It's called const / readonly. :-(
Feb 22 2004
prev sibling next sibling parent reply Sean Kelly <sean ffwd.cx> writes:
J Anderson wrote:
 Sean Kelly wrote:
 
 So there's no way for a caller to ensure he's safe from side-effects 
 when passing a class to a function?  I didn't think this was typical 
 even for GC languages.

I'm not 100% sure, but I think it's the same in Java. Although it would be nice to have a read only in D. You could always make a copy of A before you pass it (or after) if you want to modify without side effect. You can also use design by contracts.

I thought a bit more about this last night and decided the D way is good. Adding const qualifiers for parameter passing would require the existence of const qualifiers for member functions, a mutable qualifier, etc. And then there's the issue of some classes which just plain aren't copyable: streams and other complex objects with state information. I think this is one thing I'm just going to have to get used to. Document the cases where a side-effect may occur and allow the user to make a copy before passing if it's an issue. And I checked the Java spec last night and I think you're right--I could find no mention of by value vs. by reference passing. The fact that structs are copied makes me wonder though. Is it enough just to rely on the optimizer for keeping such operations efficient? I'm inclined to think so. Also, I imagine that using the inout qualifier on POD types will pass them by reference? Sean
Feb 12 2004
next sibling parent reply Ilya Minkov <minkov cs.tum.edu> writes:
Sean Kelly wrote:
 J Anderson wrote:
  >
 
 Sean Kelly wrote:

 So there's no way for a caller to ensure he's safe from side-effects 
 when passing a class to a function?  I didn't think this was typical 
 even for GC languages.

I'm not 100% sure, but I think it's the same in Java. Although it would be nice to have a read only in D. You could always make a copy of A before you pass it (or after) if you want to modify without side effect. You can also use design by contracts.

I thought a bit more about this last night and decided the D way is good. Adding const qualifiers for parameter passing would require the existence of const qualifiers for member functions, a mutable qualifier, etc. And then there's the issue of some classes which just plain aren't copyable: streams and other complex objects with state information. I think this is one thing I'm just going to have to get used to. Document the cases where a side-effect may occur and allow the user to make a copy before passing if it's an issue. And I checked the Java spec last night and I think you're right--I could find no mention of by value vs. by reference passing.

Java doesn't have the distinction. It passes all by quasi-value, which makes integers, characters and floats/doubles (ie primitive types) pass by value, everything else is a class (even if the fact is partially hidden from the user) and thus passes by reference. In general, functions should be written in a safe manner, like the copy-on-write string examples from the manual.
 The fact that structs are copied makes me wonder though.  Is it enough 
 just to rely on the optimizer for keeping such operations efficient? I'm 
 inclined to think so.  Also, I imagine that using the inout qualifier on 
 POD types will pass them by reference?

I don't know how a current implementation decides it, but your struct is admittably tiny - everything not larger than 8 bytes is faster to pass by value than by reference. I would guess that implementation decides how to actually pass. However, value pass semantics stays - this can be done e.g. by detecting whether any change is being made, and if there is, then make a local copy in the beginning of the function. If i remember correctly, Delphi had 2 access modifiers, const and var. Var was the same as inout, const was to disallow any change. While this looks elegant, it has still at least one problem: you can *never* reasonably detect a change on an object, unlike a struct. There are deep reasons to that, if you like i could explain, or you simply may guess them. So i must say i'm satisfied with D solution. Especiall, it doesn't break common code e.g. from Java. And yes, inout on structs makes them definately pass by reference, but you shouldn't do that for performance reasons. Do this only if you actually modify the struct and need the inout semantics. -eye
Feb 12 2004
parent Sean Kelly <sean ffwd.cx> writes:
Ilya Minkov wrote:
 If i remember correctly, Delphi had 2 access modifiers, const and var. 
 Var was the same as inout, const was to disallow any change. While this 
 looks elegant, it has still at least one problem: you can *never* 
 reasonably detect a change on an object, unlike a struct. There are deep 
 reasons to that, if you like i could explain, or you simply may guess 
 them. So i must say i'm satisfied with D solution.

This was my conclusion last night. Once I gave it some thought I realized the complexity involved and decided it wasn't worth the hassle. The D method now makes sense.
 And yes, inout on structs makes them definately pass by reference, but 
 you shouldn't do that for performance reasons. Do this only if you 
 actually modify the struct and need the inout semantics.

Agreed. I was mostly curious for academic reasons as I'm still getting the hang of this language. Sean
Feb 12 2004
prev sibling parent reply "Matthew" <matthew.hat stlsoft.dot.org> writes:
"Sean Kelly" <sean ffwd.cx> wrote in message
news:c0g9td$17gl$1 digitaldaemon.com...
 J Anderson wrote:
  >
 Sean Kelly wrote:

 So there's no way for a caller to ensure he's safe from side-effects
 when passing a class to a function?  I didn't think this was typical
 even for GC languages.

I'm not 100% sure, but I think it's the same in Java. Although it would be nice to have a read only in D. You could always make a copy of A before you pass it (or after) if you want to modify without side effect. You can also use design by contracts.

I thought a bit more about this last night and decided the D way is good. Adding const qualifiers for parameter passing would require the existence of const qualifiers for member functions, a mutable qualifier, etc. And then there's the issue of some classes which just plain aren't copyable: streams and other complex objects with state information.

Nooooooooooooooo! Don't believe the hype. It's bad, bad, bad. This is one important area in which the hunt for compiler simplicity is a specious one.
 I think this is one thing I'm just going to have to get used to.
 Document the cases where a side-effect may occur and allow the user to
 make a copy before passing if it's an issue.  And I checked the Java
 spec last night and I think you're right--I could find no mention of by
 value vs. by reference passing.

Everything is passed by value in Java, including references. A copy of a reference is another reference to the same object. Since the only distinguishing aspect of a refernece is its referent, it looks as if pass-by-reference is being used for references, but it's not. :-)
 The fact that structs are copied makes me wonder though.  Is it enough
 just to rely on the optimizer for keeping such operations efficient?
 I'm inclined to think so.  Also, I imagine that using the inout
 qualifier on POD types will pass them by reference?

We can but hope!
Feb 22 2004
parent reply Sean Kelly <sean ffwd.cx> writes:
Matthew wrote:

 "Sean Kelly" <sean ffwd.cx> wrote in message
 news:c0g9td$17gl$1 digitaldaemon.com...

I thought a bit more about this last night and decided the D way is
good.  Adding const qualifiers for parameter passing would require the
existence of const qualifiers for member functions, a mutable qualifier,
etc.  And then there's the issue of some classes which just plain aren't
copyable: streams and other complex objects with state information.

Nooooooooooooooo! Don't believe the hype. It's bad, bad, bad. This is one important area in which the hunt for compiler simplicity is a specious one.

I'll admit that I would like consts, but I can understand that it's far more than just adding a "const" qualifier to parameters. but with "const" missing, I'd like to add a *strong* recommendation that creators of classes supply a copy ctor.
 Everything is passed by value in Java, including references. A copy of a
 reference is another reference to the same object. Since the only
 distinguishing aspect of a refernece is its referent, it looks as if
 pass-by-reference is being used for references, but it's not. :-)

But it amounts to the same thing. This raises some interesting issues regarding exception safety and "const" gurantees. Can a function writer ever be sure that an object he is passed will be returned in an unaltered state? I'm inclined to think not, unless he always created a copy of any passed object before doing *anything* with it. This is an idea I'm still warming up to. I think I'll have to write some serious code in D before I decide whether this is an issue I'm happy with. Sean
Feb 23 2004
parent "Matthew" <matthew.hat stlsoft.dot.org> writes:
"Sean Kelly" <sean ffwd.cx> wrote in message
news:c1dfo4$2da7$1 digitaldaemon.com...
 Matthew wrote:

 "Sean Kelly" <sean ffwd.cx> wrote in message
 news:c0g9td$17gl$1 digitaldaemon.com...

I thought a bit more about this last night and decided the D way is
good.  Adding const qualifiers for parameter passing would require the
existence of const qualifiers for member functions, a mutable qualifier,
etc.  And then there's the issue of some classes which just plain aren't
copyable: streams and other complex objects with state information.

Nooooooooooooooo! Don't believe the hype. It's bad, bad, bad. This is one important area in which the hunt for compiler simplicity is a specious one.

I'll admit that I would like consts, but I can understand that it's far more than just adding a "const" qualifier to parameters. but with "const" missing, I'd like to add a *strong* recommendation that creators of classes supply a copy ctor.
 Everything is passed by value in Java, including references. A copy of a
 reference is another reference to the same object. Since the only
 distinguishing aspect of a refernece is its referent, it looks as if
 pass-by-reference is being used for references, but it's not. :-)

But it amounts to the same thing. This raises some interesting issues regarding exception safety and "const" gurantees. Can a function writer ever be sure that an object he is passed will be returned in an unaltered state? I'm inclined to think not, unless he always created a copy of any passed object before doing *anything* with it. This is an idea I'm still warming up to. I think I'll have to write some serious code in D before I decide whether this is an issue I'm happy with.

I am not bothered about the language absolutely preventing modification. In fact, I would disagree with that - the Spirit Of C, and all that. All I want is that the programmer cannot alter a readonly variable without making some explicit (preferably ugly and greppable) construct, a la const_cast. I don't need/want the language to try and detect such abuses. One can always abuse in any language. It's about "helping" the programmer to do correct work, rather than standing on his neck to make him do so, or not caring in the first place (which is the current situation). But we're just hot-airing. This has been debated to death, and Walter's not buying.
Feb 23 2004
prev sibling parent reply "Matthew" <matthew.hat stlsoft.dot.org> writes:
"J Anderson" <REMOVEanderson badmama.com.au> wrote in message
news:c0e6rk$t00$1 digitaldaemon.com...
 Sean Kelly wrote:

 J Anderson wrote:

 Nar, it's by-design.  You see objects in D are references (pointers)
 themselves. Structs however aren't.

So there's no way for a caller to ensure he's safe from side-effects when passing a class to a function? I didn't think this was typical even for GC languages.

I'm not 100% sure, but I think it's the same in Java. Although it would be nice to have a read only in D. You could always make a copy of A before you pass it (or after) if you want to modify without side effect. You can also use design by contracts.

No, this is an area where C/C++ got it right, and D/.NET/Java have it quite wrong. Alas Walter is influenced by the practical breakability of C/C++'s const - by casts - and its conceptual splendour seems to have skipped him. But this argument is long gone, and we lost. D will have to be broken in this regard. btw, DBC is *quite* the wrong tool for this - it's a compile time thing. Alas, because we are denied the const (or "readonly", which makes much more sense) keyword, it is our only tool. :(
Feb 22 2004
parent reply J Anderson <REMOVEanderson badmama.com.au> writes:
Matthew wrote:

"J Anderson" <REMOVEanderson badmama.com.au> wrote in message
news:c0e6rk$t00$1 digitaldaemon.com...
  

Sean Kelly wrote:

    

J Anderson wrote:
      

Nar, it's by-design.  You see objects in D are references (pointers)
themselves. Structs however aren't.
        

when passing a class to a function? I didn't think this was typical even for GC languages.

be nice to have a read only in D. You could always make a copy of A before you pass it (or after) if you want to modify without side effect. You can also use design by contracts.

No, this is an area where C/C++ got it right, and D/.NET/Java have it quite wrong.

wouldn't be nice to have a read-only in D? No you can't make a copy before/after passing it?
Alas Walter is influenced by the practical breakability of C/C++'s const -
by casts - and its conceptual splendour seems to have skipped him.

But this argument is long gone, and we lost. D will have to be broken in
this regard.

btw, DBC is *quite* the wrong tool for this - it's a compile time thing.
Alas, because we are denied the const (or "readonly", which makes much more
sense) keyword, it is our only tool. :(
  

-- -Anderson: http://badmama.com.au/~anderson/
Feb 22 2004
parent "Matthew" <matthew.hat stlsoft.dot.org> writes:
"J Anderson" <REMOVEanderson badmama.com.au> wrote in message
news:c1c89p$54m$1 digitaldaemon.com...
 Matthew wrote:

"J Anderson" <REMOVEanderson badmama.com.au> wrote in message
news:c0e6rk$t00$1 digitaldaemon.com...


Sean Kelly wrote:



J Anderson wrote:


Nar, it's by-design.  You see objects in D are references (pointers)
themselves. Structs however aren't.

when passing a class to a function? I didn't think this was typical even for GC languages.

be nice to have a read only in D. You could always make a copy of A before you pass it (or after) if you want to modify without side effect. You can also use design by contracts.

No, this is an area where C/C++ got it right, and D/.NET/Java have it


wrong.


It was no to DBC (in this case)
Feb 22 2004
prev sibling parent "Matthew" <matthew.hat stlsoft.dot.org> writes:
"Sean Kelly" <sean ffwd.cx> wrote in message
news:c0e6jd$sr0$1 digitaldaemon.com...
 J Anderson wrote:
  >
 Nar, it's by-design.  You see objects in D are references (pointers)
 themselves. Structs however aren't.

So there's no way for a caller to ensure he's safe from side-effects when passing a class to a function? I didn't think this was typical even for GC languages.

Sounds completely typical for GC languages.
Feb 22 2004
prev sibling parent "davepermen" <davepermen hotmail.com> writes:
A a and A b are both references to the same object. you can only have
references to class-types. you newed only one object. this is the only one
existing in your whole app

"Sean Kelly" <sean ffwd.cx> schrieb im Newsbeitrag
news:c0e5ma$rb2$1 digitaldaemon.com...
 From the spec, it sounds like "in" parameters are supposed to be passed
 by value and "inout" parameters are passed by reference.  I suppose
 "out" parameters are passed by reference as well, but I don't care about
 those for the moment.  Here's a simple test:

 class A
 {
 int v;
 }


 A copy_1( A a )
 {
 return a;
 }


 A copy_2( A a )
 {
 a.v++;
 return a;
 }


 void test_ref()
 {
 A a = new A();
 a.v = 5;
 A b = copy_1( a );
 b.v = 10;
 printf( "%i %i\n", a.v, b.v );
 A c = copy_2( a );
 printf( "%i %i\n", a.v, c.v );
 }

 Which (to my surprise) prints:

 10 10
 11 11

 My first assumption was that "in" parameters were copied and those
 copies were pushed onto the stack.  Were this true, copy_1 should have
 returned a valid copy of the passed object and the first line should
 have printed:

 5 10

 My second assumption was that perhaps a copy was only generated if the
 passed object was actually modified.  Were this true, copy_2 should have
 returned a valid copy of the passed object and the second line should
 have printed (assuming copy_1 failed):

 10 11

 Again not the case.  With "const" specifiers missing from function
 declarations, is there any way to pass object by value or ensure they
 aren't the victim of side-effects?  Is this just a feature that hasn't
 been completed yet?


 Sean

Feb 11 2004