在不能使用函子的情况下,如何在C ++中编写带有自定义函数调用的模板化RAII包装器?

joh*_*dav 5 c++ opengl templates function-pointers c++17

我目前正在使用标准¹C ++ 17中的OpenGl开发RAII系统,同时大量使用模板。现在,我正在研究的系统部分是通过一个通用模板来绑定和解除绑定各种OpenGl对象,然后使用声明为每种类型创建简单的别名。以下是我的头文件的相关摘录,演示了一般技术:

template<typename T, void *bind, void *unbind, typename ... Args>
class RaiiGlBinding{
public:
    explicit RaiiGlBinding(const T &t, Args... args) : m_unbindArgs(std::make_tuple(t, args...)) { bind(t, args...); }
    ~RaiiGlBinding() { if(m_isDestructable) std::apply(unbind_typed, m_unbindArgs); }

private:
    static constexpr auto bind_Vaotyped = static_cast<void (*)(T, Args...)>(bind);
    static constexpr auto unbind_typed = static_cast<void (*)(T, Args...)>(unbind);
    bool m_isDestructable = true;
    std::tuple<T, Args...> m_unbindArgs;

};
Vao
namespace glraiidetail{
    inline void bindBuffer(GLuint buffer, GLenum target) { glBindBuffer(target, buffer); }
    inline void unbindBUffer(GLuint buffer, GLenum target) { glBindBuffer(target, 0); }
}

using RaiiBufferBinding = RaiiGlBinding<GLuint, &glraiidetail::bindBuffer, &glraiidetail::unbindBuffer>;
Run Code Online (Sandbox Code Playgroud)

当我第一次尝试此类时,我在template<>声明(例如template<typename ... Args, void (*)(Args...)>)中使用了非空指针,但这引起了问题,因为1)手动指定更困难Args,2)CLion告诉我可变参数必须为最后。

然后,我将可变参数的论点移到最后,解决了两个问题。但是,这阻止了函数参数访问参数包以进行解包。

为了解决此问题,我将模板参数指针设置为void,然后将其强制转换为类主体中更有用的类型,在该类主体中,函数的地址和参数包均可用。由于我对函数指针的理解与常规指针没有什么不同,除了它们的地址指向所讨论函数的机器代码之外,我认为使用此方法除了轻度损害类型安全性²之外没有其他问题。

不幸的是,当我的编译器不允许我将函数指针void*显式转换为,或以其他方式转换时,这被证明是错误的。经过一些研究,我得出的结论是,我以前对void指针的明显解决方案并不是很多解决方案,因为void*在C ++中将函数指针强制转换为实际上是未定义的行为。

我不能使用函子,因为我希望类的用户能够通过我的using声明甚至不知道它是一个模板类,但函子将需要它的每个实例化来传递的实例。函子类型。

因此,我问非常聪明的堆栈溢出人员:如何解决此问题?

1:这意味着我强烈反对任何可能会起作用的不确定行为,因为可移植性对我很重要。2:尽管强制类型转换本身是不安全的,但是在实例化模板(因此强制类型转换)时,如果出现任何问题,编译器应停止生成错误的编译。因为我打算让代码的用户仅通过using声明使用它,所以这种可能的密码错误是一个非常小的问题。

Hen*_*nke 6

由于您使用的是C ++ 17,因此只能使用auto模板参数。您可以在类主体中添加静态断言,以确保参数实际上是函数指针。

template <typename T, auto bind, auto unbind, typename... Args>
class RaiiGlBinding {
public:
    explicit RaiiGlBinding(const T &t, Args... args)
        : m_unbindArgs(std::make_tuple(t, args...)) {
        bind(t, args...);
    }
    ~RaiiGlBinding() {
        if (m_isDestructable)
            std::apply(unbind, m_unbindArgs);
    }

private:
    bool m_isDestructable = true;
    std::tuple<T, Args...> m_unbindArgs;
};

namespace glraiidetail {
inline void bindBuffer(GLuint buffer, GLenum target) {
    glBindBuffer(target, buffer);
}
inline void unbindBuffer(GLuint buffer, GLenum target) {
    glBindBuffer(target, 0);
}
} // namespace glraiidetail

using RaiiBufferBinding = RaiiGlBinding<GLuint, &glraiidetail::bindBuffer,
                                        &glraiidetail::unbindBuffer>;
Run Code Online (Sandbox Code Playgroud)