Access violation calling GuiControl::getExtent()
by Corey · in Torque Game Engine · 07/19/2007 (8:44 pm) · 11 replies
Here's a quick blurb on what I'm trying to do:
and before I explain the problem, I should probably point out the definition of the getExtent() method that I'm calling:
It compiles fine, but I get this run-time stop error at that "mCenter = getRoot()->getExtent();" line:
I did a little research and found a short, mostly useless explanation on this site. At first I assumed what I'm sure anybody would assume - I didn't something stupid with a pointer, specifically that getExtent reference. But quite frankly, I don't see it! In fact, I did some tinkering and discovered that THIS will work! (by "work" I mean not crash the engine)
But, even if I try directly after that replacing the "400" and "300" initializations with "mCenter = test," again it causes the same stop error. Evidently the access violation is with some communication between mCenter and getExtent, which blows my amateur mind.
I would be very appreciative of any enlightenment! Many thanks, as always.
// GuiReticleCtrl.h
// a tickable bitmap reticle with a "soft" floating zone
...
class GuiReticleCtrl : public GuiBitmapCtrl, public virtual ITickable
{
...
protected:
virtual void interpolateTick(F32 delta);
virtual void processTick();
virtual void advanceTime(F21 timeDelta);
Point2I mCenter; // center of the screen
U16 mRadius, mCushion; // radius and cushioning factor of the floating zone
public:
DECLARE_CONOBJECT(GuiReticleCtrl);
GuiReticleCtrl();
...
};// GuiReticleCtrl.cc
// a tickable bitmap reticle with a "soft" floating zone
...
GuiReticleCtrl::GuiReticleCtrl()
{
mCenter = getRoot()->getExtent() / 2; // this is where run-time problems come up
mRadius = 100; // this will later be exposed to the console as a $Pref
mCushion = 10; // this will later be exposed to the console as a $Pref
}
...and before I explain the problem, I should probably point out the definition of the getExtent() method that I'm calling:
// guiControl.h
...
Class GuiControl : public SimGroup
{
...
public:
...
const Point2I& getExtent() { return mBounds.extent; } ///< Returns the extents of the control
...
};It compiles fine, but I get this run-time stop error at that "mCenter = getRoot()->getExtent();" line:
Quote:Unhandled exception at 0x009a68e7 in torqueDemo_DEBUG.exe:
0xC0000005: Access violation reading location 0x00000080.
I did a little research and found a short, mostly useless explanation on this site. At first I assumed what I'm sure anybody would assume - I didn't something stupid with a pointer, specifically that getExtent reference. But quite frankly, I don't see it! In fact, I did some tinkering and discovered that THIS will work! (by "work" I mean not crash the engine)
GuiReticleCtrl::GuiReticleCtrl()
{
//mCenter = getRoot()->getExtent() / 2; // this is where run-time problems come up
Point2I test = getRoot()->getExtent();
mCenter.x = 400;
mCenter.y = 300;
mRadius = 100; // this will later be exposed to the console as a $Pref
mCushion = 10; // this will later be exposed to the console as a $Pref
}
...But, even if I try directly after that replacing the "400" and "300" initializations with "mCenter = test," again it causes the same stop error. Evidently the access violation is with some communication between mCenter and getExtent, which blows my amateur mind.
I would be very appreciative of any enlightenment! Many thanks, as always.
About the author
Recent Threads
#2
07/21/2007 (10:56 am)
Have you stepped through with the debugger and verified that getRoot() is returning a valid pointer to a GuiCanvas object?
#3
However, in an effort to respond to your suggestion, I put a breakpoint directly after "GuiCanvas* root = getRoot();", then noticed this window:
www.bellsouthpwp.net/C/o/Correction/debugger.png
I assume that's supposed to be the address stored in the pointer, which in this case ought to be the address of the root canvas. Obviously 0x00000000 isn't right. How do I go about locating and amending this kind of problem?
07/21/2007 (11:54 am)
No, I hadn't. Sorry, I'm not really as experienced as I try to pretend to sound. :pHowever, in an effort to respond to your suggestion, I put a breakpoint directly after "GuiCanvas* root = getRoot();", then noticed this window:
www.bellsouthpwp.net/C/o/Correction/debugger.png
I assume that's supposed to be the address stored in the pointer, which in this case ought to be the address of the root canvas. Obviously 0x00000000 isn't right. How do I go about locating and amending this kind of problem?
#4
getRoot() is supposed to return a GuiCanvas pointer. So at the point you are trying to get it in your GuiReticleCtrl constructor, it does not exist, which is why it returns NULL. To solve your problem, you should move the assignment of mCenter out of the constructor into another function which is called later - when you do have a valid GuiCanvas. [Oh, and you should always check that you haven't been returned a NULL pointer before trying to use it.]
07/21/2007 (12:27 pm)
I haven't worked in the GUI code, but...getRoot() is supposed to return a GuiCanvas pointer. So at the point you are trying to get it in your GuiReticleCtrl constructor, it does not exist, which is why it returns NULL. To solve your problem, you should move the assignment of mCenter out of the constructor into another function which is called later - when you do have a valid GuiCanvas. [Oh, and you should always check that you haven't been returned a NULL pointer before trying to use it.]
#5
07/21/2007 (12:41 pm)
I think Andy nailed it with the above post. Using external pointers (and even external methods) during a constructor call is not a good practice, especially in Torque (for many classes, Torque will instantiate a single instance of every class for future accounting reasons, and this happens before quite a bit of other code is executed).
#6
I wouldn't have expected that to be a problem because I thought surely the root canvas would be built before my control. I do understand why declaring it in the constructor was a bad idea - now that just sounds silly (of course there's no root! there's not even a GuiReticleCtrl yet because it's being constructed!), but how is it that even if I try to call the root from processTick() I can still get a NULL pointer when TGE is first loading up?
And I guess lastly, is that the acceptable solution? I greatly appreciate you guys sharing your experience and wisdom with me ;)
07/21/2007 (1:53 pm)
Wow, thanks! So what I ended up doing was in processTick() (which is where I needed the center of the screen,) I surrounded all my code with...if(GuiCanvas* root = getRoot())
{
Point2I center = root->getExtent() / 2;
...
(all my original processTick() code)
...
}
else
return;I wouldn't have expected that to be a problem because I thought surely the root canvas would be built before my control. I do understand why declaring it in the constructor was a bad idea - now that just sounds silly (of course there's no root! there's not even a GuiReticleCtrl yet because it's being constructed!), but how is it that even if I try to call the root from processTick() I can still get a NULL pointer when TGE is first loading up?
And I guess lastly, is that the acceptable solution? I greatly appreciate you guys sharing your experience and wisdom with me ;)
#7
It may wind up making sense to you to actually move this to interpolateTick(), but I would have to research that more carefully. The main reason for possibly moving it there is that processTick() is called at a fixed frequency of 32 milliseconds, while interpolateTick() is called every render frame.
It's more complicated than that (of course!) and will require some additional research on your part to figure out what works for you, but you're "safe" with the processTick() implementation.
To answer the first question you ask (but how is it that even if I try to call the root from processTick() I can still get a NULL pointer when TGE is first loading up?), the client side simulation can (and does) start before the canvas is fully set up in normal operation, and due to the asynchronous startup conditions of all the integrated systems, it's important to check for this type of situation by confirming a valid pointer.
07/21/2007 (2:00 pm)
I'm not 100% sure what your actual use case is, but given assumptions based on the naming of your objects, it sounds as if you want an onscreen reticule that shows where projectiles will/should go. If that's the case (and barring other logic flow issues that may arise), yes, processTick() is at least the first place to start.It may wind up making sense to you to actually move this to interpolateTick(), but I would have to research that more carefully. The main reason for possibly moving it there is that processTick() is called at a fixed frequency of 32 milliseconds, while interpolateTick() is called every render frame.
It's more complicated than that (of course!) and will require some additional research on your part to figure out what works for you, but you're "safe" with the processTick() implementation.
To answer the first question you ask (but how is it that even if I try to call the root from processTick() I can still get a NULL pointer when TGE is first loading up?), the client side simulation can (and does) start before the canvas is fully set up in normal operation, and due to the asynchronous startup conditions of all the integrated systems, it's important to check for this type of situation by confirming a valid pointer.
#8
www.youtube.com/watch?v=BUn0ctse88Y
Sorry about the video being trash. I blame YouTube.
As for processTick() vs. interpolateTick(), right now the way it works is that the mouse is bound to functions that move a target position point (mCurPos).
In processTick() it determines mCurPos's offset from the screen center. If this is greater than the radius of our floating zone, it adjusts the players pitch and yaw accordingly and then moves the target position towards the center of the screen based on some multiplier (the cushioning factor mCushion). Lastly it updates mDeltaPos with mCurPos - mBounds.point (which is where the bitmap is actually drawn) to take note of how far off the bitmap currently is from where we actually want it to be.
In interpolateTick() I simply slide the actual bitmap's position towards the target position for smoothing with the following statement:
So in response to your original statement, I think I do want all of this to happen in processTick(). However, I can't really say that with much confidence because while it does work (as you can see from the video), my interpolation doesn't work at all. I suppose this will just take a little more research and backtracking through my own logic on my part to see where I went wrong.
07/21/2007 (2:37 pm)
This is what I'm making:www.youtube.com/watch?v=BUn0ctse88Y
Sorry about the video being trash. I blame YouTube.
As for processTick() vs. interpolateTick(), right now the way it works is that the mouse is bound to functions that move a target position point (mCurPos).
In processTick() it determines mCurPos's offset from the screen center. If this is greater than the radius of our floating zone, it adjusts the players pitch and yaw accordingly and then moves the target position towards the center of the screen based on some multiplier (the cushioning factor mCushion). Lastly it updates mDeltaPos with mCurPos - mBounds.point (which is where the bitmap is actually drawn) to take note of how far off the bitmap currently is from where we actually want it to be.
In interpolateTick() I simply slide the actual bitmap's position towards the target position for smoothing with the following statement:
if(!mDeltaPos.isZero()) mBounds.point = mCurPos + mDeltaPos * dt
So in response to your original statement, I think I do want all of this to happen in processTick(). However, I can't really say that with much confidence because while it does work (as you can see from the video), my interpolation doesn't work at all. I suppose this will just take a little more research and backtracking through my own logic on my part to see where I went wrong.
#9
My contribution would be NEVER EVER set floating point numbers into Gui .position or .extent fields. BAD THINGS will happen.
I'm still traumatized ;)
So be careful if you are moving Gui elements around via script to make sure all positions and extents (and probably other fields) are integers. I have mentally blocked out the exact error on advice from my therapist ;) but it is something like .position = "10.0 20.0"; being parsed as if it were "10". Imagine if all your extents were similarly corrupted...
08/01/2007 (8:02 am)
I've been thinking about starting a "Torque: Don't Ever Do This..." thread. My contribution would be NEVER EVER set floating point numbers into Gui .position or .extent fields. BAD THINGS will happen.
I'm still traumatized ;)
So be careful if you are moving Gui elements around via script to make sure all positions and extents (and probably other fields) are integers. I have mentally blocked out the exact error on advice from my therapist ;) but it is something like .position = "10.0 20.0"; being parsed as if it were "10". Imagine if all your extents were similarly corrupted...
#10
It has to do with truncation - I think that's what you were getting at. One-dimensionally (for simplicity), lets suppose mCurPos is 5, mDeltaPos is -3, and dt is 0.834.
What I thought would happen was that mBounds.point would equate to the following:
5 - (3 * 0.834)
or 5 - 2.502
or simply 2.498 which is then truncated to 2 (which for my purposes is fine).
However, what I didn't realize is that the 0.834 is actually truncated before the mathematical operations, so basically it was always multiplying mDeltaPos by a truncated dt, which was always 0.
It was just a simple order of operations problem that I didn't realize. This isn't just a Torque issue, but rather has to do with C++ in general. It was solved by simply declaring a "Point2I offset" and defining "offset.x = mDeltaPos.x * dt" and "offset.x = mDeltaPos.x * dt" seperately before using it in "mBounds.point = mCurPos + offset."
My reticle now works entirely as intended and moves perfectly smoothly. It looks very nice. Thanks to everybody who helped with all my silly errors! ;)
08/01/2007 (8:14 am)
If you're talking about my interpolation (specifically the "* dt" part), I did solve the problem a few days ago. Thanks.It has to do with truncation - I think that's what you were getting at. One-dimensionally (for simplicity), lets suppose mCurPos is 5, mDeltaPos is -3, and dt is 0.834.
What I thought would happen was that mBounds.point would equate to the following:
5 - (3 * 0.834)
or 5 - 2.502
or simply 2.498 which is then truncated to 2 (which for my purposes is fine).
However, what I didn't realize is that the 0.834 is actually truncated before the mathematical operations, so basically it was always multiplying mDeltaPos by a truncated dt, which was always 0.
It was just a simple order of operations problem that I didn't realize. This isn't just a Torque issue, but rather has to do with C++ in general. It was solved by simply declaring a "Point2I offset" and defining "offset.x = mDeltaPos.x * dt" and "offset.x = mDeltaPos.x * dt" seperately before using it in "mBounds.point = mCurPos + offset."
My reticle now works entirely as intended and moves perfectly smoothly. It looks very nice. Thanks to everybody who helped with all my silly errors! ;)
#11
and got around some of these issues by tracking the controls position & extent in floating point inside the control, and only rounding it to integer at the last moment.
this was necessary to have a control animate with a non-integral velocity such as a third of a pixel per frame.
08/01/2007 (9:34 am)
I implemented what sounds like a similar animating controls type thingand got around some of these issues by tracking the controls position & extent in floating point inside the control, and only rounding it to integer at the last moment.
this was necessary to have a control animate with a non-integral velocity such as a third of a pixel per frame.
Torque Owner Corey
I've come to realize that, although the debugger notes the error there, the error really is when I try to do anything with mCenter later on (such as in GuiReticleCtrl::processTick() - "Point2I offset = mCurPos - mCenter;").
I realized this when I tried to replace that with just "Point2I offset = mCurPos - getRoot()->getExtent() / 2." Then I did some tinkering, and back to my original example in the constructor I tried just a simple...
This, although it obviously doesn't actually do anything, still crashed the engine with the same stop error.
Is this some property of references that I misunderstand? Why can't I read that value, do some math with it, and then plug the result into another variable?