捕获构造函数引发的异常似乎是不可能的

时间:2016-05-12 16:19:05

标签: c++ exception

我正在使用一个库,该库需要执行某些功能来初始化库,并且需要执行其他功能来执行"清理"。具体来说,库是OpenGL(使用GLFW和GLEW),初始化函数包括glfwInit()glewInit(),清理函数是glfwTerminate()。确切的库应该无关紧要。

本着RAII的精神,我创建了一个LibraryGuard类,其构造函数初始化了库,其析构函数调用了必要的清理函数。

当然,库可能无法初始化。例如,可能没有适当的硬件支持,可能缺少动态库等。为了处理这些情况,我已经定义了LibraryGuard的构造函数,以便在无法初始化库时抛出异常。 / p>

问题是我不知道如何实际捕获此异常。显而易见的

try {
    LibraryGuard lg;
}
catch () {
    // exit gracefully
}

将无效,因为如果LibraryGuard成功创建try块,则lg的析构函数被调用,这意味着库清理功能被称为。

我能想到的唯一其他解决方案是:1)不捕捉异常;或者2)将我的整个main函数括在try块中。这两种选择都不是特别适合。

10 个答案:

答案 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)

我认为,问题实际上更为通用,而基本问题是关于例外政策。什么构成您应用中的例外?要回答这个问题,您需要回答以下问题:

  1. 什么是不可恢复的,不变的查杀错误?
  2. 您可以从哪个级别恢复?
  3. 现在,您的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),则不会抛出任何异常,因此您将获得的唯一例外是您自己提出的例外,所以或者您希望捕获这些异常在主要的,或者你不想要那些例外,

解决方案是:

  1. 如果您不想处理异常,请不要抛出异常,因为那些类似C的库本身不会抛出异常。
  2. 您想要抛出异常,然后您必须确保您的对象是异常安全的
  3. 这是最简单的解决方案(如果您需要例外)

    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{
    
        }
    }