如何完美包装函数以追踪OpenGL调用?遇void返回值实现问题
解决GCC7中OpenGL调用包装宏的void返回函数问题
你的问题根源在于GCC7对C标准的严格执行:当lambda返回类型为void时,C17之前的标准不允许在return语句后跟随void类型的表达式——而你的原始宏里,不管被调用的OpenGL函数返回什么,lambda都写了return NAME ARGUMENTS;,这在处理void返回的OpenGL函数时就触发了编译错误。
下面给你一个简洁且标准的解决方案,既兼容GCC7,又能完美处理所有返回类型的OpenGL调用,还解决了完美转发的问题:
步骤1:重构debugOpengl为单一模板函数
用C++17的if constexpr特性(GCC7已支持),把原来的两个重载合并成一个模板函数,自动处理void和非void返回的情况:
#include <utility> #include <type_traits> #include <iostream> template<typename Func> decltype(auto) debugOpengl(char const* name, Func&& exec) { GLenum err; if constexpr (std::is_void_v<decltype(std::forward<Func>(exec)())>) { // 处理返回void的函数调用 std::forward<Func>(exec)(); while ((err = glGetError()) != GL_NO_ERROR) { std::cerr << "OpenGL Error: " << name << " " << err << std::endl; } } else { // 处理带返回值的函数调用 auto ret = std::forward<Func>(exec)(); while ((err = glGetError()) != GL_NO_ERROR) { std::cerr << "OpenGL Error: " << name << " " << err << std::endl; } return ret; } }
这个版本用std::forward完美转发可调用对象,避免了std::function的类型擦除开销,同时自然解决了字面量参数的转发问题。
步骤2:修改宏以适配返回类型
调整宏定义,让lambda根据被调用函数的返回类型,自动选择是否使用return语句:
#define TOSTRING(x) #x #define EXEC_OPENGL(NAME, ARGUMENTS) \ debugOpengl(TOSTRING(NAME), \ []() -> decltype(auto) { \ if constexpr (!std::is_void_v<decltype(NAME ARGUMENTS)>) { \ return NAME ARGUMENTS; \ } else { \ NAME ARGUMENTS; \ } \ })
这里的if constexpr会在编译时直接剔除不符合当前返回类型的分支:
- 当OpenGL函数返回非
void类型时,lambda执行return NAME ARGUMENTS;,返回对应的值 - 当OpenGL函数返回
void时,lambda直接执行函数调用,不写return,完全符合标准要求
编译注意事项
编译时需要启用C++17支持,给GCC7加上编译参数:-std=c++17或者-std=gnu++17。
这样修改后,你的代码不仅能在GCC7中正常编译运行,还能正确追踪所有OpenGL调用的错误,同时完美处理各种返回类型和参数类型。
内容的提问来源于stack exchange,提问作者Maël Nison
相关产品推荐
相关产品推荐

