我正在使用一个库,该库需要执行某些功能来初始化库,并且需要执行其他功能来执行"清理"。具体来说,库是OpenGL(使用GLFW和GLEW),初始化函数包括glfwInit()
和glewInit()
,清理函数是glfwTerminate()
。确切的库应该无关紧要。
本着RAII的精神,我创建了一个LibraryGuard
类,其构造函数初始化了库,其析构函数调用了必要的清理函数。
当然,库可能无法初始化。例如,可能没有适当的硬件支持,可能缺少动态库等。为了处理这些情况,我已经定义了LibraryGuard
的构造函数,以便在无法初始化库时抛出异常。 / p>
问题是我不知道如何实际捕获此异常。显而易见的
try {
LibraryGuard lg;
}
catch () {
// exit gracefully
}
将无效,因为如果LibraryGuard
成功创建try
块,则lg
的析构函数被调用,这意味着库清理功能被称为。
我能想到的唯一其他解决方案是:1)不捕捉异常;或者2)将我的整个main
函数括在try
块中。这两种选择都不是特别适合。
答案 0 :(得分:2)
所有指向正确的解决方案都在try{}catch
的构造函数中使用LibraryGuard
,并确保库守护实际上处理库初始化失败 - 这是什么你为它发明了它!
因此,当您在构造函数中检测到初始化失败时,请执行您需要执行的操作; 然后抛出异常,让main
知道事情发生了变化。
答案 1 :(得分:2)
如果不对构造函数执行任何操作并要求库初始化为特定方法呢?
像
这样的东西int main ()
{
LibraryGuard lg; // constructor do nothing
// ....
try
{
lg.initialize(); // library initialization
}
catch (...)
{
// case of initialization failure
}
p.s:抱歉我的英语不好。
答案 2 :(得分:2)
我认为,问题实际上更为通用,而基本问题是关于例外政策。什么构成您应用中的例外?要回答这个问题,您需要回答以下问题:
现在,您的OpenGL初始化失败。我不是你的应用程序,但我想,对于任何首先需要OpenGL的应用程序来说,加载OpenGL的失败应该是不可恢复的。似乎很难想象你可以从中恢复任何级别?你会用 恢复什么?
我会说,(对你的应用程序知之甚少)你最好的行动方法是在主要内容中抓取const std::exception& e
,打印e.what()
和std::terminate
。这至少是我的风格。
答案 3 :(得分:1)
你的解决方案是完全正确的,但有点缺失。
void do_all_my_stuff_with_library() {
// whatever
}
int main() {
try {
LibraryGuard lg;
do_all_my_stuff_with_library();
}
catch () {
// exit gracefully
}
return 0;
}
答案 4 :(得分:1)
清理功能需要从inizialization函数中获得的东西吗?
如果不是这样,你可以将你的班级分为两类:
1)一个类LibraryIn,它在构造函数
中初始化2)一个类LibratyOut,其析构函数清理库。
所以
int main ()
{
LibraryOut lo; // constructor do nothing
// ...
try
{
LibraryIn li; // constructor initialize library
// destructor of li do nothing
}
catch (...)
{
// in case of library initialization failure
}
// ...
return EXIT_SUCCESS;
// destructor of lo clean-up the library
}
答案 5 :(得分:0)
您可以移动 - 启用课程并使用工厂功能:
LibraryGuard init_library()
{
try {
return LibraryGuard{};
} catch(/*...*/) {
//Log stuff
//Terminate application
}
}
//Usage
auto lg = init_library();
答案 6 :(得分:0)
某种指针:
std::unique_ptr<LibraryGuard> lg;
try {
lg.reset(new LibraryGuard());
}
catch () {
// exit gracefully
lg.release(); // don't know if this is necessary
}
答案 7 :(得分:0)
如果您可以使用C ++ 11,则可以使用std::unique_ptr
,所以
int main ()
{
std::unique_ptr<LibraryGuard> up;
try
{
up.reset(new LibraryGuard());
}
catch (...)
{
// in case of library initialization failure
}
return 0;
// destructor of LibraryGuard()
}
答案 8 :(得分:0)
您可以执行以下操作,并仍然使用std::unique_ptr:
遵守RAII#include <iostream>
#include <memory>
class LoggingLibrary
{
public:
LoggingLibrary(int trigger=0) // for demonstration purposes
{
if ( trigger > 0)
throw "Error";
}
};
using namespace std;
int main()
{
unique_ptr<LoggingLibrary> logLib;
try
{
logLib = make_unique<LoggingLibrary>(0);
}
catch(const char *msg)
{
cout << msg << " -- Didn't initialize";
}
if ( logLib )
{
cout << "Hey I'm ok"; // no exception thrown, so you can do work here
}
}
Live Example (no exception thrown)
Live Example (exception thrown)
当std::unique_ptr
超出范围时,LoggingLibrary
将调用logLib
的析构函数。如果抛出异常,则输入catch
块。
但是如果你看一下if (logLib)
- 这会测试智能指针是否有关联的对象,如果抛出异常就没有,std::unique_ptr
有一个{{} 3}}处理检查这种情况。
答案 9 :(得分:-1)
如果您正在使用类似C的库(GLEW,GLFW),则不会抛出任何异常,因此您将获得的唯一例外是您自己提出的例外,所以或者您希望捕获这些异常在主要的,或者你不想要那些例外,
解决方案是:
这是最简单的解决方案(如果您需要例外)
RelativeSource
这样,如果调用析构函数,则不再存在问题(这是非常RAII方式)。基本上你想创建一个对象,如果对象被完全初始化,那么当你退出&#34;尝试&#34;时会调用它的析构函数。阻止,如果对象被部分初始化,那么class LibraryGuard{
bool initializedGLFW;
//other flags
void error(const char* message)}{
shutDown();
throw std::exception(message);
}
public:
LibraryGuard(){
initializedGLFW = false;
if(glfwInit()!=GL_TRUE)
error("GLFW not initialized");
initializedGLFW = true;
//other libraries
}
~LibraryGuard(){
shutDown();
}
void shutDown(){
//other libraries (order reversed, sometimes order matter)
if(initializedGLFW){
initializedGLFW = false;
glfwTerminate();
}
}
}
函数无论如何都要去初始化它。
error
编辑:
似乎你想做那样的事情
int main(){
try{
LibraryGuard lg;
}catch( std::exception e){
}
}
无论你想保持图书馆存活的原因是什么,你仍然可以做以下事情(如果这有任何意义),不要我不是说这是一个好主意,似乎仍然是你想要的行为
//Your new main so you can keep the "lg" alive around ^^
int runApp(){
LibraryGuard lg;
}
int main(){
try{
runApp();
}catch{
}
}