Sign in to follow this  
rogierpennink

Runtime error at virtual function call

Recommended Posts

rogierpennink    400
I'm experiencing a rather strange run-time error (an access violation) in a certain function where I call another, virtual, function. First things first, so here's the generic error message that Visual Studio 2005 gives:
Unhandled exception at 0x0045697f in u3dtest.exe: 0xC0000005: Access violation reading location 0x3f800004.
The error happens at a line where I call the lock() function (which is virtual) on a ColorBuffer object. I call this function in the BoxGeometry::create() function, so let me paste the relevant parts of that function in here:
void BoxGeometry::create()
{
// Snippet Removed
	if ( this->cb )
	{
		/* Specify colors. */
		Color colors[] =
		{
			Color( color.r, color.g, color.b, color.a ),
			Color( color.r, color.g, color.b, color.a ),
			Color( color.r, color.g, color.b, color.a ),
			Color( color.r, color.g, color.b, color.a ),

			Color( color.r, color.g, color.b, color.a ),
			Color( color.r, color.g, color.b, color.a ),
			Color( color.r, color.g, color.b, color.a ),
			Color( color.r, color.g, color.b, color.a ),
		};

		/* Add colors to colorbuffer. The error occurs on cb->lock(). */
		cb->lock();			// Virtual function call
		cb->addColors( colors, 8 );	// Virtual function call
		cb->unlock();			// Virtual function call
	}
// Snippet removed
}
Now, this may indicate that the pointer to ColorBuffer (cb) is simply invalid, but due to the following experiment I doubt that. Let me elaborate. The BoxGeometry::create function is called from another function that is supposed to create geometry for a simple cube. To do this, it requests a new vertex, index and color buffer from my renderer and then passes those on to the BoxGeometry object. I have verified that I can call the lock() and unlock() functions before I pass the colorbuffer object into the BoxGeometry object. I'll just paste the renderer::createBox function here to show that:
BoxGeometry* U3DRenderer::createBox( float width, float height, float depth )
{
	/* First, request an index and a vertex buffer. */
	IndexBuffer* ib = this->createIndexBuffer( 36 );				// Box needs 36 indices
	VertexBuffer* vb = this->createVertexBuffer( 8, BT_STATIC );	// Box has 8 vertices
	ColorBuffer* cb = this->createColorBuffer( 8, BT_STATIC );		// Box has 8 vertices

	cb->lock();			// No problems at all here
	cb->unlock();
		
	/* Create the box geometry with requested values. */
	BoxGeometry* box = new BoxGeometry( vb, width, height, depth );

	/* Assign vertex/index buffers. */
	box->setColorBuffer( cb );
	box->setIndexBuffer( ib );

	box->dummy();

	/* Since BoxGeometry now has space for the vertices and indices, we can create the geometry. */
	box->create();

	return box;
}
As you can see, I call cb->lock() and cb->unlock() for debugging purposes and nothing happens; virtual functions are no problem. Now the weird part. Because cb seems to be a perfectly valid pointer I figured I'd add a 'dummy' function to the BoxGeometry class that calls exactly the same (virtual) methods as the BoxGeometry::create function. You can see that I call that dummy function right before I call the create function. Strangely enough, the pointer is still valid inside the dummy function as well. I can call both lock() and unlock() without a problem. Only inside the create() function does the lock() call (in fact, any call of a virtual function of the ColorBuffer object) fail. I'm completely lost. I have no idea how this is possible. Would anyone be able to explain why this might happen?

Share this post


Link to post
Share on other sites
Evil Steve    2017
Hmm, Are you sure the pointer hasn't been deleted prior to being used? What is the value of the cb pointer? 0x3f800004 doesn't look like a valid heap or stack pointer to me, it's way too high.

Are you doing a debug build? It should crap all over the heap on delete calls to make bugs like that easy to spot.

Another possiblity is a buffer overflow or something I suppose.

Share this post


Link to post
Share on other sites
rogierpennink    400
Yes, I forgot to mention that the pointer doesn't get deleted anywhere. There is only one place where the buffer gets deleted and that is in the BoxGeometry's base class destructor. For certainty, I've put in a simple messagebox call in that destructor and it doesn't seem to be called before this error occurs.

Also, this is a debug build.

Share this post


Link to post
Share on other sites
rogierpennink    400
Not of the ColorBuffer class. All I do is create a new instance using 'new' and passing the resulting pointer around. But what really boggles my mind is that no other operations occur between the dummy() function and the create() function and that the trouble only starts in the create() function. How can a pointer become invalid in such a short time-frame?

Share this post


Link to post
Share on other sites
rogierpennink    400
I just attempted the following:

I commented out the call to BoxGeometry::create and then did:
box->getColorBuffer()->lock();
box->getColorBuffer()->unlock();
So the createBox function now looks like this:
BoxGeometry* U3DRenderer::createBox( float width, float height, float depth )
{
/* First, request an index and a vertex buffer. */
IndexBuffer* ib = this->createIndexBuffer( 36 ); // Box needs 36 indices
VertexBuffer* vb = this->createVertexBuffer( 8, BT_STATIC ); // Box has 8 vertices
ColorBuffer* cb = this->createColorBuffer( 8, BT_STATIC ); // Box has 8 vertices

/* Create the box geometry with requested values. */
BoxGeometry* box = new BoxGeometry( vb, width, height, depth );

/* Assign vertex/index buffers. */
box->setColorBuffer( cb );
box->setIndexBuffer( ib );

box->dummy();

/* Since BoxGeometry now has space for the vertices and indices, we can create the geometry. */
//box->create();

box->getColorBuffer()->lock();
box->getColorBuffer()->unlock();

return box;
}


Again, there are no problems running it. It seems that it has something to do with the create function, but since the dummy function works and contains the same virtual function calls on the colorbuffer object I am left flabbergasted as to why it would fail in the create() function...

Share this post


Link to post
Share on other sites
rogierpennink    400
Certainly, here they are:
class BaseGeometry : public Rtti
{
public:
/* Constructor and virtual destructor. */
BaseGeometry();
BaseGeometry( GeometryType gt );
BaseGeometry( GeometryType gt, VertexBuffer* vb );
virtual ~BaseGeometry();

/* Getters and Setters. */
inline VertexBuffer* getVertexBuffer() { return vb; }
inline void setVertexBuffer( VertexBuffer *v ) { this->vb = v; }
inline IndexBuffer* getIndexBuffer() { return ib; }
inline void setIndexBuffer( IndexBuffer *i ) { this->ib = i; }
inline ColorBuffer* getColorBuffer() { return cb; }
inline void setColorBuffer( ColorBuffer *c ) { this->cb = c; }
inline GeometryType getGeometryType() { return this->gType; }

/* Information. */
inline bool usesIndices() { return ib ? true : false; }
inline bool usesColors() { return cb ? true : false; }

/* Return rtti information. */
virtual std::string getClassname() { return "BaseGeometry"; }

protected:

VertexBuffer* vb;
IndexBuffer* ib;
ColorBuffer* cb;

GeometryType gType;
};


class BoxGeometry : public BaseGeometry
{
public:
/* Default constructor. */
BoxGeometry();
/* Constructor taking a VertexBuffer as argument. */
BoxGeometry( VertexBuffer* v );
/* Constructor taking a VertexBuffer and 3 dimensions for the box as arguments. */
BoxGeometry( VertexBuffer* v, float width, float height, float depth );
/* Virtual destructor. */
virtual ~BoxGeometry();

/* BoxGeometry public interface. */
void setDimensions( float width, float height, float depth );
void create();
void dummy();

/* Getters. */
inline float getWidth() { return width; }
inline float getHeight() { return height; }
inline float getDepth() { return depth; }
inline Color getColor() { return color; }

/* Setters. */
inline void setColor( Color& c ) { color = c; }

/* Return rtti information. */
std::string getClassname() { return "BaseGeometry.BoxGeometry"; }

private:
Color color; // Box color
float width; // Length along the x-axis
float height; // Length along the y-axis
float depth; // Length along the z-axis
bool isCreated; // Has the object already been created?
};

Share this post


Link to post
Share on other sites
Sandman    2210
And what else does the create() function do that the dummy function does not?

It's looking like something is probably writing out of bounds and screwing up your pointer. Try stepping through the create function in a debugger and watch keep an eye on what's happening to the cb pointer at each step.

in fact, thinking about it, 0x3f800004 looks suspiciously like it's meant to be a floating point number.

Share this post


Link to post
Share on other sites
rogierpennink    400
Wow, thanks a thousand times. In the vertexbuffer and colorbuffer class I was copying vertex/color data to a wrong offset; I used the arguments vertCount/colCount respectively that were passed into those functions and that I sheepishly gave the same name as the class members that keep track of the number of vertices/colors inside the buffer.

This is the first time I'm having one of those 'shoot yourself in the foot' situations with C++ and I have to say that I look at the language with an increasing feeling of awe...

I can't thank you enough, I can finally continue rendering things! :D

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this