使用 C++ 和 glfw 的 raii 架构

raii architecture using c++ and glfw

我目前正在尝试使用 glfw 用 C++ 编写一个小型引擎来创建 window。我想充分利用 raii 来提出一个异常安全的架构,并使引擎几乎不可能以错误的方式使用。 但我目前正在为一些问题而苦苦挣扎,我觉得我错过了一些重要的东西,所以我在这里问,提前谢谢大家!

假设我有一个名为 glfw_context 的基本 raii 结构,它在 ctor 中调用 glfwInit() 并在 dtor 中调用 glfwTerminate() .它还接受一个 error-callback 并在调用 glfwInit() 之前设置它,但我会省略这些细节,因为我想专注于基本问题。 我还创建了一个 class glfw_window,它在 ctor 中需要一个 const glfw_context&,而重载的 ctor 采用相同类型的参数,但带有 glfw_context&&deleted.

这背后的想法是,当上下文是 rvalue 时,它只会在 ctor 调用期间存在,但 glfwTerminate() 会在所有 windows 调用之前被调用正确销毁(这发生在 glfw_window class 的 dtor 中)。 我为 glfw_window 正确实施了 moves,而 glfw_context 既不能复制也不能移动。但问题从这里开始: 我无法阻止用户创建多个 glfw_context 实例。所以我现在和一个 static 成员一起去了,因为 glfw 没有暴露像 glfwIsInit() 这样的东西,它解决了我框架范围内的这个问题(只有最老的实例会调用 glfwTerminate()) 但这仍然不能防止用户编写这样的代码:

std::vector< glfw_window > windows;
{
    // introduce explicit scope to demonstrate the problem
    glfw_context context{};
    windows.emplace_back( context, ... );
}

在这种情况下,上下文仍然不会超过 window。

有解决这个问题的好方法吗?我不想把它作为一个要求放在文档中(像 "the context must outlive each window" 这样的东西似乎对我来说并不适用)。

我目前的方法是使用 std::shared_pointer< glfw_context > 作为 glfw_windowctor 的参数并将其存储为成员。然而,这仍然没有解决我的问题,因为我仍然可以 make_shared< glfw_context >() 不同的上下文并将它们传递给不同的 windows。并且由于只有第一个分配的实例会调用 glfwTerminate() 我仍然可以引发上下文在所有 windows.

之前被破坏的情况

那么解决这类问题的正确方法是什么?我可以构建一个无论用户如何尝试(错误)使用它都能在这里正常工作的漂亮架构吗? 我的其他一些想法包括 glfw_context 中的 private ctorstatic 工厂方法与 shared_pointer 方法相结合,但这感觉很像 singleton 和我怀疑这是处理问题的最佳方式。

您可能会使用单例的变体:

class glfw_context
{
    glfw_context() {/*Your impl*/}

    glfw_context(const glfw_context&) = delete;
    glfw_context& operator=(const glfw_context&) = delete;
public:
    friend std::shared_ptr<glfw_context> CreateContext()
    {
        static std::weak_ptr<glfw_context> instance;

        auto res = instance.lock();
        if (res == nullptr) {
            res = std::make_shared<glfw_context>();
            instance = res;
        }
        return res;
    }

    /* Your impl */
};

那么只要你的实例至少有一个"reference",CreateContext() returns它,否则它会创建一个新的。
不可能有 2 个不同的 glfw_context

实例