Load an OpenGL buffer from file The function's job is to load a vertex attribute buffer or an element buffer from a specified path, and upload it to OpenGL. Also, optionally, return the number of vertices/indices loaded through an output parameter.

Signature:



GLuint loadBufferFromFile(std::string const&amp; bufferPath, unsigned char vertexElementCount, GLenum vertexFormat, GLenum bufferType=GL_ARRAY_BUFFER, GLuint *vertexCount=NULL, GLenum usageHint=GL_STATIC_DRAW);


Body: 

GLuint loadBufferFromFile(std::string const&amp; bufferPath, unsigned char vertexElementCount, GLenum vertexFormat, GLenum bufferType, GLuint *vertexCount, GLenum usageHint)
{
    const std::unordered_set&lt;GLenum&gt; VALID_VERTEX_FORMATS = {GL_FLOAT, GL_UNSIGNED_INT, GL_UNSIGNED_BYTE};
    //bufferType and usageHint will be validated by OpenGL

    if(vertexElementCount &lt; 1 || vertexElementCount &gt; 4)
    {
        throw std::invalid_argument("Vertex element count must be in range &lt;1,4&gt;");
    }

    if(bufferType == GL_ELEMENT_ARRAY_BUFFER &amp;&amp; vertexElementCount != 1)
    {
        throw std::invalid_argument("Buffer of type GL_ELEMENT_ARRAY_BUFFER must have element count of 1");
    }

    if(VALID_VERTEX_FORMATS.find(vertexFormat) == VALID_VERTEX_FORMATS.end())
    {
        throw std::invalid_argument("Invalid vertex format");
    }

    std::ifstream file;
    file.open(bufferPath, std::ios::binary | std::ios::in);

    if(!file.is_open())
    {
        throw std::runtime_error(std::string("Buffer file ") + bufferPath + " could not be opened");
    }

    file.seekg(0, file.end);
    unsigned int length = file.tellg();
    file.seekg(0, file.beg);

    if(vertexCount != NULL)
    {
        std::size_t vertexSize;

        switch(vertexFormat)
        {
            case GL_FLOAT:
                vertexSize = sizeof(GLfloat);
                break;
            case GL_UNSIGNED_INT:
                vertexSize = sizeof(GLuint);
                break;
            case GL_UNSIGNED_BYTE:
                vertexSize = sizeof(GLubyte);
                break;
        }

        vertexSize *= vertexElementCount;
        *vertexCount = length/vertexSize;
    }

    char *buffer = new char [length];
    file.read(buffer, length);
    file.close();

    GLuint bufferID;
    glGenBuffers(1, &amp;bufferID);

    glBindBuffer(bufferType, bufferID);
    glBufferData(bufferType, length, buffer, usageHint);

    delete[] buffer;
    return bufferID;
}


Specific questions:


Is it appropriate to fine-tune data types when it is not strictly necessary, like I did with vertexElementCount? int would have also worked just fine. 
Is this level of error handling/validation appropriate?
Is it good to use a const collection for validation in this way? Are there runtime costs beyond the act of validation itself?
If yes, unordered_set or vector? On one hand, a set seems a logical choice for this, on the other, wisdom of the ancients says "Use vector for everything unless you absolutely can't!"
Are those appropriate exceptions to use?
Even though in this case I can be certain it will not lead to a memory leak (nothing in between new and delete throws exceptions), directly using dynamic allocation rubs me the wrong way. Can I avoid it in a reasonable way?
As for your questions:


Just use int, unless this is critical code where benchmarks have shown that integers are noticeably slower. Furthermore: your char will support 255 vertices which isn't a lot, I've loaded models with millions of vertices.
This level of error checking is possibly an indication that your code smells. You have a function that accepts many parameters, perhaps you can split it into multiple functions or a class type of structure.
Myself, I make everything const unless it cannot be const.
If the size is known at compile time, how about std::array instead of std::vector? As for std::unordered_set it seems too fancy for the job (e.g., a self balancing tree with 3 items, whoop! whoop!), but I have no strong arguments.
No opinion. Myself, I never use C++ exceptions - due to my lack of understanding / education. In other languages I never directly use the build-in Exception types, I always subclass my own - if anything it helps the library user understand if they are library errors or system errors.
You can place your new'ed memory in a std::unique_ptr or use the resize features of std::vector or std::string


Couple more random things:


file.close(); is optional, the std::ifstream will also close the resource (if it was open).
You're not testing for OpenGL related errors.
I don't like 'out' arguments, such as vertexCount. Just return a std::tuple with multiple arguments, or a struct.
Does your file contain binary data? You're reading it from disk, and directly upload it to the GPU without parsing/validation? If anything, make sure you've actually read the amount of bytes that tellg claims are available.
std::ifstream has a constructor, use it.