www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - What's wrong with this code?

reply Chris <cdallacosta gmail.com> writes:
This crashes when triggered:

		voxel_vec [string] move_buttons = [
			"button_xp" : voxel_vec ([ 1, 0, 0 ]),
			"button_xm" : voxel_vec ([ -1, 0, 0 ]),
			"button_yp" : voxel_vec ([ 0, 1, 0 ]),
			"button_ym" : voxel_vec ([ 0, -1, 0 ]),
			"button_zp" : voxel_vec ([ 0, 0, 1 ]),
			"button_zm" : voxel_vec ([ 0, 0, -1 ]) ];

		foreach (bname, vec; move_buttons) {
			Button bmove = cast (Button) g.getObject (bname);
			bmove.addOnClicked (delegate void (Button aux) {
				if (!glw.sel_active) return;
				
				glw.vf.move (3, vec, glw.sel_limits);
				glw.set_mesh (glw.vf.polygonize);
			} );
		}

I get:

First-chance exception at 0x754fc42d in 3dprinterapp.exe: 
0xE0440001: 0xe0440001.
Unhandled exception at 0x754fc42d in 3dprinterapp.exe: 
0xE0440001: 0xe0440001.

Inspection with the debugger reveals the var glw is "out of 
scope" at the location of the if (!glw... and single stepping 
past that line triggers the exception. The variable glw is in 
scope however, this code just above it works:

		b.addOnValueChanged (delegate void (SpinButton aux) {
			for (int i = 0; i < 6; i++)
				sellimits [i] = selwidgets [i].getValueAsInt ();

			glw.set_sel_box (sellimits);
			glw.sel_active = 1;
			glw.actual_queue_draw ();
		});

(works)

Could this be a compiler bug? I have no idea what could be wrong. 
Bringing it out of the loop produces the same error:

		Button xmove = cast (Button) g.getObject ("button_xp");
		xmove.addOnClicked (delegate void (Button aux) {
			if (!glw.sel_active) return;
			glw.vf.move (3, voxel_vec ([ 1, 0, 0 ]), glw.sel_limits);
			glw.set_mesh (glw.vf.polygonize);
		});

(same error)

This is with DMD 2.067.1 under Windows, I'm stuck with it because 
if I upgrade to 2.068 then GtkD doesn't work anymore. Also using 
GtkD 3.0.0, version 3.1.4 doesn't work with either version of DMD.

Thanks in advance for any help.
Sep 18 2015
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Saturday, 19 September 2015 at 02:30:39 UTC, Chris wrote:
 			bmove.addOnClicked (delegate void (Button aux) {
What's the context of this call? If it is inside a struct and you are accessing local variables you can get in trouble because the context pointer may not be correct. You are using `vec` there, if `glw` is a struct/class member too, then the compiler can sometimes be a bit stupid about bringing in the correct `this` reference to the delegate. Basically the problem is that a delegate only has one pointer inside to the data it needs. Usually, it points to the this for an object OR to a copy of the local variables. But since you want to access members of both, it is too stupid to handle that case and tries to access like this.glw through the local variable copy... where this and thus glw don't actually exist, which is why the debugger is saying what it is saying. I've seen this before (and even briefly mentioned it in my dconf talk this year) and found three possible workaround solutions: 1) make some parts struct static. Probably not applicable to you... 2) use a temporary delegate to which you pass arguments explicitly, to make a copy of them in one spot 3) or, in a similar vein, make a local copy of the this variable or references you need and access parts through it. something like auto glw = this.glw; or something, maybe you will want to change names so it isn't as ambiguous. But give that a try and run it again, I betcha you'll find some success.
 		b.addOnValueChanged (delegate void (SpinButton aux) {
 			for (int i = 0; i < 6; i++)
 				sellimits [i] = selwidgets [i].getValueAsInt ();

 			glw.set_sel_box (sellimits);
 			glw.sel_active = 1;
 			glw.actual_queue_draw ();
 		});
See, here, you're just using glw, no local variables, to if it pulls the struct/class `this` as the delegate pointer, you'll be in business. That's why I suspect this is the issue.
 Could this be a compiler bug?
arguably, it should at least not compile and do the wrong thing.
Sep 18 2015
parent reply Chris <cdallacosta gmail.com> writes:
On Saturday, 19 September 2015 at 02:45:54 UTC, Adam D. Ruppe 
wrote:
 On Saturday, 19 September 2015 at 02:30:39 UTC, Chris wrote:
 			bmove.addOnClicked (delegate void (Button aux) {
What's the context of this call? If it is inside a struct and you are accessing local variables you can get in trouble because the context pointer may not be correct. You are using `vec` there, if `glw` is a struct/class member too, then the compiler can sometimes be a bit stupid about bringing in the correct `this` reference to the delegate.
Hi thanks for the response. The context is inside a try block in the program's main() function. Glw is indeed a class but it doesn't look like it quite configures the problem you describe.

 something like
 
 auto glw = this.glw;
There's no "this", it's the main() function. I stuck a "auto glw2 = glw" in there but it didn't help. I found more funny stuff. If I move the code around and change the order of that snippet to before the other one I posted earlier, the first line (if (!glw...) now works *despite* the debugger saying the variable glw is "not found". So now as long as the condition evaluates to true it will work as expected. However when the condition is made to eval to false then the *next* line of code will trigger the exception as I first encountered. So I'm going to go ahead and say I found a compiler bug.
Sep 19 2015
parent Chris <cdallacosta gmail.com> writes:
Update:

If I add *also* a auto vec2 = vec; now the code works. So it 
looks like this now:

		voxel_vec [string] move_buttons = [
			"button_xp" : voxel_vec ([ 1, 0, 0 ]),
			"button_xm" : voxel_vec ([ -1, 0, 0 ]),
			"button_yp" : voxel_vec ([ 0, 1, 0 ]),
			"button_ym" : voxel_vec ([ 0, -1, 0 ]),
			"button_zp" : voxel_vec ([ 0, 0, 1 ]),
			"button_zm" : voxel_vec ([ 0, 0, -1 ]) ];

		foreach (bname, vec; move_buttons) {
			Button bmove = cast (Button) g.getObject (bname);

			bmove.addOnClicked (delegate void (Button aux) {
				auto glw2 = glw;
				auto vec2 = vec;

				if (!glw2.sel_active) return;

				glw2.vf.move (3, vec2, glw2.sel_limits);
				glw2.set_mesh (glw2.vf.polygonize);
			} );
		}

and works as expected. I'm going to leave it like that in the 
belief it's a compiler bug, if someone wants to take a look at 
the full source I'm willing to disclose it.

Thanks again for the help and the idea of the work around.
Sep 19 2015