unique_ptr, move, auto

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 GLfloats.

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.

Leave a Reply

Your email address will not be published. Required fields are marked *