0

I am learning a little bit of opengl and i run into condition where i needed to use templates the file which has class declaration and defination is :

#pragma once
#include <vector>
#include <glad/glad.h>

template<class T>
class Vertexbuffer
{
private:
    unsigned m_bufferID;
    std::vector<T> m_data;
    unsigned m_capacity;
    unsigned m_count;
    mutable bool gpu_del;


    void setup(const T* vertices, const  unsigned count);

public:
    const std::vector<T>& getdata() { return m_data; }
    Vertexbuffer() :m_bufferID(0), m_capacity(0), m_count(0), gpu_del(true) {}
    Vertexbuffer(const T* vertices, const  unsigned count, bool g_del = true);
    Vertexbuffer(const float* data, unsigned size);
    Vertexbuffer(const Vertexbuffer& in);
    ~Vertexbuffer();

    void Bind() const;
    void Unbind() const;

    void AddData(const T* vertices,const  unsigned count);

    const unsigned getID() const { return m_bufferID; }

};


template<class T>
Vertexbuffer<T>::Vertexbuffer(const T* vertices, const  unsigned count, bool g_del) :gpu_del(g_del)
{
    setup(vertices, count);
}

template<class T>
Vertexbuffer<T>::Vertexbuffer(const float* data, unsigned size) : gpu_del(true), m_capacity(size), m_count(size)
{
    GLcall(glGenBuffers(1, &m_bufferID));
    GLcall(glBindBuffer(GL_ARRAY_BUFFER, m_bufferID));
    GLcall(glBufferData(GL_ARRAY_BUFFER, size * sizeof(float), data, GL_DYNAMIC_DRAW));
}

template<class T>
Vertexbuffer<T>::~Vertexbuffer()
{
    if (gpu_del) {
        //std::cout << "vbo freed\n";
        GLcall(glDeleteBuffers(1, &m_bufferID));
    }
}


template<class T>
void Vertexbuffer<T>::setup(const T* vertices, const unsigned count)
{

    if (vertices == nullptr) {
        m_capacity = count;
        m_count = 0;
        if (count == 0)
            return;
    }
    else {
        m_capacity = count;
        m_count = count;
        m_data.reserve(m_count);
        for (uint i = 0; i < m_count; i++) {
            m_data.push_back(vertices[i]);
        }
    }

    GLcall(glGenBuffers(1, &m_bufferID));
    GLcall(glBindBuffer(GL_ARRAY_BUFFER, m_bufferID));

    GLcall(glBufferData(GL_ARRAY_BUFFER, sizeof(T) * count, vertices, GL_DYNAMIC_DRAW));
    GLcall(glBindBuffer(GL_ARRAY_BUFFER, 0));

}

template<class T>
void Vertexbuffer<T>::Bind() const
{
    GLcall(glBindBuffer(GL_ARRAY_BUFFER, m_bufferID));
}



template<class T>
void Vertexbuffer<T>::Unbind() const
{
    GLcall(glBindBuffer(GL_ARRAY_BUFFER, 0));

}

template<class T>
void Vertexbuffer<T>::AddData(const T* vertices, const unsigned count)
{
    if ((m_bufferID == 0) || (m_count == 0)) {

        setup(vertices, count);

    }
    else
    {
        m_data.resize(m_count + count);
        m_capacity = m_data.capacity();
        unsigned a = 0;
        for (uint i = 0; i < count; i++) {
            a = i + m_count;
            m_data[a] = vertices[i];
        }
        GLcall(glBindBuffer(GL_ARRAY_BUFFER, m_bufferID));
        GLcall(void* pointer = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY));

#ifdef _MSC_VER
        void* tempdata = _malloca(m_count * sizeof(T));
        memcpy_s(tempdata, m_count * sizeof(T), pointer, m_count * sizeof(T));
#else
        void* tempdata = malloc(m_count * sizeof(T));
        memcpy(tempdata, pointer, m_count * sizeof(T));
#endif

        GLcall(glDeleteBuffers(1, &m_bufferID));
        GLcall(glGenBuffers(1, &m_bufferID));
        GLcall(glBindBuffer(GL_ARRAY_BUFFER, m_bufferID));
        GLcall(glBufferData(GL_ARRAY_BUFFER, (m_count + count) * sizeof(T), nullptr, GL_DYNAMIC_DRAW));
        GLcall(glBufferSubData(GL_ARRAY_BUFFER, 0, m_count * sizeof(T), tempdata));
        GLcall(glBufferSubData(GL_ARRAY_BUFFER, m_count * sizeof(T), sizeof(T) * count, vertices));
        GLcall(glBindBuffer(GL_ARRAY_BUFFER, 0));
        m_count = m_data.size();

    }

}

template<class T>
Vertexbuffer<T>::Vertexbuffer(const Vertexbuffer& in) 
    : m_bufferID(in.m_bufferID), m_capacity(in.m_capacity), m_count(in.m_count), gpu_del(true) 
{
    m_data = in.m_data;
    in.gpu_del = false;
}

this is a file which works fine as it has all definition at the bottom, but when I try to copy all definition to separate cpp file and as I learnt in this site:

https://www.codeproject.com/Articles/48575/How-to-Define-a-Template-Class-in-a-h-File-and-Imp#:~:text=The%20common%20procedure%20in%20C%2B%2B,and%20linking%20problems%20will%20arise.

I just wrote template class Vertexbuffer<int>; at the end of the .cpp file and it didnot work. But if we write the program mentioned in the page then it works! What am I missing here?

Any helps or ideas are warmly welcomed..

EDIT1

I created the .cpp file by cutting all the definitions of all member functions to vertexbuffer.cpp and just left

template<class T>
class Vertexbuffer
{
...
}

declaration like this in veretxbuffer.h file now i get a linking error like 1>Renderer.obj : error LNK2001: unresolved external symbol "public: __cdecl Vertexbuffer<struct Vertex>::Vertexbuffer<struct Vertex>(struct Vertex const *,unsigned int,bool)" (??0?$Vertexbuffer@UVertex@@@@QEAA@PEBUVertex@@I_N@Z) at each and every file where this class is used to make object. But when i keep all the code like all the member unction defination in .h file it works as expected.

template class Vertexbuffer<int>; was added at the bottom of .cpp file just to avoid linking error as said in the site above.

EDIT2

It worked when I explicitly instantiated the class with template argument being all the classes and data types which I was using in the whole project at the end of the .cpp file like

template class Vertexbuffer<Vertex>;
template class Vertexbuffer<float>;

which means I can only use the template with these template arguments and even defining these in respective file where i intend to use these instantiations did not work. Any workaround that so that i can instantiate the Vertexbuffer class with any class as template argument without changing the .cpp file?

dstrants
  • 7,423
  • 2
  • 19
  • 27
ankit
  • 171
  • 1
  • 1
  • 11
  • 1
    What do you mean "it didnot work"? What did you expect to accomplish by putting `template class Vertexbuffer;`, in some cpp file? With most C++ compilers, all that does is explicitly instantiate the template, was that your goal, and why? How did you conclude that this "did not work"? P.S. Having reviewed the nonsense at the linked site, it just reinforces my belief that reading random web sites is a poor way to learn C++. – Sam Varshavchik Jun 09 '20 at 12:53
  • 2
    Describe "it didnot work" in more detail. That is not useful by itself. How did it not work? Does it not compile? Is there no error message? Does it behave different from what you expected. How did you expect it to behave? – eerorika Jun 09 '20 at 12:53
  • please show a [mcve] for the broken code instead of the one that is fine. I suppose the details of the template are not essential for your problem, so you could make a template that is ~5 lines of code perhaps. What did you put in the .h and what exactly did you put in the .cpp ? – 463035818_is_not_an_ai Jun 09 '20 at 12:55
  • Only a guess, but you might be missing that you need to provide an explicit instantiation of the template for every template argument that is used in the linked code. Are you sure that `int` is enough? – Daniel Langr Jun 09 '20 at 13:00
  • None of the 3 methods shown in the code project article are the correct way to implement templated classes in cpp files. See the linked duplicate for the correct methods. – Alan Birtles Jun 09 '20 at 13:01
  • sorry for my bad or missing details but i have edited the post will not try do those kinds of mistake again.. – ankit Jun 09 '20 at 13:10
  • *Any workaround that so that i can instantiate...* - just implement templates in headers, then the compiler will do it for you. – Evg Jun 09 '20 at 13:48
  • @Evg what if the member implementations are meant to be private? and we are supposed to make a .lib file out of it and expect it to work with just including the .h file and link the made .lib file? – ankit Jun 09 '20 at 14:18
  • 1
    @ankit, unfortunately, you can't implement templates privately. To put class templates into a compiled library, you have to use explicit instantiations for all predefined templates parameters (effectively making class templates just ordinary classes). The only workaround is to replace templated code with non-templated code, e.g. via PIMPL idiom. This is not always possible. In C++20 we'll have modules to address this and other issues. – Evg Jun 09 '20 at 14:49

0 Answers0