June 25, 2006 Re: C++ reference type | ||||
---|---|---|---|---|
| ||||
Posted in reply to David Medlock | 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
>
|
June 25, 2006 Re: C++ reference type | ||||
---|---|---|---|---|
| ||||
Posted in reply to David Medlock | David Medlock wrote: > Tom S wrote: >> David Medlock wrote: >>> If you want reference semantics use a class. >>> If you want opPostInc use a struct. >>> > > This should have read 'value semantics' not opPostInc. Heh... now it makes more sense ;) > <snip> >> 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. 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. > 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. >> 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; You could with classes, but in this case typeof(arr4[0]) is a struct. > 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 :P Ummm... no, thanks :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. That's a corner case which misses the point a bit... -- Tomasz Stachowiak /+ a.k.a. h3r3tic +/ |
June 25, 2006 Re: C++ reference type | ||||
---|---|---|---|---|
| ||||
Posted in reply to Tom S | 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. > > > >> 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; > > Agreed, changing to a custom class would bite you there. > 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. > > > >>> 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; > > > 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? > > >> 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. > > 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. > >> 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 > > > Ummm... no, thanks :P > Tongue in cheek example of course, but I'm so far liking the delegate syntax. > > >> 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. > > > That's a corner case which misses the point a bit... > I 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. -DavidM |
June 25, 2006 Re: C++ reference type | ||||
---|---|---|---|---|
| ||||
Posted in reply to David Medlock | David Medlock wrote: > Tom S wrote: >> You could with classes, but in this case typeof(arr4[0]) is a struct. >> > > I meant in the case you returned a pointer, Ah.. sorry then ;) > 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 :P >> Ummm... no, thanks :P >> > > Tongue in cheek example of course, but I'm so far liking the delegate syntax. It's not *very* bad, but I've got no idea what performance consequences it yields. > 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 +/ |
June 26, 2006 Re: C++ reference type | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dave | 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: >>> 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); >> >> The only thing missing is a reference return type. > >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). > >- Dave |
June 26, 2006 Re: C++ reference type | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dave | 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; > } > } > |
June 26, 2006 Re: C++ reference type | ||||
---|---|---|---|---|
| ||||
Posted in reply to BCS | 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 &). - Dave > 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; >> } >> } >> |
Copyright © 1999-2021 by the D Language Foundation