用于OpenGL调用的c ++ 11包装器

时间:2015-12-05 19:04:57

标签: c++ c++11 opengl

大多数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)都会显得难看并且难以记住。

2 个答案:

答案 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,我只是在其中一个上使用它无论如何我还需要三个额外的参数。