I have learned my first fun little lesson with C++.
OpenGL involves buffers full of floating point numbers, or GLfloats
, both for vertices and colors. These buffers have to be passed into OpenGL functions as ‘raw’ buffers, i.e. as a GLfloat*
that points to a bunch of consecutive GLfloat
s.
So this seems like a perfect opportunity to create a dynamic array using some of the funky new C++ pointer types! This should get me in some trouble. I could probably just use a vector, but that wouldn’t be any fun. Anyway, here’s my class:
#include <glad/glad.h>
#include <memory>
class DynamicGLFloatBuffer
{
int len;
int capacity;
std::unique_ptr<GLfloat[]> data;
public:
DynamicGLFloatBuffer();
void append(const GLfloat* newdata, int newdatalen);
void clear();
GLfloat* get();
int length();
int size();
};
And the constructor:
DynamicGLFloatBuffer::DynamicGLFloatBuffer()
{
len = 0;
capacity = 65536;
data = std::make_unique<GLfloat[]>(capacity);
}
There’s the fun part, that make_unique
function. That creates a unique_ptr
, which ensures that the memory allocated to the array belongs to us and only us, and it will be deleted for us when we go out of scope.
Where I got into trouble was in the append
function:
void DynamicGLFloatBuffer::append(const GLfloat* newdata, int newdatalen)
{
// if we don't have enough room
if (capacity < len + newdatalen)
{
// double capacity until we have enough
while (capacity < len + newdatalen) capacity *= 2;
// create new array
std::unique_ptr<GLfloat[]> tempdata = std::make_unique<GLfloat[]>(capacity);
// copy data into new array
std::memcpy(tempdata.get(), data.get(), len);
// adopt new array
data = tempdata;
}
// copy memory into place
std::memcpy(data.get() + len, newdata, newdatalen * sizeof(GLfloat));
// adjust length to include newdatalen
len += newdatalen;
}
You can’t see it here, but in my IDE there’s a red squiggly line under the equals sign in data = tempdata;
, and it says:
function "std::unique_ptr<_Ty [], _Dx>::operator=(const std::unique_ptr<_Ty [], _Dx> &) [with _Ty=GLfloat, _Dx=std::default_delete<GLfloat []>]" (declared at line 2055 of "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.26.28801\include\memory") cannot be referenced -- it is a deleted function
Wow I am so glad they have made my life easier with these new C++ constructs.
Well it turns out you can’t just assign a unique_ptr
; you have to “move” it, like this:
data = std::move(tempdata);
And then everybody’s happy. I’m not sure why they couldn’t have just overridden that ‘=
‘ operator to do a move, since that’s what I have to do anyway… there’s nothing else to do in that situation.
We can go a step further into modern C++ and use the “auto” keyword to declare tempdata
:
auto tempdata = std::make_unique<GLfloat[]>(capacity);
As far as I can tell, this “auto” keyword exists solely so that I won’t have to type std::unique_ptr<GLfloat[]>
again. Which is nice, I guess. Seems like overkill, I’m not sure if it really makes code more readable or safer. But maybe there is something I don’t know about it yet.