使用c++;和glfw 我目前正试图在C++中编写一个小引擎,用于窗口创建。我想充分利用raii来设计一个异常安全的架构,使引擎实际上不可能以错误的方式使用。 但是我现在遇到了一些问题,我觉得我错过了一些重要的东西,所以我在这里问,提前谢谢大家
假设我有一个名为使用c++;和glfw 我目前正试图在C++中编写一个小引擎,用于窗口创建。我想充分利用raii来设计一个异常安全的架构,使引擎实际上不可能以错误的方式使用。 但是我现在遇到了一些问题,我觉得我错过了一些重要的东西,所以我在这里问,提前谢谢大家,c++,glfw,raii,exception-safety,c++17,C++,Glfw,Raii,Exception Safety,C++17,假设我有一个名为glfw_context的基本raii-struct,它在ctor中调用glfwInit(),在dtor中调用glfwetermint()。它还接受一个错误回调,并在调用glfwInit()之前对其进行设置,但我将省略这些细节,因为我想把重点放在基本问题上。 我还创建了一个类glfw_窗口,当重载的ctor采用相同类型的参数但带有glfw_上下文&&被删除时,它需要ctor中的常量glfw_上下文& 这背后的想法是,当上下文是一个rvalue时,它只会在ctor调用期间存在,但是
glfw_context
的基本raii
-struct,它在ctor
中调用glfwInit()
,在dtor
中调用glfwetermint()
。它还接受一个错误回调
,并在调用glfwInit()
之前对其进行设置,但我将省略这些细节,因为我想把重点放在基本问题上。
我还创建了一个类glfw_窗口
,当重载的ctor
采用相同类型的参数但带有glfw_上下文&&
被删除时,它需要ctor
中的常量glfw_上下文&
这背后的想法是,当上下文是一个rvalue
时,它只会在ctor
调用期间存在,但是glfwderminate()
会在所有窗口被正确销毁之前被调用(这发生在glfw\u窗口
类的dtor
中)。
我为glfw_窗口
正确执行了moves
,而glfw_上下文
既不能复制也不能移动。但问题从这里开始:
我无法阻止用户创建多个glfw_context
实例。因此,我现在使用了一个静态
成员,因为它没有公开这样的东西,在我的框架范围内解决了这个问题(只有最旧的实例会调用glfweterminate()
),但这仍然不能保护用户编写这样的代码:
std::vector< glfw_window > windows;
{
// introduce explicit scope to demonstrate the problem
glfw_context context{};
windows.emplace_back( context, ... );
}
std::vectorwindows;
{
//引入显式范围来演示问题
glfw_上下文{};
windows.emplace_back(上下文,…);
}
在这种情况下,上下文仍然不会超过窗口
有什么好办法解决这个问题吗?我不想把它作为一项要求放在文档中(像“上下文必须比每个窗口都长”这样的东西似乎对我来说并不合适)
我目前的方法是使用std::shared_指针
作为glfw_窗口的ctor
的参数,并将其存储为成员。然而,这仍然不能解决我的问题,因为我仍然可以使_共享()
不同的上下文,并将它们传递到不同的窗口。由于只有第一个分配的实例会调用glfwTerminate()
,因此我仍然可以证明上下文在所有窗口之前被破坏的情况
那么,解决这类问题的正确方法是什么?无论用户如何尝试(错误地)使用它,我是否可以在这里构建一个良好的体系结构并正常工作?
我的一些其他想法包括glfw_context
中的私有ctor
和静态
工厂方法与共享指针
-方法相结合,但这感觉很像单例
,我怀疑这是处理问题的最佳方式。您可能会使用单例的一种变体:
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 */
};
类glfw_上下文
{
glfw_context(){/*您的impl*/}
glfw_上下文(const glfw_context&)=删除;
glfw_上下文&运算符=(常量glfw_上下文&)=删除;
公众:
friend std::shared_ptr CreateContext()
{
静态std::弱_ptr实例;
auto res=instance.lock();
如果(res==nullptr){
res=std::使_共享();
实例=res;
}
返回res;
}
/*你的建议*/
};
然后,只要至少有一个“引用”到您的实例,CreateContext()
就会返回它,否则它会创建一个新的引用。
不可能有两个不同的glfw\u context
实例使构造函数私有,并创建一个返回现有共享\u ptr的工厂或创建一个工厂。但是如果该共享\u ptr
是静态的,它将如何被删除?当然,我可以在对glfw_context::get_context()
的第一次调用中创建它,并将其存储在glfw_窗口
实例中,这样glfw_context
将比它更长寿,但该静态成员仍然会持有一个引用,对吗?那它的一生呢?谢谢@jarod42!能否请您澄清,将实例
作为类的静态
成员或CreateContext()的静态
成员之间是否有区别?我是否可以将实现和静态实例
隐藏在.cpp/.cc文件中,或者这会产生一些问题?谢谢你的解决方案-虽然我没有足够的代表来支持它。你可以将CreateContext
的定义放在cpp文件中(但它仍然应该是friend
(或静态方法),以便能够调用构造函数)。在static
成员和static
内部方法之间有细微的区别。例如,当它第一次初始化时。对于您的情况,我希望这两种方法都是可行的(您不会在main
之前调用CreateContext
)。对,这些调用只会发生在main中,所以这种方法会起作用。关于代码的最后一个问题是:CreateContext()
是否打算成为private
而不是static
?它应该叫什么名字?我想我最常用的方法是使用名为CreateContext()
的public static
成员函数,所有细节都隐藏在cc文件中。当然,一个免费的friend
功能也会像你建议的那样工作,如果你能详细说明与mayb相比的后果,我将非常感激