gpt4 book ai didi

c++ - 当仿函数不是一个选项时,我如何在 C++ 中编写带有自定义函数调用的模板化 RAII 包装器?

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:22:43 25 4
gpt4 key购买 nike

我目前正在使用标准¹ C++ 17 为 OpenGl 开发 RAII 系统,同时大量使用模板。现在,我正在处理的系统部分是通过一个通用模板绑定(bind)和取消绑定(bind)各种 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>;

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

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

为了解决这个问题,我将模板参数指针设为 void,然后将它们转换为类主体中更有用的类型,其中函数的地址和参数包都可用。由于我对函数指针的理解与常规指针没有什么不同,除了它们的地址指向所讨论函数的机器代码之外,我认为使用这种方法除了稍微损害类型安全之外不会有任何问题²。

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

我不能使用仿函数,因为我希望我的类的用户能够通过我的 using 声明在不知道它是模板类的情况下相处,但是仿函数需要它的每个实例化来传递一个仿函数类型的实例。

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

1:这意味着我强烈反对任何可能有效™ 的未定义行为,因为可移植性对我来说很重要。2:虽然强制转换本身是不安全的,但如果在实例化模板和强制转换时出现任何问题,编译器应该停止编译并报错。因为我打算让我的代码的用户仅通过 using 声明来使用它,所以这种可能神秘的错误是一个非常小的问题。

最佳答案

由于您使用的是 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>;

关于c++ - 当仿函数不是一个选项时,我如何在 C++ 中编写带有自定义函数调用的模板化 RAII 包装器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57839204/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com