Hi everyone, new to the forum so not sure if this is the correct place to ask this question.
I'm writing a simple OpenGL application.
I am trying to create a base class for all the OpenGL resources used in the application so that inheriting from it they are forced to be in sync with the graphics API (be valid/initialized before usage and don't leak memory on the gpu).
Does it make sense to try to force this behavior via inheritance? are there better ways?
I came up with this:
class OGLResource
{
public:
OGLResource()
: _id(0) {};
// No Copy Constructor/Assignment allowed
OGLResource(const OGLResource&) = delete;
OGLResource& operator=(const OGLResource&) = delete;
OGLResource(OGLResource&& other) noexcept
{
_id = other._id;
other._id = 0;
};
OGLResource& operator=(OGLResource&& other) noexcept
{
if (this != &other)
{
FreeResources();
NotifyDestruction();
OGLUtils::CheckOGLErrors();
_id = other._id;
other._id = 0;
}
return *this;
};
~OGLResource()
{
if(_id)
throw "Trying to destroy an OpenGL resource without deleting it first";
}
void Create()
{
_id = InitResources();
OGLUtils::CheckOGLErrors();
NotifyCreation();
}
void Destroy()
{
FreeResources();
NotifyDestruction();
OGLUtils::CheckOGLErrors();
_id = 0;
}
unsigned int ID()
{
if (!_id)
throw "Trying to use an uninitialized/deleted OpenGL resource";
return _id;
}
protected:
virtual unsigned int InitResources() = 0;
virtual void FreeResources() = 0;
virtual std::string ResourceType() { return "UNKNOWN"; }
void NotifyCreation() { std::cout << std::endl << "Creating OGL Object: " + ResourceType() + "_" + std::to_string(ID()); }
void NotifyDestruction() { std::cout << std::endl << "Destroying OGL Object: " + ResourceType() + "_" + std::to_string(ID()); }
private:
unsigned int _id;
};
The idea is that subclasses must define the behavior of Init/FreeResources(), the base class will be responsible for calling them, and since the resource's ID is private and accessible only via a method in the base class I can be sure the resource is correctly initialized before use (as long as InitResources() is correctly implemented).
Same for the destruction part.
A subclass example:
class OGLVertexShader : public OGLResource
{
private:
std::string _source;
protected:
unsigned int InitResources() override { return glCreateShader(GL_VERTEX_SHADER); }
void FreeResources() override { glDeleteShader(OGLResource::ID()); }
std::string ResourceType() override { return "VERTEX_SHADER"; }
public:
OGLVertexShader(std::string source) : _source{ source }
{
OGLResource::Create();
const char* vsc = source.data();
glShaderSource(OGLResource::ID(), 1, &vsc, NULL);
glCompileShader(OGLResource::ID());
OGLUtils::CheckCompileErrors(OGLResource::ID(), ResourceType());
OGLUtils::CheckOGLErrors();
}
OGLVertexShader(OGLVertexShader&& other) noexcept : OGLResource(std::move(other))
{
_source = std::move(other._source);
};
OGLVertexShader& operator=(OGLVertexShader&& other) noexcept
{
if (this != &other)
{
OGLResource::operator=(std::move(other));
_source = std::move(other._source);
}
return *this;
};
~OGLVertexShader()
{
OGLResource::Destroy();
}
};
Does this make any sense? Could you point me in the right direction?