-1

I am writing a small toy game engine using Tinyobjloader for loading .obj files. I store the vertex data and everything using glm::vecX to make things easier.

Tinyobjloader gives me an std::vector<float>, when I want an std::vector<glm::vecX>. How would I do this without copying?

To be clear, a glm::vecX is a simple struct containing, for example, the float members x, y, z.

I was thinking that since structs can behave a bit like arrays, that std::move would work, but no luck.

Thanks!

Edit: I know I wasn't clear about this, sorry. I would like to either move the std::vector<float> into an std::vector<glm::vecX> or pass it as a std::vector<glm::vecX>&.

Copying the data using std::memcpy works fine, but it copies the data, which I would like to avoid.

Severin Pappadeux
  • 18,636
  • 3
  • 38
  • 64
fordcars
  • 457
  • 3
  • 14

2 Answers2

1

It may be possible to directly interpret the contents of the vector as instances of the struct, without having to copy the data. If you can guarantee the representation is compatible, that is. The contents of a vector<float> are laid out in memory as a sequence of float values directly following each other (an array) with no extra padding, while the contents of a vector<glm::vecX> are laid out as a sequence of vecX. Thus, you need to ensure the following conditions hold:

  • That glm::vecX is exactly the size of X floats, with no padding. Depending on the declaration of the struct, this may be platform-dependant.
  • That the contents of the vector<float> are in the correct sequence, i.e. as [x1,y1,z1, x2,y2,z2, ...] for a vec3 instead of [x1,x2,...,xN,y1,y2...].

In that case, you can safely reinterpret the data pointer of the float vector as pointer to an array of vecX as in this example:

std::vector<float> myObjData = ...;
auto nVecs = myObjData.size() / 3; // You should check that there are no remainders!
glm::vec3* vecs = reinterpret_cast<glm::vec3*>(myObjData.data());
std::cout << vecs[0]; // Use vecs[0..nVecs-1]

You cannot, however, safely reinterpret the vector itself as a vector of glm::vecX, not even as a const vector, because the number of elements stored in the vector might not be consistent after the reinterpretation. It depends on whether the vector<T> code stores the number of elements directly, or the number of allocated bytes (and then size() divides that by sizeof(T)):

// Don't do this, the result of .size() and .end() may be wrong!
const std::vector<glm::vec3>& bad = *reinterpret_cast<std::vector<glm::vec3>*>(&myObjData);
bad[bad.size()-1].z = 0; // Potential BOOM!

Most of the time, however, you don't need to pass an actual vector, since most functions in the standard library accept a container range, which is easy to give for arrays like the one in the first example. So, if you wanted to sort your vec3 array based on z position, and then print it out you would do:

// nVecs and vecs from the first example
std::sort(vecs, vecs+nVecs, // Sort by Z position
    [](const glm::vec3& a, const glm::vec3& b) { return a.z < b.z; });
std::copy(vecs, vecs+nVecs, std::ostream_iterator<glm::vec3>(std::cout, "\n"));
Javier Martín
  • 2,537
  • 10
  • 15
  • Oooo, fancy! Since `glm::vecX` is supposed to be paddingless (that's the point of glm). Now, of course, as you said, this could be platform-dependant, so I'll be checking for that before using it. [Check this out](http://stackoverflow.com/questions/13571898/c-opengl-glm-and-struct-padding) and [this](http://stackoverflow.com/questions/33058927/how-does-opengl-fill-buffers-and-read-them-back) for more information on this. – fordcars Jan 04 '16 at 19:24
0

In short: It is - to the best of my knowledge - not possible without copying.

And in my opinion, std::memcpy has no business being used with std::vector.

MikeMB
  • 20,029
  • 9
  • 57
  • 102