2

I am working with a library that requires certain functions to be executed to initialize the library and requires that other functions be executed to perform "clean up". To be concrete, the library is OpenGL (with GLFW and GLEW), the initialization functions include glfwInit() and glewInit(), and the cleanup function is glfwTerminate(). The exact library should not matter though.

In the spirit of RAII, I created a LibraryGuard class whose constructor initializes the library and whose destructor calls the necessary cleanup functions.

The library, of course, may fail to initialize. For example, there may not be proper hardware support, dynamic libraries may be missing, etc. To handle these cases, I have defined LibraryGuard's constructor to throw an exception if the library cannot be initialized.

The problem is that I have no idea how to actually catch this exception. The obvious

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

will not work because LibraryGuard's destructor is called at the end of the try block if lg is successfully created, which means that the library clean-up functions are called.

The only other solutions I can think of are to either 1) not catch the exception; or 2) enclose my entire main function inside a try block. Neither option is particularly palatable.

Alessandro Power
  • 2,395
  • 2
  • 19
  • 39
  • I am not sure I understood what you mean but if an exception is thrown from the constructor the destructor is not called. The constructor did not finish (by a `return` or `}`) so the object is not considered "alive" – george_ptr May 12 '16 at 16:23
  • 1
    I don't really understand your problem: http://stackoverflow.com/a/10212864/390913 – perreal May 12 '16 at 16:23
  • check my answer, someone have downvoted it, but it should work. – CoffeDeveloper May 12 '16 at 16:36
  • 1
    If the only point where you could imagine a try/catch block is around all of `main`, then you might as well just not catch the exception and let it terminate the program. That's what you were going to do anyway. – Kerrek SB May 12 '16 at 16:44
  • 1
    @KerrekSB, disagree. You should catch exception in main, print the message within it, and `abort`. This is what I would do. – SergeyA May 12 '16 at 16:51
  • The question is not clear, if the problem is that functions for shutting down various libaries are not called or are called when not needed, my answer is enough (still guessing why so many downvotes, it should work if there are no more typos), if the problem is another, then edit the question :) – CoffeDeveloper May 12 '16 at 16:51
  • @DarioOO To clarify: the problem is that if `LibraryGuard`'s ctor throws an exception, that exception should be caught, which means that the creation of an instance of `LibraryGuard` must be placed inside a `try` block. However, at the end of the `try` block, the newly created object goes out of scope, which means that its dtor is called, "uninitializing" the library, even if nothing went wrong with the library initialization. Hope that clears things up. – Alessandro Power May 12 '16 at 16:58
  • I already answered, In my code snippet, the library get correctly un-initialized, regardless if the destructor was called or an exception raised, in that way you can place your object inside the "try" block :) – CoffeDeveloper May 12 '16 at 16:59
  • look carefully at what the method `error` in my class does :) – CoffeDeveloper May 12 '16 at 17:01
  • If you place everything inside the `try` block then everything goes out of scope, so no reason why you want to keep the `LibraryGuard` object alive and around (also if initialization fails, there is no reason to keep it around. You should edit your question to reflect that precisation. – CoffeDeveloper May 12 '16 at 17:03
  • 2
    @AlessandroPower -- You declared a local variable within a `{ }` block, so you need to work with it inside that block. It doesn't matter if it's a `try / catch` block, an `if` block, a function block, etc. Just because it happens to be `try / catch` doesn't change the scoping rules of C++. – PaulMcKenzie May 12 '16 at 17:03
  • @PaulMcKenzie I know, I'm just hoping that there's a way of avoiding the need to stick the entirety of `main` inside that `try` block. It's increasingly looking like the answer is "no", unless I have library initialization take place in a method rather than the ctor. – Alessandro Power May 12 '16 at 17:08
  • @AlessandroPower, then why don't you use a pointer/new? – perreal May 12 '16 at 17:14
  • @AlessandroPower I edited the answer again, you seems you just want a "onion" layer of objects, that does not make any sense to me, don't do that, if you really want "that" the only way to do that is by nesting "try/catch" blocks... That's how C++ is done. :/ – CoffeDeveloper May 12 '16 at 17:16

10 Answers10

2

All points to the right solution being having this try{}catch in your LibraryGuard's constructor, and making sure the library guard actually deals with failing library initialization – that's what you've invented it for!

So, do whatever you need to do when you detect initialization failed in your constructor; then throw the exception to let you main know things went downhill.

Marcus Müller
  • 34,677
  • 4
  • 53
  • 94
  • 1
    Maybe I'm missing something, but I don't see how this helps. According to http://www.gotw.ca/gotw/066.htm, an exception thrown in a constructor, even if caught, will still cause the constructor to throw. So I still have the problem of figuring out how to catch the constructor's potential exception. – Alessandro Power May 12 '16 at 16:34
  • @AlessandroPower this is related to `funcntion try-catch block`. You can have generic one inside constructor body and it will work fine. – Revolver_Ocelot May 12 '16 at 16:45
  • @AlessandroPower the revolver ocelot is right – the correct way to let a constructing party know the constructor failed is to make the constructor throw an exception. However, if you've got something that takes care of the state of your library, that instance should be in charge of cleaning up, even if the constructor fails. – Marcus Müller May 12 '16 at 22:14
2

What about doing nothing with the constructor and demand the library initialization to a specific method?

Something like

int main ()
 {
   LibraryGuard  lg;  // constructor do nothing

   // ....

   try
    {
      lg.initialize(); // library initialization
    }
   catch (...)
    {
      // case of initialization failure
    }

p.s.: sorry for my bad English.

max66
  • 65,235
  • 10
  • 71
  • 111
2

I think, the question is actually more generic, and the underlying question is about exception policy. What consitutes an exception in your app? To answer this, you need an answer to following questions:

  1. What are unrecoverable, invariant-killing errors?
  2. At which level you can recover from them?

Now, your OpenGL initialiation failure. I am not your app, but I would imagine, the failure to load OpenGL should be pretty unrecoverable for any application which needed OpenGL in first place. And it seem hard to imagine any level you can recover from that? What would you recover with?

I'd say, (knowing little if anything about your app) your best course of actions is to catch const std::exception& e in main, print e.what() and std::terminate. This is my style, at least.

SergeyA
  • 61,605
  • 5
  • 78
  • 137
1

Your solution is exactly right, but there's a bit missing.

void do_all_my_stuff_with_library() {
    // whatever
}

int main() {
    try {
        LibraryGuard lg;
        do_all_my_stuff_with_library();
    }
    catch () {
        // exit gracefully
    }
    return 0;
}
Pete Becker
  • 74,985
  • 8
  • 76
  • 165
1

The clean-up functions need something obtained from the inizialization function?

If it isn't so, you can split your class in 2 classes:

1) a class LibraryIn, that initialize in constructor

2) a class LibratyOut, whose destructor clean-up the library.

So

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
 }
max66
  • 65,235
  • 10
  • 71
  • 111
0

You can move-enable your class and use factory function:

LibraryGuard init_library()
{
    try {
        return LibraryGuard{};
    } catch(/*...*/) {
        //Log stuff
        //Terminate application
    }
}

//Usage
auto lg = init_library();
Revolver_Ocelot
  • 8,609
  • 3
  • 30
  • 48
  • To be honest: that's functionally the same as my answer, but less elegant. Why not just put the catch into your ctor? – Marcus Müller May 12 '16 at 16:27
  • @MarcusMüller The only reason I could see, is that it migh be sometimes preferable to allow exception to propagate — library initialization failure is not always fatal, or there might be not information to handle it there. – Revolver_Ocelot May 12 '16 at 16:29
0

Some sort of pointer:

std::unique_ptr<LibraryGuard> lg;
try {
    lg.reset(new LibraryGuard());
}
catch () {
    // exit gracefully
    lg.release(); // don't know if this is necessary
}
perreal
  • 94,503
  • 21
  • 155
  • 181
  • Doesn't this defeat the purpose of RAII? Now I have to explicitly `delete lg` instead of having the compiler do it for me. – Alessandro Power May 12 '16 at 17:20
  • 1
    you have a point but I said some sort of pointer, smart or raw – perreal May 12 '16 at 17:22
  • @AlessandroPower this answer is correct, among others (like mine ^^), basically you are defeating the purpose of RAII by design/requisites, you are discarding all RAII answers by telling "the problem is that those are RAII".. u.u you don't want the destructor called, however that's exactly what RAII is. RAII = destructor called (wich however you don't want..) .. perreal just used a pointer but in the end its code is perfectly equivalent to code of other answers. – CoffeDeveloper May 12 '16 at 17:23
  • Unless there are problems I am not seeing, I think this is the best solution. @perreal If you update your answer to use smart pointers I will accept it. – Alessandro Power May 12 '16 at 17:31
  • `nullptr` is not in `std` namespace but otherwise I think this solution is fine. – Alessandro Power May 12 '16 at 17:41
0

If you can use C++11, you can use std::unique_ptr, so

int main ()
 {
   std::unique_ptr<LibraryGuard>  up;

   try
    {
      up.reset(new LibraryGuard());
    }
   catch (...)
    {
      // in case of library initialization failure
    }

   return 0;

   // destructor of LibraryGuard()
 }
max66
  • 65,235
  • 10
  • 71
  • 111
0

You can do the following, and still adhere to RAII by using std::unique_ptr:

#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)

The std::unique_ptr will call the destructor of LoggingLibrary when logLib goes out of scope. If there was an exception thrown, the catch block is entered.

But if you take a look at the if (logLib) -- this tests if the smart pointer has an associated object, and there isn't one if an exception was thrown The std::unique_ptr has an operator bool() that handles checking this scenario.

PaulMcKenzie
  • 34,698
  • 4
  • 24
  • 45
  • This is a good solution but @perreal offered the "smart pointer" solution first, so I accepted his answer. – Alessandro Power May 12 '16 at 17:42
  • The solution I gave has an additional step that may be helpful to you. Look at the `if` block after the `catch`. From your original post, it seemed that you wanted to have a "local" in the try block, and then work with it outside of try / catch if the constructor didn't fail -- well... – PaulMcKenzie May 12 '16 at 17:44
-1

If you are using C-like libraries (GLEW, GLFW) then NO exception is thrown, so the only exceptions you would get are the exceptions you raise yourself, so or you want those exceptions caught in main, or you don't want that exceptions,

the solution is either:

  1. Do not throw exceptions if you don't want to handle them, because those C-like libraries will not throw exceptions on their own.
  2. You want to throw exceptions, then you have to make sure your object is exception safe

This is the simplest solution (if you want exceptions)

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();
           }
     }

}

This way there are no longer problems if destructor is called (it is very RAII way). Basically you want to create an object, if the object is fully initialized then its destructor is called when you quit the "try" block, if the object is partially initialized then the error function wil anyway de-initialize it.

int main(){  

    try{
       LibraryGuard lg;
    }catch( std::exception e){

    }


}

EDIT:

Seems you want to do something like that

//Your new main so you can keep the "lg" alive around ^^
int runApp(){

    LibraryGuard lg;

}

int main(){
    try{
            runApp();
    }catch{

    }
}

Whatever the reason for wich you want to keep the library alive you can still do the following (if that make any sense), do not I'm not saying it is a good idea, still seems the behaviour you want.

int main(){ 
    try{
        LibraryGuard lg;
        // object alive here
        try{
            //other code

        }catch(...){

        }
    }catch(...){

    }

}
CoffeDeveloper
  • 7,961
  • 3
  • 35
  • 69
  • Downvoters invited for feedback as usual, the OP clearly made clear that he don't want to call destructor because it calls "Terminate", however the simple fix is that we can track with a flag wich libraries have been initialized and hence need cleanup. The solution work, if there are troubles with it the OP will comment. I have prooven experience with excpetion safety code, so I know my way. – CoffeDeveloper May 12 '16 at 16:35
  • 2
    I think OP problem is that OP does not want try block to encompass whole `main()` and if he makes it small, library cannot be used past `try` block (destructor called and library deinitializated) – Revolver_Ocelot May 12 '16 at 16:41
  • I think you misunderstand my problem. Your solution still causes the constructor to potentially throw; my problem is that I don't know how to catch this exception. – Alessandro Power May 12 '16 at 16:43
  • Try again, I edited my answer in the meanwhile. However why Ii'm being downvoted, try the solution will work, Most important you should provide a code snippets of how you want the main. Consider editing your question instead of pointing out problems in other's answers – CoffeDeveloper May 12 '16 at 16:49
  • I don't see how your proposal will work. If library initialization is successful, `initializedGLFW` is `true`, which means that when `lg`'s destructor is called (and `lg`'s dtor will be called at the end of the `try` block), `glfwTerminate()` will be executed, which uninitializes the library. Incidentally, I am not downvoting you. – Alessandro Power May 12 '16 at 17:02
  • Why the heck you want to keep the library alive? Just put all code inside try/catch. this way you will also catch exceptions. Seems you just want to wrap the main u.u (see the edit) – CoffeDeveloper May 12 '16 at 17:07
  • @DarioOO "Just put all code inside try/catch." I am trying to avoid this because I don't want to have a ~100 line `try` block. If I didn't mind a `try` block that large, then my original code would suffice. – Alessandro Power May 12 '16 at 17:27