Ручная загрузка расширений OpenGL LNK2005 и LNK1169

#visual-studio #opengl #visual-c

#visual-studio #opengl #visual-c

Вопрос:

Я использую Visual C и вручную пытаюсь загрузить расширения OpenGL. Однако по какой-то причине определение указателей из предоставленных Khronos Groups заголовков приводит к ошибкам компоновщика, и поэтому я никогда не получаю изменений даже для определения этих функций в моем контексте OpenGL. Ниже я включил упрощенную версию моего кода и его структуру, которая вызывает ту же проблему.

 //Test.cpp
#include "MyGL.h"
#include <iostream>

int main()
{
    std::cout << "Hello World!n"; 
}
  
 //MyGL.h
#pragma once
#include "MyGLOpenGL.h"
  
 //MyGL.cpp
#include "MyGL.h"
  
 //MyGLOpenGL.h
#pragma once

#include <windows.h> // Windows functions
#include <GL/gl.h> // Provided w/ Compiler
#include "GL/glext.h" // Put out by Khronos Group
#include "GL/wglext.h" // Put out by Khronos Group
#pragma comment(lib, "opengl32.lib") // Provided w/ Compiler



#ifndef GL_OPENGL
#define GL_OPENGL

void glInitPointers(); // Defines pointers to opengl functions
void* glGetAnyProcAddress(const char* name); // Gets a pointer to any OpenGL function

extern PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB;
extern PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB;
#endif

  
 //MyGLOpenGL.cpp
#include "MyGLOpenGL.h"

void glInitPointers() {
    wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)glGetAnyProcAddress("wglChoosePixelFormatARB"); //load function
    wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)glGetAnyProcAddress("wglCreateContextAttribsARB"); //load function
    return;
}

void* glGetAnyProcAddress(const char* name) {
    void *pointer = (void *)wglGetProcAddress(name);
    if (pointer == 0 || (pointer == (void*)0x1) || (pointer == (void*)0x2) || (pointer == (void*)0x3) || (pointer == (void*)-1)) {
        HMODULE module = LoadLibraryW(L"opengl32.dll");
        pointer = (void *)GetProcAddress(module, name);
    }
    return pointer;
};
  

При компиляции этого я получаю следующие ошибки компоновщика:

 1>MyGLOpenGL.obj : error LNK2001: unresolved external symbol "int (__stdcall* wglChoosePixelFormatARB)(struct HDC__ *,int const *,float const *,unsigned int,int *,unsigned int *)" (?wglChoosePixelFormatARB@@3P6GHPAUHDC__@@PBHPBMIPAHPAI@ZA)
1>MyGLOpenGL.obj : error LNK2001: unresolved external symbol "struct HGLRC__ * (__stdcall* wglCreateContextAttribsARB)(struct HDC__ *,struct HGLRC__ *,int const *)" (?wglCreateContextAttribsARB@@3P6GPAUHGLRC__@@PAUHDC__@@PAU1@PBH@ZA)
1>G:DevelopmentTestDebugTest.exe : fatal error LNK1120: 2 unresolved externals
  

Поскольку проблема была неразрешена с внешними символами, я попытался удалить ключевое слово extern, чтобы вместо этого получить эти две ошибки:

 1>MyGLOpenGL.obj :error LNK2005: "int (__stdcall* wglChoosePixelFormatARB)(struct HDC__ *,int const *,float const *,unsigned int,int *,unsigned int *)" (?wglChoosePixelFormatARB@@3P6GHPAUHDC__@@PBHPBMIPAHPAI@ZA) already defined in MyGL.obj
1>MyGLOpenGL.obj : error LNK2005: "struct HGLRC__ * (__stdcall* wglCreateContextAttribsARB)(struct HDC__ *,struct HGLRC__ *,int const *)" (?wglCreateContextAttribsARB@@3P6GPAUHGLRC__@@PAUHDC__@@PAU1@PBH@ZA) already defined in MyGL.obj
1>Test.obj : error LNK2005: "int (__stdcall* wglChoosePixelFormatARB)(struct HDC__ *,int const *,float const *,unsigned int,int *,unsigned int *)" (?wglChoosePixelFormatARB@@3P6GHPAUHDC__@@PBHPBMIPAHPAI@ZA) already defined in MyGL.obj
1>Test.obj : error LNK2005: "struct HGLRC__ * (__stdcall* wglCreateContextAttribsARB)(struct HDC__ *,struct HGLRC__ *,int const *)" (?wglCreateContextAttribsARB@@3P6GPAUHGLRC__@@PAUHDC__@@PAU1@PBH@ZA) already defined in MyGL.obj
1>G:DevelopmentTestDebugTest.exe : fatal error LNK1169: one or more multiply defined symbols found
  

Я также уже убедился, что «#pragma once» был во всех файлах, защита заголовка, добавление «OpenGL32.lib» в дополнительные зависимости в visual studio, добавление комментария pragma для библиотеки, установка указателей равными null в объявлении, и я просто в полной растерянности, что еще можно попробовать, даже после поиска проблемы в Google. И в моем случае, используя GLEW или любую другую библиотеку загрузки расширений, потому что это именно то, что я пытаюсь создать.

Ответ №1:

В C и C есть важное различие между объявлением и определением. Заявление по форме

 extern <type> <symbol>;
  

заявляет, что где-то есть какой-то символ, но на самом деле он не создает его. Это больше похоже на обещание компилятору, что символ будет определен где-то в другом месте. Когда ты писал

 extern PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB;
extern PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB;
  

на самом деле вы не создавали переменные для этих указателей на функции, вы просто «перечислили их в оглавлении» своей программы. На самом деле вам нужно где-то их определить. Т.е. в каком-то модуле компиляции вы должны написать (без extern).

 PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB;
PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB;
  

Это не имеет абсолютно ничего общего с инициализацией, как намекнул @EricLopushansky; все дело в том, что они действительно определяются. Поскольку это глобальная область видимости, они в любом случае будут инициализированы значением 0, даже если вы явно этого не напишете = 0 .

Комментарии:

1. Спасибо. Ваше объяснение было намного более кратким, чем мое, и, вероятно, более полезным для других людей, которые сталкиваются с такой же проблемой при прямом копировании кода из OpenGL wiki, не слишком задумываясь.

Ответ №2:

Это не очень хорошо объясняется OpenGL wiki в статье Загрузка функций OpenGL, но просто сделайте следующее. Установка указателя на null в файле cpp устранит проблему. Например:

 PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = NULL;
  

Для всех, кому интересно, вот соответствующий код из GLEW, который служит той же цели:

 //glew.h
#ifdef GLEW_STATIC
#  define GLEWAPI extern
#else
#  ifdef GLEW_BUILD
#    define GLEWAPI extern __declspec(dllexport)
#  else
#    define GLEWAPI extern __declspec(dllimport)
#  endif
#endif
#define GLEW_FUN_EXPORT GLEWAPI

//wglew.h
#define WGLEW_GET_FUN(x) x
#define wglChoosePixelFormatARB WGLEW_GET_FUN(__wglewChoosePixelFormatARB)
typedef BOOL (WINAPI * PFNWGLCHOOSEPIXELFORMATARBPROC) (HDC hdc, const int* piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats);
#define WGLEW_FUN_EXPORT GLEW_FUN_EXPORT
WGLEW_FUN_EXPORT PFNWGLCHOOSEPIXELFORMATARBPROC __wglewChoosePixelFormatARB;

//glew.c
#  define glewGetProcAddress(name) wglGetProcAddress((LPCSTR)name)
PFNWGLCHOOSEPIXELFORMATARBPROC __wglewChoosePixelFormatARB = NULL;
r = ((wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)glewGetProcAddress((const GLubyte*)"wglChoosePixelFormatARB")) == NULL) || r;


//I would be using this static so it would evaluate in my code to
//MyGLOpenGL.h
#define wglChoosePixelFormatARB __wglewChoosePixelFormatARB
typedef BOOL (WINAPI * PFNWGLCHOOSEPIXELFORMATARBPROC) (HDC hdc, const int* piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats);
extern PFNWGLCHOOSEPIXELFORMATARBPROC __wglewChoosePixelFormatARB;

//MyGLOpenGL.cpp
PFNWGLCHOOSEPIXELFORMATARBPROC __wglewChoosePixelFormatARB = NULL;
wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress((const GLubyte*)"wglChoosePixelFormatARB")