大多数OpenGL API调用都不返回值。使用C ++ 11完美转发,可以很容易地编写错误检查包装函数模板和可变参数宏,如下所示:
template <typename F, typename ...Args>
void call(
const char *text,
int line,
const char *file,
F && f, Args &&... args
)
{
std::forward<F>(f)(std::forward<Args>(args)...);
auto err = glGetError();
if (err != 0) _throw_error(text, err, line, file);
}
#define GL(fn, ...) call(#fn, __LINE__, __FILE__, gl##fn, __VA_ARGS__)
这有效,并允许调用如下:
GL(Viewport, 0, 0, 1920, 1080);
但是,某些OpenGL函数确实返回一个值,因此在检查错误之前,包装函数必须将返回值存储在临时变量中。具有返回值的调用的包装函数模板可以这样写:
template <typename F, typename ...Args>
auto call(
const char *text,
int line,
const char *file,
F && f, Args &&... args
)
{
auto && res = std::forward<F>(f)(std::forward<Args>(args)...);
auto err = glGetError();
if (err != 0) _throw_error(text, err, line, file);
return res;
}
(可以使用相同的宏来调用此表单。)
问题在于无法定义上述函数模板的两个,因为对于所有具有void
返回类型的API调用,两个模板都将匹配,从而导致“模糊调用重载函数“错误(Visual C ++)。
当然,可以为两种形式定义单独的宏,但这并不优雅。 GL
是包装器宏名称的自然,明显和正确的选择,任何其他宏(例如GLR
)都会显得难看并且难以记住。
答案 0 :(得分:4)
特别是在发布版本中,您不希望在每次调用后调用glGetError
。它对性能不利,glGetError
无论如何都不会返回描述性错误(GL_INVALID_OPERATION
用于很多事情。)
相反,要进行调试,请创建debug context并使用glDebugMessageCallback
注册回调,以打印出错误消息。这是一个相对较新的OpenGL功能,但根据我的经验提供了更好的消息。
void debug_callback(GLenum source, GLenum type, GLuint id,
GLenum severity, GLsizei length, const GLchar* message, const void* userParam) {
printf("%s", message);
}
glDebugMessageCallback(&debug_callback, nullptr);
答案 1 :(得分:1)
上述问题的解决方案并不是很困难,但是我很惊讶地发现Visual C ++不会接受(意味着它被内部编译器错误所阻塞)我使用{{3 },使用gcc。
当然需要Coliru,而C ++ 11支持非常方便的SFINAE。与gcc一起使用的解决方案,但与Visual Studio 2015没有关系,是:
template <typename F, typename ...Args,
typename R = std::enable_if_t<!std::is_void<std::result_of_t<F(Args...)>>::value, std::result_of_t<F(Args...)>>>
>
R call(const char *text, int line, const char *file, F && f, Args &&...args)
{...}
(类似于void函数,改为使用std:is_void<...>::value
)。
经过一番搜索后,我想出了以下编译代码:
template <typename F, typename ...Args>
auto call(
std::enable_if_t<!std::is_void<std::result_of_t<F(Args...)>>::value, const char> *text,
int line,
const char *file,
F && f, Args &&... args
)
{
auto && res = std::forward<F>(f)(std::forward<Args>(args)...);
auto err = glGetError();
if (err != 0) _throw_error(text, err, line, file);
return res;
}
和
template <typename F, typename ...Args>
void call(
std::enable_if_t<std::is_void<std::result_of_t<F(Args...)>>::value, const char> *text,
int line,
const char *file,
F && f, Args &&... args
)
{
std::forward<F>(f)(std::forward<Args>(args)...);
auto err = glGetError();
if (err != 0) _throw_error(text, err, line, file);
}
这个解决方案唯一值得注意的是它使用SFINAE的方式。 std::enable_if_t<>
不仅用于消除不需要的特化,而且同时声明宏传入的text
参数的类型。因此,不是通过一个额外的,未使用的参数和一个默认值(我试过但由于某种原因导致了#34;内部编译器错误&#34;在Visual Studio下)进行SFINAE,我只是在其中一个上使用它无论如何我还需要三个额外的参数。