Skip to main content
gamedev.net
🔒 Locked

(SOLVED) boost::shared_ptr polymorphism problem

Started by mrbastard Jan 14, 2005 at 3:37 PM 4 replies 2.4k views
Original Post
mrbastard
mrbastard
I've been using boost's shared_ptr to great effect in my game engine. Suddenly I'm seeing weird behaviour from them. I know some people here use boost, so thought maybe I could get some suggestions. (EDIT, OK this post is BIG. Just read the headers of the classes; I only put in the implementation details in case anyone spotted any silly mistakes I'd made there that may affect it. The headers should describe the problem fine though.) I define some classes as below: Shader.h

#ifndef SHADER_H
#define SHADER_H

#include <string>
using namespace std;

#include "glheader.h"

class Shader
{
public:
	Shader(void);
	virtual ~Shader(void);

	void SetSrc(string s){src=s;}
	bool LoadSrc(string filename);
	virtual void UploadSrc()=0;
	virtual bool Compile()=0;
	void Delete();
	GLhandleARB GetGLname(){return GLname;}

	

protected:
	//flags whether the src has been passed to the GL
	bool inGL;
	//the shader's handle
	GLhandleARB GLname;
	//string containing the glsl sourcecode of the shader
	string src;
	//the compile status of the shader
	GLint compile_status;
	
};

#endif





Shader.cpp

#include "Shader.h"
#include <fstream>
using namespace std;

Shader::Shader(void)
{
	inGL = false;
	GLname = 0;

}

Shader::~Shader(void)
{
	//if the shader is taking up mem on the server
	if (inGL)
	{
		//delete it
		Delete();
	}
}

bool Shader::LoadSrc(string filename)
{
	ifstream instream;
	instream.open(filename.c_str(),ios::in);
	if (instream.is_open())
	{
		//using the null character as a delimeter should get the whole file
		getline(instream,src,'\0');
		//close the file
		instream.close();
		return true;
	}

	return false;
}

void Shader::Delete()
{
	glDeleteObjectARB(GLname);
}





VertexShader.h

#ifndef VERTEX_SHADER_H
#define VERTEX_SHADER_H

#include "Shader.h"

class VertexShader : public Shader
{
public:
	VertexShader(void);
	~VertexShader(void);
	void UploadSrc();
	bool Compile();
};

#endif





VertexShader.cpp

#include "VertexShader.h"

VertexShader::VertexShader(void)
{
	this->Shader::Shader();
}

VertexShader::~VertexShader(void)
{
}

void VertexShader::UploadSrc()
{
	//if there's an object in use 
	if(inGL)
	{
		//delete it
		Delete();
	}
	const char* s = src.c_str();
	GLname = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
	glShaderSourceARB(GLname,1,&s,NULL);
}


bool VertexShader::Compile()
{
	glCompileShaderARB(GLname);
	glGetObjectParameterivARB(GLname, GL_OBJECT_COMPILE_STATUS_ARB, &compile_status);
	return compile_status;
}





FragmentShader.h

#ifndef FRAGMENT_SHADER_H
#define FRAGMENT_SHADER_H

#include "Shader.h"

class FragmentShader :	public Shader
{
public:
	FragmentShader(void);
	~FragmentShader(void);
	virtual void UploadSrc();
	virtual bool Compile();
};

#endif





and I use like so (test code only!) extract from game.cpp

///shadertest
	boost::shared_ptr<Shader> v(new VertexShader);
	boost::shared_ptr<Shader> f(new FragmentShader);
	boost::shared_ptr<GPUProgram>prog(new GPUProgram);
	
	//load shaders
	v->LoadSrc("./data/perpixel.vert");
	v->UploadSrc();
	f->LoadSrc("./data/perpixel.frag");
	f->UploadSrc();
	//attach them to the program
	prog->AttachShader(v);
	prog->AttachShader(f);
	//compile them
	v->Compile();
	f->Compile();
	//link the program
	prog->Link();


done that way, I get a runtime error 'Pure Virtual function call' on 'v->UploadSrc()' I thought maybe shared_ptrs to abstract classes didn't work (or didn't like having child classes being allocated in their constructor), so did it like so:

///shadertest
	boost::shared_ptr<VertexShader> v(new VertexShader);
	boost::shared_ptr<FragmentShader> f(new FragmentShader);
	boost::shared_ptr<GPUProgram>prog(new GPUProgram);
	
	//load shaders
	v->LoadSrc("./data/perpixel.vert");
	v->UploadSrc();
	f->LoadSrc("./data/perpixel.frag");
	f->UploadSrc();
	//attach them to the program
	boost::shared_ptr<Shader> shader;
	shader = v;
	prog->AttachShader(shader);
	shader = f;
	prog->AttachShader(shader);
	//compile them
	v->Compile();
	f->Compile();
	//link the program
	prog->Link();





and still the same problem. This is strange, as surely the shared_ptr would have no pure virtual functions, as it implements it's own versions of Shader's pure virtual functions. so I'm forced to do it like this

///shadertest
	boost::shared_ptr<VertexShader> v(new VertexShader);
	boost::shared_ptr<FragmentShader> f(new FragmentShader);
	boost::shared_ptr<GPUProgram>prog(new GPUProgram);
	
	//load shaders
	v->LoadSrc("./data/perpixel.vert");
	v->VertexShader::UploadSrc();
	f->LoadSrc("./data/perpixel.frag");
	f->FragmentShader::UploadSrc();
	//attach them to the program
	boost::shared_ptr<Shader> shader;
	shader = v;
	prog->AttachShader(shader);
	shader = f;
	prog->AttachShader(shader);
	//compile them
	v->VertexShader::Compile();
	f->FragmentShader::Compile();
	//link the program
	prog->Link();





which seems to work. It doesn't break my design, but is far from perfect... Now the weird thing is, I wrote a simple test to check shared_ptr behaved how I thought it should

#include <iostream>
using namespace std;
#include <boost/shared_ptr.hpp>
using namespace boost;

struct a
{
	int n;
	a(){n=1;}
	virtual void print() = 0;
};

struct b : public a
{
	b(){n=2;}
	virtual void print(){cout << n;}
};

struct c : public a
{
	c(){n=3;}
	virtual void print(){cout << n;}
};


int main()
{
	shared_ptrsp(new b);
	shared_ptrspp(new c);
	sp->print();
	spp->print();
	return 0;
}





and that works fine! Any ideas why? Also any ideas of a workaround? - I'd much rather use shared_ptrs to the baseclass in most places. Thanks for reading Dan [Edited by - mrbastard on January 14, 2005 4:32:07 PM]
[size="1"]
Fruny
Fruny
Quote:
(EDIT, OK this post is BIG. Just read the headers of the classes; I only put in the implementation details in case anyone spotted any silly mistakes I'd made there that may affect it. The headers should describe the problem fine though.)


I'm sorry, but no. I'm definitely not willing to wade through a dozen code boxes before I even begin to understand out what it is exactly you have problems with. Distill your problem to a small-but-complete code sample and then we'll talk. Heck, while doing so, you might even figure out what's wrong all by yourself.
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Enigma
Enigma
AP is correct. Base class constructors are called by:
Derived::Derived()	:	Base(arguments){}

not through the this pointer. BCC and GCC wouldn't even compile it with the call you had (which is another argument for using multiple compilers).

Enigma
mrbastard
mrbastard
Quote:
Original post by Fruny
Distill your problem to a small-but-complete code sample and then we'll talk.


Understood. Check the final code box for an example that does work, and does the same thing as I'm trying to do in the others.

Heres the problem in a nutshell:

I can call member functions from a basesclass pointer and the real (eg the class the object was constructed as) functions are called instead - polymorphism rocks. This should work with shared_ptrs to baseclasses too, and in the small example, it does. However, in my real code (which is syntacticly and logically equivalent in every way I can think of that's relevant) it doesn't work. Hell, it doesn't even work through a shared_ptr to the actual type, unless I specify the type scope (eg BLAH::)
[size="1"]
mrbastard
mrbastard

Thanks AP! and thanks Enigma!
Yes, it seems to work now.

Yet another reason to get my linuxbox working again and do a gcc build.
I can't even imagine what msvc interpretted my gibberish as.

And thanks to everyone else whose time I've wasted...

I feel very silly now, please excuse me ;¬)
[size="1"]

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.