0

In a file called gl_ext.h I have the following:

#ifndef GLEXT_H_INCLUDED
#define GLEXT_H_INCLUDED

#include <stdexcept>

#ifdef WIN32
#include <Windows.h>
#include <GL/GL.h>
#include <GL/glext.h>
# define glGetProcAddress(arg) wglGetProcAddress(arg)
#elif __linux__
#include <GL/gl.h>
#include <GL/glext.h>
# include <GL/glx.h>
# define glGetProcAddress(arg) glXGetProcAddress((const GLubyte*)arg)
#endif

PFNGLCREATESHADERPROC glCreateShader = 0;

namespace glext
{
  bool load_gl_extensions()
  {
    static bool loaded = false;
    if (loaded) {
      return true;
    }
    if (!glCreateShader) {
      glCreateShader = 
        (PFNGLCREATESHADERPROC)(glGetProcAddress("glCreateShader"));
      if (!glCreateShader) {
        throw "Failed to load glCreateShader";
      }
    }
  }
}
#endif

When building from within qt creator using the following .pro file

QT += core gui opengl

TEMPLATE = app
TARGET = GLExtensions
INCLUDEPATH += .

LIBS += -lGL
HEADERS += gl_ext.h \
           qtrenderer.h        

SOURCES += main.cpp \
           qtrenderer.cpp

The usage of this "header library" is as follows: main.cpp

#include <QtGui/QApplication>
#include "qtrenderer.h"

int main(int argc, char * argv[]) {
  QApplication app(argc, argv);

  QtRenderer *renderer = new QtRenderer();
  renderer->show();

  app.exec();
}

qtrenderer.h

#ifndef QTRENDERER_H_INCLUDED
#define QTRENDERER_H_INCLUDED

#include <QtCore/QObject>
#include <QtOpenGL/QGLWidget>

#include <gl_ext.h>

class QtRenderer : public QGLWidget
{

  Q_OBJECT

private:
  QtRenderer(const QtRenderer &other);
  QtRenderer &operator = (const QtRenderer &other);

protected:
  virtual void paintGL();
  virtual void initializeGL();

public:
  QtRenderer();
  ~QtRenderer();

public slots:
  virtual void updateGL();
};

#endif

qtrenderer.cpp

#include "qtrenderer.h"
QtRenderer::QtRenderer() :
  QGLWidget() {
}

QtRenderer::~QtRenderer() {
}

void QtRenderer::initializeGL() {
  try {
    glext::load_gl_extensions();
  } catch (...) {
    throw std::runtime_error("Failed to load needed extensions.");
  }
}

void QtRenderer::paintGL() {
  swapBuffers();
}

void QtRenderer::updateGL() {
  paintGL();
}

When building this source code using

gcc (GCC) 4.7.2 20120921 (Red Hat 4.7.2-2)

I get the following build errors:

qtrenderer.o: In function `glext::load_gl_extensions()':
/home/mehoggan/Devel/test_gl/./gl_ext.h:28: multiple definition of `glCreateShader'
main.o:/home/mehoggan/Devel/test_gl/./gl_ext.h:28: first defined here

Why is this so?

iKlsR
  • 2,642
  • 6
  • 27
  • 45
Matthew Hoggan
  • 7,402
  • 16
  • 75
  • 140

2 Answers2

3

Well, the header gl_ext.h is included multiple times. Remember that #include is like replacing the #include statement with the content of the file in a copy&paste manner.

You should put the implementation of load_gl_extensions() into a .cpp file, and put only the declaration into the header file.

gl_ext.h:

//...

extern PFNGLCREATESHADERPROC glCreateShader;

namespace glext
{
  bool load_gl_extensions();
}

gl_ext.cpp:

#include "gl_ext.h"

PFNGLCREATESHADERPROC glCreateShader = 0;

namespace glext
{
  bool load_gl_extensions()
  {
    static bool loaded = false;
    if (loaded) {
      return true;
    }
    if (!glCreateShader) {
      glCreateShader = 
        (PFNGLCREATESHADERPROC)(glGetProcAddress("glCreateShader"));
      if (!glCreateShader) {
        throw "Failed to load glCreateShader";
      }
    }
  }
}

extern tells the compiler that the variable/function pointer (glCreateShader) is placed in a different compilation unit (every .cpp file is compiled as a different unit). The linker then inserts the correct memory-adress of your variable. Maybe you should do some research on how C++ compilation and linkage works.

Marius
  • 2,234
  • 16
  • 18
  • I dont want users of the "library to have to include cpp files into their source to get it to work with their code base. I want it to remain in the header hence the #ifndef #define guard. – Matthew Hoggan Jun 24 '13 at 19:47
  • 1
    That just doesn't work with C++ if you need to introduce global variables. If you are creating a library, you should create a static or dynamic library (.lib/.dll on Windows, .a/.so on Linux) and distribute that along with your header. – Marius Jun 24 '13 at 19:50
  • glm (gl mathematics) is an example of where this is not the case. I know boost has struggled with the same thing, and that is why certain portions of their library is not a header only component. Is there a work around for what I want? – Matthew Hoggan Jun 24 '13 at 19:52
  • Yes, capsulate these things inside a Class. You might name it 'GlLibImpl' or something. Do not use global variables, all of them go into your class. Then derive your class from a pure virtual class named 'GlLib'. Then let the user of your library instantiate your class like `GlLib *glLib = new GlLibImpl()` in exactly one .cpp file, then tell the user to use your GlLib Interface from now on. The downside: users have to call glLib.glCreateShader instead of just glCreateShader and you force the user to distribute the pointer to your class-instance through his/her whole program. – Marius Jun 24 '13 at 20:00
  • For handling OpenGL extensions you might as well take a look at GLEW (GL Extension Wrangler) or GLee. – Marius Jun 24 '13 at 20:01
  • GLEW is not supported on all systems. In some cases you break license agreements on some OSes by installing it. RHEL 5 and Open SUSE 11 sp 1 are examples. If you try and write a library with dependencies only on opengl you have to reconsider some things. – Matthew Hoggan Jun 24 '13 at 20:04
  • 2
    By the way, the #ifdef guard just prevents multiple inclusion in the SAME compilation unit, not among different ones. Preventing inclusion among different compilation units would be like not including them at all except in one unit. – Marius Jun 24 '13 at 20:40
0

The solution to the problem consisted of making the method inlined and declaring the function pointer as static.

Matthew Hoggan
  • 7,402
  • 16
  • 75
  • 140
  • Please note that `inline` is just a hint to the compiler. The compiler decides wether to inline your method or not. There are some extensions like __force_inline etc. but they are compiler-specific. You would run into the same issue if the functions gets longer and the compiler starts refusing to inline it. – Marius Jun 24 '13 at 20:56
  • @Marius - I believe inline makes compiler/linker 'accept' multiple (identical) definitions, even if the function is never actually inlined. – Roddy Jun 24 '13 at 21:14
  • You are right Roddy, my bad. But code bloat is always an issue when putting function bodys into headers and the initialization of opengl extensions is usually a lot of code, so the bloat would be even worse. – Marius Jun 24 '13 at 21:19
  • I would rather get code bloat then occur the time delay when pushing and poping off the stack through a function call. – Matthew Hoggan Jun 24 '13 at 21:35