2013-06-24 2 views
0

в файле gl_ext.h я следующее:Несколько определений в C++

#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 

При создании внутри Qt Creator, используя следующий файл .pro

QT += core gui opengl 

TEMPLATE = app 
TARGET = GLExtensions 
INCLUDEPATH += . 

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

SOURCES += main.cpp \ 
      qtrenderer.cpp 

Использование этого " библиотека заголовка»выглядит следующим образом: 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(); 
} 

При создании этого исходного кода с помощью

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

я получаю ошибки построения:

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 

Почему это так?

+0

Как сказал Клиппи, «похоже, что вы пытаетесь написать библиотеку только для заголовков» – Roddy

ответ

3

Ну, заголовок gl_ext.h включен несколько раз. Помните, что #include похожа на замену оператора #include содержимым файла в копии & вставки.

Вы должны поместить реализацию load_gl_extensions() в .cpp-файл и поместить только объявление в заголовочный файл.

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 сообщает компилятору, что переменная/указатель функции (glCreateShader) помещается в другой единице компиляции (каждый .cpp файл скомпилирован как другое устройство). Затем компоновщик вставляет правильный адрес памяти вашей переменной. Возможно, вам следует провести некоторое исследование того, как работает компиляция и привязка C++.

+0

Я не хочу, чтобы пользователи «библиотеки» должны были включать файлы cpp в свой источник, чтобы заставить его работать с их кодом Я хочу, чтобы он остался в заголовке, поэтому защитник #ifndef #define. –

+1

Это просто не работает с C++, если вам нужно ввести глобальные переменные. Если вы создаете библиотеку, вы должны создать статическую или динамическую библиотеку (.lib/.dll в Windows, .a/.so в Linux) и распространять его вместе с вашим заголовком. – Marius

+0

glm (gl mathematics) - пример того, где это не так. Я знаю, что boost борется с тем же и поэтому некоторые части их библиотеки не являются компонентом только для заголовка. Есть ли работа для того, что я хочу? –

0

Решение проблемы состояло в том, чтобы сделать метод встроенным и объявить указатель функции как статический.

+0

Обратите внимание, что 'inline' - это всего лишь подсказка для компилятора. Компилятор решает, вставить ваш метод или нет. Существуют некоторые расширения, такие как __force_inline и т. Д., Но они специфичны для компилятора. Вы столкнулись бы с той же проблемой, если функции будут длиннее, а компилятор начнет отказываться от встроенного. – Marius

+0

@Marius - Я полагаю, что inline делает компилятор/компоновщик «признавать» несколько (идентичных) определений, даже если функция никогда не была встроена. – Roddy

+0

Вы правы, Родди, мой плохой. Но разбухание кода всегда является проблемой при установке функций bodys в заголовки, а инициализация расширений opengl - это, как правило, много кода, поэтому раздувание будет еще хуже. – Marius

Смежные вопросы