手动检索和存储指向 OpenGL 函数的指针时遇到问题
Posted
技术标签:
【中文标题】手动检索和存储指向 OpenGL 函数的指针时遇到问题【英文标题】:Trouble retrieving and storing pointers to OpenGL functions manually 【发布时间】:2016-06-02 16:20:52 【问题描述】:我在手动检索和存储指向 OpenGL 函数的指针时遇到了一些麻烦,这是我的代码的“简化 sn-p”版本:
#ifdef WIN32
#include <windows.h>
#endif
#include <GL/gl.h>
class CGLManager
public:
// Manager functions
bool GetAnyGLFuncAddress( const char *_cName, void *_pFunc );
bool LoadFunctions( void );
// OpenGL functions
void (APIENTRY *glBegin)( GLenum mode );
void (APIENTRY *glEnd)( void );
void (APIENTRY *glVertex3f)( GLfloat x, GLfloat y, GLfloat z );
private:
#ifdef WIN32
HMODULE m_hLib; // opengl32.dll
#else
void *m_hLib; // libGL.so
#endif
;
这是源代码:
extern CGLManager gGL;
// GetAnyGLFuncAddress - Attempt to retrieve the OpenGL function named "_cName" and store it in "_pFunc", returns true if success or false otherwise
bool CGLManager::GetAnyGLFuncAddress( const char *_cName, void *_pFunc )
#ifdef WIN32
// Similar to https://www.opengl.org/wiki/Load_OpenGL_Functions#Windows
_pFunc = (void *)wglGetProcAddress( _cName );
if ( _pFunc == 0 || (_pFunc == (void *)0x1) || (_pFunc == (void *)0x2) || (_pFunc == (void *)0x3) || (_pFunc == (void *)-1) )
_pFunc = (void *)GetProcAddress( m_hLib, _cName );
#else
// TODO: Test this
// According to some websites, NVIDIA drivers prefer the ARB implementation over the core one
_pFunc = (void *)glXGetProcAddressARB( _cName );
if ( _pFunc == 0 || (_pFunc == (void *)0x1) || (_pFunc == (void *)0x2) || (_pFunc == (void *)0x3) || (_pFunc == (void *)-1) )
_pFunc = (void *)glXGetProcAddress( _cName );
#endif
return (_pFunc != NULL);
// LoadFunctions - Attempt to retrieve all used OpenGL functions, returns true if all of them were retrieved or false if there is a single failure
bool CGLManager::LoadFunctions( void )
if ( !(GetAnyGLFuncAddress( "glBegin", &gGL.glBegin )) )
return false;
if ( !(GetAnyGLFuncAddress( "glEnd", &gGL.glEnd )) )
return false;
if ( !(GetAnyGLFuncAddress( "glVertex3f", &gGL.glVertex3f )) )
return false;
return true;
这是我的经理的一般工作方式:它首先检查游戏引擎使用的渲染器(软件、OpenGL 或 Direct3D),如果不是 OpenGL,我们将停止进一步研究。否则,我们加载库(opengl32.dll
或 libGL.so
)并检查它是否良好(同样,如果失败,我们停止),我们检索并存储指向 OpenGL 函数的指针(glBegin
,glEnd
, glVertex3f
) 使用 LoadFunctions
方法,如果一切正常或发生错误,我们将返回。
现在的问题: GetAnyGLFuncAddress
方法成功检索 OpenGL 函数(换句话说,glBegin
将返回 true,glARandomMethodThatDontExist
将返回 false)但出于某种原因,gGL.glBegin
LoadFunctions
中的(它是“朋友”)没有更新,它总是 NULL 导致崩溃。
我已经尝试了几个小时通过在 Internet 和 *** 上搜索来找到解决方案,但我没有找到任何可以解决问题的答案。
在我在 *** 上找到的许多网站和答案中,很多人建议使用像 GLEW 这样的 OpenGL 加载库,甚至 OpenGL wiki 也推荐它。 但是,由于我正在处理的环境的性质,我不能使用这些库,也不能直接使用 OpenGL 函数,我知道我正在经历手动做所有事情的痛苦方式,但我别无选择。
感谢您的回答。
【问题讨论】:
暂时忘记openGL等,如果你在GetAnyGLFuncAddress
中更新pFunc
(实际上是一个本地临时的),该功能将如何工作?您设置 pFunc
的值不会反映给调用者。我希望*pFunc = whatever;
,而不是pFunc = whatever
,即您应该取消引用该指针值。
"但是,由于我工作环境的性质,我不能使用那种库" ...这确实是一个非常奇怪的环境不能包含 .h 文件和 .cpp 文件的地方。有些加载器比其他加载器简单。
@PaulMcKenzie 在_pFunc
之前放置星号会导致 C2100 错误(非法间接)
@Shepard62700FR:这不是论坛。答案在答案部分。你不会编辑你的问题说它已经解决了。
@NicolBolas 抱歉,不会再发生了。
【参考方案1】:
bool CGLManager::GetAnyGLFuncAddress( const char *_cName, void *_pFunc )
这只是常见的、损坏的 C++。 _pFunc
是 void*
。指针就是值。改变_pFunc
的值不会改变传入的变量的值。
您应该只返回指针(NULL 表示失败条件),或者_pFunc
应该是void**
。不过,这可能需要调用者进行强制转换,GetAnyGLFuncAddress
需要执行 *_pFunc =
来设置值。
如果要返回函数指针,则需要在存储之前将返回的void*
转换为适当的函数指针类型。这就是为什么你经常看到 OpenGL 加载器使用 typedef 来表示函数指针类型。
typedef void (APIENTRY *(PFN_GLBEGIN))( GLenum );
...
PFN_GLBEGIN glBegin;
...
glBegin = static_cast<PFN_GLBEGIN>(GetAnyGLFuncAddress("glBegin"));
类似的东西。
【讨论】:
请注意,您正在返回指向函数的指针。 OP 似乎是通过一个参数来设置指针,并且从它的外观来看,这样做是不正确的。 @PaulMcKenzie:我在发布原始版本后意识到这一点。 @NicolBolas:我的第一次尝试就像你说的那样:返回指针而不是在方法中分配它,并根据成功/失败返回真或假,但由于某种原因,我永远无法“将结果分配给gGL.glBegin
。
@Shepard62700FR:当然不是。这是 C++,而不是 C。在 C++ 中,void*
指针不会神奇地将自己转换为其他类型。如果你想玩 C++ 中的类型游戏,你必须明确并进行适当的转换。
@Shepard62700FR:此外,“出于某种原因”在 C++ 中什么也没有发生。如果编译失败,它会告诉你原因是什么。【参考方案2】:
忘记你的最终目标是什么,我看到了基本的 C++ 错误。
让我们删掉所有不相关的代码并专注于此:
bool CGLManager::GetAnyGLFuncAddress( const char *_cName, void *_pFunc )
_pFunc = (void *)wglGetProcAddress( _cName ); // <-- This sets the local pointer
//...
然后你像这样调用上面的代码:
bool CGLManager::LoadFunctions( void )
if ( !(GetAnyGLFuncAddress( "glBegin", &gGL.glBegin )) )
return false;
您在第二个参数中传递了地址,但在函数GetAnyGLFuncAddress
中,您并没有更新值以便将新值反映回调用者。您正在设置本地值,因此不会设置 gGL.glBegin
(以及来自其他两个调用的所有其他地址)。
在GetAnyGLFuncAddress
内部,我原以为这会起作用:
bool CGLManager::GetAnyGLFuncAddress( const char *_cName, void *_pFunc )
*_pFunc = (void *)wglGetProcAddress( _cName ); // <-- Note the *
//...
在GetAnyGLFuncAddress
内部对pFunc
的任何后续使用也应该反映取消引用的值。
另一种解决方案(因为这个是 C++),您可以放弃void *
类C 编码,并将函数作为模板,将函数指针类型作为模板参数:
template <typename FnPtr>
bool CGLManager::GetAnyGLFuncAddress( const char *_cName, FnPtr* _pFunc )
*_pFunc = reinterpret_cast<FnPtr>(wglGetProcAddress( _cName ));
//...
由于类型现在是FnPtr*
,所以无论您传入什么,FnPtr
都会自动成为该类型。没有void *
(除了wglGetProcAddress
的返回值,它是强制转换的)。
【讨论】:
如果我这样做并将原型更改为const char *_cName, void **_pFunc
,我会遇到与强制转换相关的错误(请参阅@Nicol Bolas 的回答,他建议我应该返回指针而不是分配和返回成功/失败)
@Shepard62700FR 如果您使用模板函数而不是void *
,则可能会缓解此错误。查看我的编辑。
这解决了我的问题,问题似乎解决了,不再崩溃,gGL.glBegin
和其余的不再为 NULL,谢谢^^【参考方案3】:
*gl*GetProcAddres
仅用于返回扩展 功能的入口点地址。它不需要返回在操作系统 OpenGL ABI 要求中找到的函数的地址(用于 Windows 的 OpenGL-1.1、用于 Linux Softare Base (LSB) 4 的 OpenGL-1.2、用于 LSB-5 的 OpenGL-2.1)。
一般来说,如果您的程序二进制文件直接链接到 API 库(存根)(与使用在运行时动态加载 API 库(存根)的加载器相比),这非常愚蠢使用*gl*GetProcAddress
检索作为操作系统的 OpenGL ABI 合同一部分的函数,因为这些函数无论如何都可以通过常规 API 库获得。 wglGetProcAddress
与 OpenGL-1.1 函数一起导出,glXGetProcAddress
与所有 OpenGL-1.2 函数一起导出(至少),所以如果您的程序看到 *GetProcAddress
函数,它也会看到其他 OpenGL 函数。
通过 GetProcAddress 加载 所有 OpenGL 符号的唯一原因是,如果您使用 LoadLibrary 加载 opengl32.dll
或使用 dlopen 加载 libGL.so
。
【讨论】:
已经编写了几个 OpenGL 加载器,如果它在每个平台上的工作方式都相同,它会使加载代码变得简单得多。您不必根据所使用的平台在函数声明和函数指针声明之间进行选择;一切都是总是一个函数指针。然后,您的加载器可以循环所有函数并加载它们;它不必有#ifdef WINDOWS
来检查它是否应该加载GL 1.1 指针。
@NicolBolas:我的回答中没有任何内容否认您的说法。事实上,最后一段是说明 gl_LoadGen 中的方法
"一般来说,检索作为操作系统的 OpenGL ABI 合同一部分的函数是非常愚蠢的" 这就是我对你的回答的问题。这根本不是“很傻”。这是完全合理的。事实上,如果你正在编写一个加载器,我会认为加载所有函数指针是非常愚蠢的not。此外,OP 确实加载了opengl32.dll
,这是m_lib
在他对GetProcAddress
的调用中的来源。诚然,他没有显示模块的加载,但它一定是发生了调用才能工作。
@NicolBolas:是的,它的措辞有点偏颇。我在这方面编辑了答案。当谈到 OpenGL 加载器时,恕我直言,这是一把双刃剑:如果系统配置中的某些内容损坏或缺少功能,则不使用加载器会在程序启动时提供直接反馈。拥有一个加载器可以提供用户友好的诊断。此外,加载器是实现更高功能库的唯一方法,这些库只能可选地依赖于 OpenGL 或其他 API(例如我的体积光栅化器库,它可能使用 3D API,但如果没有可用的 API 不会破坏程序)
@NicolBolas 和 @datenwolf :感谢您的解释,为了清楚起见,我没有发布初始化/关闭方法,因为问题不存在,但确实有一个 @987654332 @/m_hLib = dlopen( "libGL.so", RTLD_LAZY );
和 FreeLibrary( m_hLib );
/dlclose( m_hLib );
参与以上是关于手动检索和存储指向 OpenGL 函数的指针时遇到问题的主要内容,如果未能解决你的问题,请参考以下文章