1) Throws std::bad_alloc when it can't allocate more memory. I don't use exceptions, so this terminates.
Yeah, Hanson's Array version does that and it's not even C++! (It raises a memory "exception" by setting setjmp / longjmp.)
2) Always allocates new buffer even though realloc can extend the current memory
Indeed. The stretchy buffer does a realloc:
"Unlike C++ vector<>, the stretchy_buffer has the same semantics as an object that you manually malloc and realloc."
-Sean Barrett
3) Has a bunch of stuff to handle std::move, destructors, constructors... I don't need any of that since all my types are POD
4) Tanks compile times
Man, I hear you. Modern C++ tends to very
casually bring in hidden complexity. There's a well-known graphics API tutorial that goes into de-duplicating vertices from a mesh by:
(a) Importing an unordered map
(b) Overloading several operators, and finally,
(c) Designing a template specialization for a custom hash function.
which lets it store unique vertices through a vertex data type acting as an index to the unordered map:
| // Loop over mesh shapes
// ...
if (uniqueVertices.count(vertex) == 0) {
uniqueVertices[vertex] = static_cast<uint32_t>(vertices.size());
vertices.push_back(vertex);
}
// ...
// End loop
|
Meanwhile, I went with vanilla C: a loop and memory compare over structs. Runtime performance was rather excellent. Over time, simpler decisions made my program compile faster and the codebase is more readable.
I mean, we'll need to introduce complexity as needed, like if / when my models grow in the million-vertices range. That's the thing though: the complete tutorial series never
once went into this range and the complexity detracted from teaching us the API.
However (!) When we switch to vanilla C a funny phenomenon is people immediately expect more out of us? For example, the tutorial from before uses TinyOBJ to load a 3D model, like so:
| if (!tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, MODEL_PATH.c_str())) {
throw std::runtime_error(warn + err);
}
|
Contrast it with the vanilla C version of TinyOBJ:
| int result = tinyobj_parse_obj(&attrib, &shapes, &num_shapes, &materials, &num_materials, MODEL_PATH, get_file_data, flags);
if (result != TINYOBJ_SUCCESS)
die("failed to parse obj file: '%s'!\n", MODEL_PATH);
|
Notice in particular the new
get_file_data and
flags. The first is a callback -- the C version requires us to register one to read model data without any parsing or modification. My implementation does an
mmap over the file, which is OS-dependent.
The flags parameter is usually set to
TINYOBJ_FLAG_TRIANGULATE to treat our shape's faces as triangles. I believe the C++ version always defaults to triangulation so LoadObj() has the value as a default argument.