digitalmars.D - C++ reference type
- Tesuji (1/1) Jun 24 2006 Will C++ reference type ever be included in D? In one form or the other?
- Yossarian (7/10) Jun 24 2006 what do you mean? every complex object is internally passed by
- Hasan Aljudy (2/13) Jun 24 2006 The only thing missing is a reference return type.
- David Medlock (10/26) Jun 24 2006 The attribute functions in classes serve this purpose do they not?
- Tom S (7/17) Jun 24 2006 Unless you want to be able to say 'foo.my_attr++;'
- David Medlock (10/31) Jun 24 2006 Ok, but what exactly does that buy you?
- Tom S (22/57) Jun 24 2006 Excuse me, but I have no idea what you're talking about. Reference
- Andrei Khropov (4/71) Jun 24 2006 Yeah, I agree, D constraints in these cases are really irritating.
- David Medlock (39/101) Jun 24 2006 This should have read 'value semantics' not opPostInc.
- Tesuji (2/29) Jun 25 2006
- Tom S (40/114) Jun 25 2006 If it requires you to create workarounds all over the place, makes code
- David Medlock (21/138) Jun 25 2006 I meant in the case you returned a pointer, but yes that wouldn't follow...
- Tom S (31/50) Jun 25 2006 That's the approach I've proposed some time ago to someone implementing
- Dave (33/47) Jun 24 2006 There's one more irritating limitation, and that is the ability to pass
Will C++ reference type ever be included in D? In one form or the other?
Jun 24 2006
Tesuji napsal(a):Will C++ reference type ever be included in D? In one form or the other?what do you mean? every complex object is internally passed by reference, and if you want the same behaviour as in c++ references, try inout keyword in declaration: D: void func(inout int a); is equal to C++: void func(int &a);
Jun 24 2006
Yossarian wrote:Tesuji napsal(a):The only thing missing is a reference return type.Will C++ reference type ever be included in D? In one form or the other?what do you mean? every complex object is internally passed by reference, and if you want the same behaviour as in c++ references, try inout keyword in declaration: D: void func(inout int a); is equal to C++: void func(int &a);
Jun 24 2006
Hasan Aljudy wrote:Yossarian wrote:The attribute functions in classes serve this purpose do they not? C++: int& my_attr(); D: void my_attr( int ); -DavidM use, in both cases: foo.my_attr = 100; -DavidMTesuji napsal(a):The only thing missing is a reference return type.Will C++ reference type ever be included in D? In one form or the other?what do you mean? every complex object is internally passed by reference, and if you want the same behaviour as in c++ references, try inout keyword in declaration: D: void func(inout int a); is equal to C++: void func(int &a);
Jun 24 2006
David Medlock wrote:The attribute functions in classes serve this purpose do they not? C++: int& my_attr(); D: void my_attr( int ); use, in both cases: foo.my_attr = 100;Unless you want to be able to say 'foo.my_attr++;' or 'foo.someStruct.bar = 1;' The latter can be accomplished by making the function return a pointer to the struct, but then you cant say 'Foo x = my_attr();' -- Tomasz Stachowiak /+ a.k.a. h3r3tic +/
Jun 24 2006
Tom S wrote:David Medlock wrote:Ok, but what exactly does that buy you? If you want reference semantics use a class. If you want opPostInc use a struct. Even without you could just as easily say: int bar(int r) { return this.someStruct.bar = r; } if you have several 'bars' perhaps its time to refactor? Its like saying : I cant ride my car on bike trails, even though I can ride my bike on roads. Just use the bike. -DavidMThe attribute functions in classes serve this purpose do they not? C++: int& my_attr(); D: void my_attr( int ); use, in both cases: foo.my_attr = 100;Unless you want to be able to say 'foo.my_attr++;' or 'foo.someStruct.bar = 1;' The latter can be accomplished by making the function return a pointer to the struct, but then you cant say 'Foo x = my_attr();'
Jun 24 2006
David Medlock wrote:Tom S wrote:Excuse me, but I have no idea what you're talking about. Reference return types are useful in many cases. E.g. if you want to implement your own Array class. With normal arrays you can say: int[] arr1; ... ; arr1[0] += 2; but when you define your own array, like: Array!(int) arr2; ... ; then how do you implement 'arr2[0] += 2;' ? You can't, because there are only opIndex and opIndexAssign operators. There's no opIndexAddAssign and opIndex*Assign for that purpose :0 This gets worse, e.g. if you have a struct Foo { int bar; } Foo[] arr3; ... ; arr3[0].bar = 1; but when you define Array!(Foo) arr4; ... ; then to accomplish the 'arr4[0].bar = 1;' functionality, you have to make opIndex return a pointer. Yet then you can't say 'Foo x = arr4[0];' because there's a type mismatch. You'd have to dereference the pointer. -- Tomasz Stachowiak /+ a.k.a. h3r3tic +/David Medlock wrote:Ok, but what exactly does that buy you? If you want reference semantics use a class. If you want opPostInc use a struct. Even without you could just as easily say: int bar(int r) { return this.someStruct.bar = r; } if you have several 'bars' perhaps its time to refactor? Its like saying : I cant ride my car on bike trails, even though I can ride my bike on roads. Just use the bike.The attribute functions in classes serve this purpose do they not? C++: int& my_attr(); D: void my_attr( int ); use, in both cases: foo.my_attr = 100;Unless you want to be able to say 'foo.my_attr++;' or 'foo.someStruct.bar = 1;' The latter can be accomplished by making the function return a pointer to the struct, but then you cant say 'Foo x = my_attr();'
Jun 24 2006
Tom S wrote:David Medlock wrote:Yeah, I agree, D constraints in these cases are really irritating. -- AKhropovTom S wrote:Excuse me, but I have no idea what you're talking about. Reference return types are useful in many cases. E.g. if you want to implement your own Array class. With normal arrays you can say: int[] arr1; ... ; arr1[0] += 2; but when you define your own array, like: Array!(int) arr2; ... ; then how do you implement 'arr2[0] += 2;' ? You can't, because there are only opIndex and opIndexAssign operators. There's no opIndexAddAssign and opIndex*Assign for that purpose :0 This gets worse, e.g. if you have a struct Foo { int bar; } Foo[] arr3; ... ; arr3[0].bar = 1; but when you define Array!(Foo) arr4; ... ; then to accomplish the 'arr4[0].bar = 1;' functionality, you have to make opIndex return a pointer. Yet then you can't say 'Foo x = arr4[0];' because there's a type mismatch. You'd have to dereference the pointer.David Medlock wrote:Ok, but what exactly does that buy you? If you want reference semantics use a class. If you want opPostInc use a struct. Even without you could just as easily say: int bar(int r) { return this.someStruct.bar = r; } if you have several 'bars' perhaps its time to refactor? Its like saying : I cant ride my car on bike trails, even though I can ride my bike on roads. Just use the bike.The attribute functions in classes serve this purpose do they not? C++: int& my_attr(); D: void my_attr( int ); use, in both cases: foo.my_attr = 100;Unless you want to be able to say 'foo.my_attr++;' or 'foo.someStruct.bar = 1;' The latter can be accomplished by making the function return a pointer to the struct, but then you cant say 'Foo x = my_attr();'
Jun 24 2006
Tom S wrote:David Medlock wrote:This should have read 'value semantics' not opPostInc. <snip>Tom S wrote:David Medlock wrote:Ok, but what exactly does that buy you? If you want reference semantics use a class. If you want opPostInc use a struct.The attribute functions in classes serve this purpose do they not? C++: int& my_attr(); D: void my_attr( int ); use, in both cases: foo.my_attr = 100;Unless you want to be able to say 'foo.my_attr++;' or 'foo.someStruct.bar = 1;' The latter can be accomplished by making the function return a pointer to the struct, but then you cant say 'Foo x = my_attr();'Excuse me, but I have no idea what you're talking about.I am talking about 'possible' versus 'thats how we do it in C++'. If you want to use C++ idioms, then yes its very hard to use D that way. The point was that its (somewhat of) a shortcoming. Certain C++ idioms require new ones in D. Thats not 'worse', its just different.Reference return types are useful in many cases. E.g. if you want to implement your own Array class. With normal arrays you can say: int[] arr1; ... ; arr1[0] += 2; but when you define your own array, like: Array!(int) arr2; ... ; then how do you implement 'arr2[0] += 2;' ? You can't, because there are only opIndex and opIndexAssign operators. There's no opIndexAddAssign and opIndex*Assign for that purpose :0You can't opAddAssign in that case yes, but you can add 2 to the first item with: arr2[0] = arr2[0] + 2; Is this really a lot worse? Its maybe 2-3 cycles at the most?This gets worse, e.g. if you have a struct Foo { int bar; } Foo[] arr3; ... ; arr3[0].bar = 1; but when you define Array!(Foo) arr4; ... ; then to accomplish the 'arr4[0].bar = 1;' functionality, you have to make opIndex return a pointer. Yet then you can't say 'Foo x = arr4[0];' because there's a type mismatch. You'd have to dereference the pointer.Not to pick but you could say: auto x = arr4[0]; x.bar = 100; or you could simply use a different method: class Array(T) { T[] items; ... T* ptr(int n=0) { return items.ptr + n ; } } arr.ptr[5].bar = 1; Since the built int arrays use a .ptr property, I usually define this method anyways. Once you use it for a while its second nature. If you need to change a lot of entries, use foreach which returns an inout reference. If you want to get crazy: void opIndexAssign( void delegate(T* item) fn, int n ) { fn( items.ptr + n ); } then: arr[0] = (Foo* f){f.bar += 3;}; hehe :P Seriously though, if the item is small enough to not warrant a class, then just extend the container class and define a custom method to update the item. -DavidM
Jun 24 2006
Sounds like you have quite a lot of experience working around the lack of C++ reference type. I just wonder if it will ever be included in D?Not to pick but you could say: auto x = arr4[0]; x.bar = 100; or you could simply use a different method: class Array(T) { T[] items; ... T* ptr(int n=0) { return items.ptr + n ; } } arr.ptr[5].bar = 1; Since the built int arrays use a .ptr property, I usually define this method anyways. Once you use it for a while its second nature. If you need to change a lot of entries, use foreach which returns an inout reference. If you want to get crazy: void opIndexAssign( void delegate(T* item) fn, int n ) { fn( items.ptr + n ); } then: arr[0] = (Foo* f){f.bar += 3;}; hehe :P Seriously though, if the item is small enough to not warrant a class, then just extend the container class and define a custom method to update the item. -DavidM
Jun 25 2006
David Medlock wrote:Tom S wrote:Heh... now it makes more sense ;)David Medlock wrote:This should have read 'value semantics' not opPostInc.If you want reference semantics use a class. If you want opPostInc use a struct.<snip>If it requires you to create workarounds all over the place, makes code less transparent than the C++ way and is less efficient, then I'm tempted to say that it's worse.Excuse me, but I have no idea what you're talking about.I am talking about 'possible' versus 'thats how we do it in C++'. If you want to use C++ idioms, then yes its very hard to use D that way. The point was that its (somewhat of) a shortcoming. Certain C++ idioms require new ones in D. Thats not 'worse', its just different.You can't opAddAssign in that case yes, but you can add 2 to the first item with: arr2[0] = arr2[0] + 2; Is this really a lot worse? Its maybe 2-3 cycles at the most?It's not about speed in *this* case. The compiler might optimize it away. The point is that if you use normal arrays in some place, but then decide to switch to your custom classes, you may be in trouble. Say you have some myArray which stores structs and somewhere you write: myArray[i].prop = 5; The code previously used a builtin array and was just fine. After changing to the custom array template, it doesnt work as expected. It changes the 'prop' in a temporary object. Ok, you then realize what is up and modify the opIndex to return a pointer. But then all of a sudden code like this: MyStruct foo = myArray[i]; stops working because myArray[i] returns a pointer and there's a mismatch. Ok, you fix all of these lines to MyStruct foo = *myArray[i]; You live happily, until one day you find out that your custom logging functions are behaving in a weird way. Instead of printing the contents of your MyStruct's, they output some ridiculous hexadecimal numbers. You jump into the code just to find out (after some struggles), that the wrong logging function was called, because you have void log(MyStruct); and void log(void*); and somewhere in your code you had log(myArray[i]). No error was reported because the compiler silently assumed that you wanted to print a pointer.You could with classes, but in this case typeof(arr4[0]) is a struct.This gets worse, e.g. if you have a struct Foo { int bar; } Foo[] arr3; ... ; arr3[0].bar = 1; but when you define Array!(Foo) arr4; ... ; then to accomplish the 'arr4[0].bar = 1;' functionality, you have to make opIndex return a pointer. Yet then you can't say 'Foo x = arr4[0];' because there's a type mismatch. You'd have to dereference the pointer.Not to pick but you could say: auto x = arr4[0]; x.bar = 100;or you could simply use a different method: class Array(T) { T[] items; ... T* ptr(int n=0) { return items.ptr + n ; } } arr.ptr[5].bar = 1; Since the built int arrays use a .ptr property, I usually define this method anyways. Once you use it for a while its second nature.Yet I don't feel like losing array bounds checking... It would have to be 'arr.ptr(5).bar = 1;' plus a modification to the 'ptr' function. But then it doesn't read as 'arr[5].bar = 1;'. At the moment the language doesn't provide enough abstraction in this area. At least IMO.If you need to change a lot of entries, use foreach which returns an inout reference.Sure.If you want to get crazy: void opIndexAssign( void delegate(T* item) fn, int n ) { fn( items.ptr + n ); } then: arr[0] = (Foo* f){f.bar += 3;}; hehe :PUmmm... no, thanks :PSeriously though, if the item is small enough to not warrant a class, then just extend the container class and define a custom method to update the item.That's a corner case which misses the point a bit... -- Tomasz Stachowiak /+ a.k.a. h3r3tic +/
Jun 25 2006
Tom S wrote:David Medlock wrote: If it requires you to create workarounds all over the place, makes code less transparent than the C++ way and is less efficient, then I'm tempted to say that it's worse.Agreed, changing to a custom class would bite you there.You can't opAddAssign in that case yes, but you can add 2 to the first item with: arr2[0] = arr2[0] + 2; Is this really a lot worse? Its maybe 2-3 cycles at the most?It's not about speed in *this* case. The compiler might optimize it away. The point is that if you use normal arrays in some place, but then decide to switch to your custom classes, you may be in trouble. Say you have some myArray which stores structs and somewhere you write: myArray[i].prop = 5;You live happily, until one day you find out that your custom logging functions are behaving in a weird way. Instead of printing the contents of your MyStruct's, they output some ridiculous hexadecimal numbers. You jump into the code just to find out (after some struggles), that the wrong logging function was called, because you have void log(MyStruct); and void log(void*); and somewhere in your code you had log(myArray[i]). No error was reported because the compiler silently assumed that you wanted to print a pointer.I meant in the case you returned a pointer, but yes that wouldn't follow the 'native' array semantics so it wouldn't be good to return a pointer. Maybe with the static if you could make your container behave different with non-object types?You could with classes, but in this case typeof(arr4[0]) is a struct.This gets worse, e.g. if you have a struct Foo { int bar; } Foo[] arr3; ... ; arr3[0].bar = 1; but when you define Array!(Foo) arr4; ... ; then to accomplish the 'arr4[0].bar = 1;' functionality, you have to make opIndex return a pointer. Yet then you can't say 'Foo x = arr4[0];' because there's a type mismatch. You'd have to dereference the pointer.Not to pick but you could say: auto x = arr4[0]; x.bar = 100;Agreed. There is a lack there. It just hasn't bit me a lot thus far (and I have written a fair amount of code in D). I don't see a clear solution short of reference/inout returns, but I wonder what that would do to the rest of the language semantics. I can't recall if Walter said why he didn't include them.or you could simply use a different method: class Array(T) { T[] items; ... T* ptr(int n=0) { return items.ptr + n ; } } arr.ptr[5].bar = 1; Since the built int arrays use a .ptr property, I usually define this method anyways. Once you use it for a while its second nature.Yet I don't feel like losing array bounds checking... It would have to be 'arr.ptr(5).bar = 1;' plus a modification to the 'ptr' function. But then it doesn't read as 'arr[5].bar = 1;'. At the moment the language doesn't provide enough abstraction in this area. At least IMO.Tongue in cheek example of course, but I'm so far liking the delegate syntax.If you want to get crazy: void opIndexAssign( void delegate(T* item) fn, int n ) { fn( items.ptr + n ); } then: arr[0] = (Foo* f){f.bar += 3;}; hehe :PUmmm... no, thanks :PI meant that usually your algorithm entails a bit more than just updating a single member of a struct. In that case hiding the use of a pointer would be IMO ok. We aren't really disagreeing on any implementation details, only on their impact on daily use. Cheers. -DavidMSeriously though, if the item is small enough to not warrant a class, then just extend the container class and define a custom method to update the item.That's a corner case which misses the point a bit...
Jun 25 2006
David Medlock wrote:Tom S wrote:Ah.. sorry then ;)You could with classes, but in this case typeof(arr4[0]) is a struct.I meant in the case you returned a pointer,but yes that wouldn't follow the 'native' array semantics so it wouldn't be good to return a pointer. Maybe with the static if you could make your container behave different with non-object types?That's the approach I've proposed some time ago to someone implementing a custom container class. Not very consistent, but if there's nothing better at the moment...It just hasn't bit me a lot thus far (and I have written a fair amount of code in D) I don't see a clear solution short of reference/inout returns, but I wonder what that would do to the rest of the language semantics.I've had a few occasions to miss this feature from C++ ... I'm not sure if reference return types would be very hard to implement in the compiler, but a good syntax would have to be developed and probably the 'inout' keyword wouldn't cut it, e.g. void foo (inout int function()); Does foo take an inout function pointer or does it take a function that returns an int by reference ? Putting 'inout' after the basic type could work, e.g. void foo(int inout function()); but then it's inconsistent with void bar(inout int x); If it was supposed to be consistent in this case, that 'bar' decl would have to become 'void bar(int inout x)'... but it doesn't read fine. But then renaming inout to 'ref' would make it sound ok again, e.g. void bar(int ref x) void foo(int ref function()); but then I wouldn't see a point in not using the '&' token instead. What would it do to other semantics ? Not much, I guess, but people would want references everywhere and not only for return and parameter types :PIt's not *very* bad, but I've got no idea what performance consequences it yields.Ummm... no, thanks :PTongue in cheek example of course, but I'm so far liking the delegate syntax.We aren't really disagreeing on any implementation details, only on their impact on daily use.True. I don't want it to done like in C++, but at least *something* ought to be done. E.g. to make various custom containers seem more like built-ins. -- Tomasz Stachowiak /+ a.k.a. h3r3tic +/
Jun 25 2006
Hasan Aljudy wrote:Yossarian wrote:There's one more irritating limitation, and that is the ability to pass a temporary byref: private import std.stdio; void main() { // error: (opCall)(100) is not an lvalue writefln(foo(MyStruct(100),100)); } int foo(inout MyStruct s, int i) { return s.i * i; } struct MyStruct { private int i = 0; static MyStruct opCall(int i) { MyStruct s; s.i = i; return s; } } I think the 'byref' modifier could be added to allow this. In the compiler it could be implemented the same as 'inout' except that the lvalue limitation would not be enforced. In C++ you'd have to do 'const <type> &', but I'm not suggesting we get into the const debate again just for this purpose; just add the byref for cases like that, for cases where the programmer just wants to pass byref w/o an intention to modify the parameter, and also the 'byref' modifier could then be used for function return types as well (makes more sense to me than using 'inout' for return types). - DaveTesuji napsal(a):The only thing missing is a reference return type.Will C++ reference type ever be included in D? In one form or the other?what do you mean? every complex object is internally passed by reference, and if you want the same behaviour as in c++ references, try inout keyword in declaration: D: void func(inout int a); is equal to C++: void func(int &a);
Jun 24 2006
An L-value return is needed to make any custom type truly transparent with a builtin type. This holds for custom containers as well as custom number types. Consider: int a; (+a) = 3 Now change the type of a into MyVector or MyMatrix, and it wouldn't work. Operator + returns an L-value. As of current D, you couldn't write an operator + that returns an L-value. The charm of C/C++ is that there is no magic exclusive to the compiler. In Pascal for instance, the WriteLn function can take a variable parameter list of serveral types (string, int) and print it out. There is no way to write a comparable replacement function for WriteLn standard Pascal itself, it's a compiler magic. Java has a GC, but you cannot write a GC in Java; it must be done for you by superior powers. It would be a pity if D retains compiler magic in the form of privileged builtin types, because there is no way to return an L-value of a value type for custom code. In article <e7ju5f$18lf$1 digitaldaemon.com>, Dave says...Hasan Aljudy wrote:Yossarian wrote:There's one more irritating limitation, and that is the ability to pass a temporary byref: private import std.stdio; void main() { // error: (opCall)(100) is not an lvalue writefln(foo(MyStruct(100),100)); } int foo(inout MyStruct s, int i) { return s.i * i; } struct MyStruct { private int i = 0; static MyStruct opCall(int i) { MyStruct s; s.i = i; return s; } } I think the 'byref' modifier could be added to allow this. In the compiler it could be implemented the same as 'inout' except that the lvalue limitation would not be enforced. In C++ you'd have to do 'const <type> &', but I'm not suggesting we get into the const debate again just for this purpose; just add the byref for cases like that, for cases where the programmer just wants to pass byref w/o an intention to modify the parameter, and also the 'byref' modifier could then be used for function return types as well (makes more sense to me than using 'inout' for return types). - DaveTesuji napsal(a):The only thing missing is a reference return type.Will C++ reference type ever be included in D? In one form or the other?what do you mean? every complex object is internally passed by reference, and if you want the same behaviour as in c++ references, try inout keyword in declaration: D: void func(inout int a); is equal to C++: void func(int &a);
Jun 26 2006
How about some sort of cast to l-value syntax that would be used from the calling code. It would generate a copy and pass a reference to it. For that matter, any reason not to do that by default? Dave wrote:[...]There's one more irritating limitation, and that is the ability to pass a temporary byref: private import std.stdio; void main() { // error: (opCall)(100) is not an lvalue writefln(foo(MyStruct(100),100)); } int foo(inout MyStruct s, int i) { return s.i * i; } struct MyStruct { private int i = 0; static MyStruct opCall(int i) { MyStruct s; s.i = i; return s; } }
Jun 26 2006
BCS wrote:How about some sort of cast to l-value syntax that would be used from the calling code. It would generate a copy and pass a reference to it. For that matter, any reason not to do that by default?I don't think there is -- that's kinda where I was headed with this. I don't think the 'lvalue' limitation needs to be there because either way a temporary would already be created by the opCall (whether it is passed 'in', 'out' or 'inout'). That is, the temporary is already created anyhow - why not just remove the restriction. Besides the coding convenience, the reason I suggested the new 'byref' modifier is because the current 'lvalue' limitation kind-of makes sense in the context of 'out' or 'inout'. But there are lots of times when you may just want to pass something 'byref' not intending it to be modified (but that side effect _will_ still be there - I'm not suggesting some sort of C++-like const &). - DaveDave wrote:[...]There's one more irritating limitation, and that is the ability to pass a temporary byref: private import std.stdio; void main() { // error: (opCall)(100) is not an lvalue writefln(foo(MyStruct(100),100)); } int foo(inout MyStruct s, int i) { return s.i * i; } struct MyStruct { private int i = 0; static MyStruct opCall(int i) { MyStruct s; s.i = i; return s; } }
Jun 26 2006