digitalmars.D - Calling members of null pointers-to-struct
- Tor Myklebust (43/43) May 06 2007 Why is this illegal in D? As I understand it (from discussion with
- James Dennett (10/17) May 06 2007 It's technically invalid in C++ also unless the function
- Tor Myklebust (8/28) May 06 2007 OK, what you say about the C++ standard is true; 9.3.2.1 says the this
- Daniel Keep (24/30) May 06 2007 I suppose one could argue that if it is allowable to call the method
- Tor Myklebust (43/60) May 07 2007 It doesn't make sense, though, which was precisely the point of the
- BCS (7/18) May 06 2007 I was found this trick because I had a function that needed to be called...
Why is this illegal in D? As I understand it (from discussion with GregorR on IRC), methods of a D struct are not virtual --- the following program cannot use vtables as an excuse for its AssertError. struct foo { int doit(int x) { return 42; } } void main() { foo *bar; bar.doit(9); } Neither does the following slightly less trivial code (note that null pointers passed as the 'this' argument aren't dereferenced by struct list's methods): struct list { list *next; int data; static list *make(list *foo, int bar) { list *x = new list(); x.next = foo; x.data = bar; return x; } list *put(int k) { return make(this, k); } bool has(int k) { if (this == null) return false; return data == k || next.has(k); } } void main() { list *foo; foo = foo.put(42); foo = foo.put(17); foo = foo.put(31345); assert(foo.has(42)); assert(foo.has(17)); assert(foo.has(31345)); assert(!foo.has(24)); } (Except for one minor detail about foo not being initialised to null by default, this is perfectly good C++ code; calling a nonvirtual method of a null pointer-to-struct simply results in the 'this' parameter coming out to be null. So, like, why doesn't D do the same?) Tor Myklebust
May 06 2007
Tor Myklebust wrote:Why is this illegal in D? As I understand it (from discussion with GregorR on IRC), methods of a D struct are not virtual --- the following program cannot use vtables as an excuse for its AssertError.[snip](Except for one minor detail about foo not being initialised to null by default, this is perfectly good C++ code; calling a nonvirtual method of a null pointer-to-struct simply results in the 'this' parameter coming out to be null. So, like, why doesn't D do the same?)It's technically invalid in C++ also unless the function is a static member function. The fact that most compilers allow it to run means nothing; in a portably valid C++ program, the value of the "this" pointer is never null. If D requires this to be diagnosed in at least some modes, that would be a minor improvement on C++ (which leaves this as undefined, largely for performance reasons). -- James
May 06 2007
James Dennett <jdennett acm.org> wrote:Tor Myklebust wrote:OK, what you say about the C++ standard is true; 9.3.2.1 says the this pointer is a pointer to the object on which the function is called, and there can't very well be such a thing if this is null. (I still see no earthly reason why it *should* be invalid for nonvirtual functions --- by extension, I see no earthly reason why it should be invalid for methods in D structs.) Tor MyklebustWhy is this illegal in D? As I understand it (from discussion with GregorR on IRC), methods of a D struct are not virtual --- the following program cannot use vtables as an excuse for its AssertError.[snip](Except for one minor detail about foo not being initialised to null by default, this is perfectly good C++ code; calling a nonvirtual method of a null pointer-to-struct simply results in the 'this' parameter coming out to be null. So, like, why doesn't D do the same?)It's technically invalid in C++ also unless the function is a static member function. The fact that most compilers allow it to run means nothing; in a portably valid C++ program, the value of the "this" pointer is never null. If D requires this to be diagnosed in at least some modes, that would be a minor improvement on C++ (which leaves this as undefined, largely for performance reasons).
May 06 2007
Tor Myklebust wrote:[...] (I still see no earthly reason why it *should* be invalid for nonvirtual functions --- by extension, I see no earthly reason why it should be invalid for methods in D structs.) Tor MyklebustI suppose one could argue that if it is allowable to call the method with a non-existent instance for context, then there's no reason for making it a member function in the first place. Conversely, the vast majority of member functions are member functions specifically *because* they require the context to be there, where the assertion makes sense to have. Of course, since all D ever gives is "ZOMG! ASSERT ERRORZ!" and neither tells you *where* it happened[1], or on what, it's not very useful for more than telling you you screwed up somewhere. Thank $DEITY for ddbg, though. -- Daniel [1] Ok, it tells you the line of the function being called, but all things considered, that isn't very useful. Oh for the day we get back-traces in the standard library :) -- int getRandomNumber() { return 4; // chosen by fair dice roll. // guaranteed to be random. } http://xkcd.com/ v2sw5+8Yhw5ln4+5pr6OFPma8u6+7Lw4Tm6+7l6+7D i28a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP http://hackerkey.com/
May 06 2007
Daniel Keep <daniel.keep.lists gmail.com> wrote:Tor Myklebust wrote:It doesn't make sense, though, which was precisely the point of the original post. I even gave an example demonstrating why it doesn't make sense; it is reproduced below: struct list { list *next; int data; static list *make(list *foo, int bar) { list *x = new list(); x.next = foo; x.data = bar; return x; } list *put(int k) { return make(this, k); } bool has(int k) { if (this == null) return false; return data == k || next.has(k); } } void main() { list *foo; foo = foo.put(42); foo = foo.put(17); foo = foo.put(31345); assert(foo.has(42)); assert(foo.has(17)); assert(foo.has(31345)); assert(!foo.has(24)); } This is a toy implementation of a linked list representing a set of ints. The empty list is represented by a null pointer. A nonempty list is represented by a null-terminated, er, list of struct lists. It makes sense for put() to be a member function because put() needs to know what 'this' is. It doesn't need to know about any of the members of 'this', though, so it doesn't need to dereference 'this'. It also makes sense for 'has' to be a member function, since it needs to know about 'data' and 'has'. (Neither one is needed if 'this' is null, however, since null represents the empty list and the empty list contains nothing.) Thus, in neither method does it make sense for a not-null assertion to be made but in both methods the context provided by 'this' is required. Tor Myklebust[...] (I still see no earthly reason why it *should* be invalid for nonvirtual functions --- by extension, I see no earthly reason why it should be invalid for methods in D structs.) Tor MyklebustI suppose one could argue that if it is allowable to call the method with a non-existent instance for context, then there's no reason for making it a member function in the first place. Conversely, the vast majority of member functions are member functions specifically *because* they require the context to be there, where the assertion makes sense to have.
May 07 2007
Reply to Tor,Why is this illegal in D? As I understand it (from discussion with GregorR on IRC), methods of a D struct are not virtual --- the following program cannot use vtables as an excuse for its AssertError. struct foo { int doit(int x) { return 42; } } void main() { foo *bar; bar.doit(9); }try thisfoo *bar= cast(foo*)(null+1); bar.doit(9);I was found this trick because I had a function that needed to be called as a delegate and I didn't want the overhead of doing the nested call trick. p.s. Wow, 2 (strange things I was working on that other people bring up) in one day! The other is label variables.
May 06 2007