www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Ref / NotNull struct

reply "Namespace" <rswhite4 googlemail.com> writes:
I like to present my version of an NotNull Reference struct.
Two Bugs with "alias this" caused me a lot of trouble and one of 
them (the recursive infinite loop) i cannot fix by myself. 
Because of that you have to access the object in the struct with 
"obj.access" instead of "obj".
Little Example:
[code]
class A {
     // these mixin template contains the functionality to convert 
"A" to "Ref!(A)"
     mixin TRef!(A);

     void say_something() const {
         writeln("Hello");
     }
}

void foo(Ref!(A) ra) {
     // don't work until yet
     // ra.say();
     // so you have to write
     ra.access.say();
}

In my opinion a little annoying but i hope this bug will be fixed 
soon.
I think the reason for this behaviour is simple: if you have two 
structs/classes wich convert each to another and mark these 
convert methods with "alias this" the compiler found no solution 
and turns into an ininite loop. That could explain the 
"Stackoverflow" after compiling.
Or isn't that a real bug?

Well, here is the code:

[code]
template CreateMsg(string msg, string file = __FILE__, size_t 
line = __LINE__) {
	enum CreateMsg = msg ~  "   " ~ file ~ " in Line " ~ 
to!(string)(line) ~ ".";
}

string CreateErrorMsg(string msg, string file = __FILE__, size_t 
line = __LINE__) {
	return msg ~  "   " ~ file ~ " in Line " ~ to!(string)(line) ~ 
".";
}

struct Ref(T : Object) {
private:
	 T _obj;

public:
	 disable
	this();

	 disable
	this(typeof(null));

	this(T obj) {
		assert(obj !is null, CreateMsg!("Object is null!"));

		this._obj = obj;
	}

	 disable
	typeof(this) opAssign()(typeof(null));

	typeof(this) opAssign(U : T)(Ref!(U) refer) {
		this._obj = refer.access;

		return this;
	}
	
	typeof(this) opAssign(U : T)(U refer) {
		assert(refer !is null, CreateMsg!("Object is null"));

		this._obj = refer;

		return this;
	}
	
	 property
	inout(T) access() inout {
		assert(this._obj !is null, CreateMsg!("Access: Object is 
null!"));

		return this._obj;
	}

	// DMD Bug: The compiler creates and infinite loop.
	// if uncomment, the compiler print "Stackoverflow" or 
"recursive expansion"
	//alias access this;
}

mixin template TRef(T : Object) {
	final Ref!(T) getRef(string file = __FILE__, size_t line = 
__LINE__) in {
		assert(this !is null, CreateErrorMsg("Object is null!", file, 
line));
	} body {
		return Ref!(T)(this);
	}
	
	final Ref!(const T) getRef(string file = __FILE__, size_t line = 
__LINE__) const in {
		assert(this !is null, CreateErrorMsg("Object is null!", file, 
line));
	} body {
		return Ref!(const T)(this);
	}
	
	// Hack for Bug 8001 
(http://forum.dlang.org/thread/bug-8001-3 http.d.puremagic.com%2Fissues%2F)
	U opCast(U : Object)() inout {
		return *(cast(U*) &this);
	}

	alias getRef this;
}
[/code]

I'm using this construct already in one of my university projects 
and until now i found no errors. But for completeness: here the 
code of my test module: http://codepad.org/XGp3APCt

I would be glad for criticism and suggestions and maybe a 
solution/workaround for the infinite loop bug, if it's the case, 
that it's really a bug.
May 06 2012
next sibling parent "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
 I would be glad for criticism and suggestions and maybe a  
 solution/workaround for the infinite loop bug, if it's the case, that  
 it's really a bug.

First thought (after just copying the code and compiling it, not reading the post :p), was 'where are the unittest blocks?' Then I saw you had a separate module. As for the infinite loop bug, consider this: class A { B b; alias b this; } class B { A a; alias a this; } void main( ) { A a = 3; } This fails with the exact same message. The compiler goes 'Eh, no int in A. But ooh, there's an alias this here!' 'Eh, no int in B. But ooh, there's an alias this here!' Repeat as necessary. Filed as 8053: http://d.puremagic.com/issues/show_bug.cgi?id=8053
May 06 2012
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
On Sunday, 6 May 2012 at 14:04:30 UTC, Simen Kjaeraas wrote:
 I would be glad for criticism and suggestions and maybe a 
 solution/workaround for the infinite loop bug, if it's the 
 case, that it's really a bug.

First thought (after just copying the code and compiling it, not reading the post :p), was 'where are the unittest blocks?' Then I saw you had a separate module. As for the infinite loop bug, consider this: class A { B b; alias b this; } class B { A a; alias a this; } void main( ) { A a = 3; } This fails with the exact same message. The compiler goes 'Eh, no int in A. But ooh, there's an alias this here!' 'Eh, no int in B. But ooh, there's an alias this here!' Repeat as necessary. Filed as 8053: http://d.puremagic.com/issues/show_bug.cgi?id=8053

Ah, I see. Thanks for this. I'm glad when dmd 2.060 comes and fix this. That is really a little bit annoying. I hate the ".acces" notation to access the object which is holding by Ref. But I have a workaround for the problem: use mixin Proxy!(this._obj); and it works. Now the only problem is, that Ref!(Foo) cannot be downcast to Foo, but you have now full object access wihtout the ".access" notation. Great step forward i think. :) And as far as i used it, i needn't the conversion from Ref!Foo to Foo. Here the changed Code with test calls below: http://codepad.org/lfxe5plJ I must excuse me at this point again for my bad english.
May 06 2012
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
I found an error: if you have something like this

[code]
void const_ref_foo(const Ref!(Foo) rf) {
	write("Const: ");
	rf.say();
}
[/code]

and call it with

[code]
Foo f1 = new Foo();
const_ref_foo(f1);
[/code]

You get this error:

ref_test.d(101): Error: template 
ref_test.Ref!(Foo).Ref.Proxy!(_obj).opDispatch!
("say").opDispatch does not match any function template 
declaration
D:\D\dmd2\windows\bin\..\..\src\phobos\std\typecons.d(2842): 
Error: template ref
_test.Ref!(Foo).Ref.Proxy!(_obj).opDispatch!("say").opDispatch(this 
X,Args...) c
annot deduce template function from argument types !()()

To avoid this you must change line 2842 in std.typecons from

[code]
auto ref opDispatch(this X, Args...)(Args args) { return 
mixin("a."~name~"(args)"); }
[/code]

in

[code]
auto ref opDispatch(this X, Args...)(Args args) inout { return 
mixin("a."~name~"(args)"); }
[/code]

It seems to be a bug. But must i create a new bug report for 
something like this?
May 06 2012
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
It seems that "inout" isn't enough to fix the problem.

So i extend the template to this:
http://codepad.org/SwBPoVM2

I wrote a copy of the function and now there is one with and one
without const. I thought inout would do the job alone but it
seems that it doesn't.

Now all of my code works as i expected. But because of my bad
english i would prefer that one of you report the bug and the fix.
May 06 2012
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
On Sunday, 6 May 2012 at 17:58:22 UTC, Namespace wrote:
 It seems that "inout" isn't enough to fix the problem.

 So i extend the template to this:
 http://codepad.org/SwBPoVM2

 I wrote a copy of the function and now there is one with and one
 without const. I thought inout would do the job alone but it
 seems that it doesn't.

 Now all of my code works as i expected. But because of my bad
 english i would prefer that one of you report the bug and the 
 fix.

I hoped for a little more interest/suggestions/criticism. Particularly for the missed implementation of "Proxy".
May 07 2012
prev sibling next sibling parent "Chris Cain" <clcain uncg.edu> writes:
On Monday, 7 May 2012 at 10:37:57 UTC, Namespace wrote:
 I hoped for a little more interest/suggestions/criticism.
 Particularly for the missed implementation of "Proxy".

I think it's great! I'm planning on trying it out on a few things and I'll let you know if I have any suggestions. I _almost_ suggested Proxy earlier, but I had never used it and I wasn't sure whether it was appropriate for the problem you were having. Overall, I think a lot of people will get a lot of use out of it. It's something I know people have been looking for, but I think you'll have to save the link and when someone asks/talks about NonNull references/pointers you can refer them here.
May 07 2012
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
 I think it's great! I'm planning on trying it out on a few 
 things and I'll let you know if I have any suggestions. I 
 _almost_ suggested Proxy earlier, but I had never used it and I 
 wasn't sure whether it was appropriate for the problem you were 
 having.

I would be glad if you will do this. :)
 Overall, I think a lot of people will get a lot of use out of 
 it. It's something I know people have been looking for, but I 
 think you'll have to save the link and when someone asks/talks 
 about NonNull references/pointers you can refer them here.

That's a good idea.
May 07 2012
prev sibling next sibling parent "Chris Cain" <clcain uncg.edu> writes:
OK, I tried a few things, but something that I think might need 
some attention: UFCS is very problematic with this.

https://gist.github.com/6198938b9c6d40a53ca1
May 07 2012
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
On Monday, 7 May 2012 at 18:46:55 UTC, Chris Cain wrote:
 OK, I tried a few things, but something that I think might need 
 some attention: UFCS is very problematic with this.

 https://gist.github.com/6198938b9c6d40a53ca1

I see. I think that is the same problem as "alias this". The compiler doesn't know what he has to do. I hope that functionality like this and the conversion from Foo to Ref!Foo and in reverse works in the next version. But I don't know how i can solve the problem. :/
May 07 2012
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
On Monday, 7 May 2012 at 18:46:55 UTC, Chris Cain wrote:
 OK, I tried a few things, but something that I think might need 
 some attention: UFCS is very problematic with this.

 https://gist.github.com/6198938b9c6d40a53ca1

But apart from that, nor anything else that does not work? :)
May 07 2012
prev sibling next sibling parent "Chris Cain" <clcain uncg.edu> writes:
On Monday, 7 May 2012 at 19:36:52 UTC, Namespace wrote:
 But apart from that, nor anything else that does not work? :)

I put together a few toy examples with it and it seems to work in general otherwise. It might be better if someone has a larger project that they wanted a non-null reference for to get a lot of code tested all at once. Overall, I'm pretty happy with it as long as I don't use UFCS with it. Again, thanks for your efforts developing it :)
May 07 2012
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
On Monday, 7 May 2012 at 21:10:17 UTC, Chris Cain wrote:
 On Monday, 7 May 2012 at 19:36:52 UTC, Namespace wrote:
 But apart from that, nor anything else that does not work? :)

I put together a few toy examples with it and it seems to work in general otherwise. It might be better if someone has a larger project that they wanted a non-null reference for to get a lot of code tested all at once. Overall, I'm pretty happy with it as long as I don't use UFCS with it. Again, thanks for your efforts developing it :)

Not at all, it was my pleasure. I'm sure you and perhaps others will find a few criticisms that can be fixed. But I have noticed again, that in almost all functions/methods of "Proxy" in std.typecons the "inout" is missing. Maybe i should open a Bug Report for this.
May 08 2012
prev sibling parent "Namespace" <rswhite4 googlemail.com> writes:
On Tuesday, 8 May 2012 at 16:27:27 UTC, Namespace wrote:
 On Monday, 7 May 2012 at 21:10:17 UTC, Chris Cain wrote:
 On Monday, 7 May 2012 at 19:36:52 UTC, Namespace wrote:
 But apart from that, nor anything else that does not work? :)

I put together a few toy examples with it and it seems to work in general otherwise. It might be better if someone has a larger project that they wanted a non-null reference for to get a lot of code tested all at once. Overall, I'm pretty happy with it as long as I don't use UFCS with it. Again, thanks for your efforts developing it :)

Not at all, it was my pleasure. I'm sure you and perhaps others will find a few criticisms that can be fixed. But I have noticed again, that in almost all functions/methods of "Proxy" in std.typecons the "inout" is missing. Maybe i should open a Bug Report for this.

Another Bug: If you try to cast a const Ref to his normal object like this void foo(const Ref!(Foo) rf) { Foo f = cast(Foo) rf; } you get this error "ref_test.d(121): Error: template ref_test.Ref!(Foo).Ref.Proxy!(_obj).opCast does not match any function template declaration D:\D\dmd2\windows\bin\..\..\src\phobos\std\typecons.d(2796): Error: template ref _test.Ref!(Foo).Ref.Proxy!(_obj).opCast(T,this X) cannot deduce template functio n from argument types !(Foo)()" After i changed line 2796 and made the opCast method "inout" it works as i want.
May 08 2012