1

I have a library written in C++. The library has a function which accepts commands as a string and executes them. If an error is encountered (either in the command or while running the command) an "error function" is called which does some cleanup and finally calls exit(1). I am now trying to implement a graphical user interface (using Qt) to the library. The problem is that when an error is encountered, exit is called and my application crashes. I have access to the library source code but I would like to keep modifying the source code to minimum.

I am thinking of rewriting the error function such that it just stops executing code and stays in an idle state until another command is passed to the library from the user-interface. The problem is I am not sure how to go about doing it. I am basically looking for a function call equivalent to exit system call (so that the error function never returns to the code which generated the error) except that I do not want the application to exit but instead just go to an idle state and wait for calls from the user interface.

If there is another way to implement this please let me know. Please also let me know if you need more details.

Thanks in advance,

Here is some code which shows what my problem is

#include <iostream>
#include <string>
#include <cstdlib>
using namespace std;

void error_func(string error); 
void create_sphere(float radius); 
void create_rect(float length, float width); 

int main()
{
  string command; 
  while(1)  {
    cout << "Enter command: "; 
    cin >> command; 

    if(command.compare("create_sphere") == 0)  {
      float radius; 
      cout << "Enter radius: "; 
      cin >> radius;
      create_sphere(radius); 
    }
    else if(command.compare("create_rect") == 0)  {
      float l, w; 
      cout << "Enter length and width: "; 
      cin >> l >> w; 
      create_rect(l, w); 
    }
    else if(command.compare("quit") == 0)
      break; 
  } 
}

void create_sphere(float  radius)
{
  if(radius < 0)
    error_func(string("Radius must be positive")); 

  cout << "Created sphere" << endl; 
}

void create_rect(float length, float width)
{
  if(length < 0)
    error_func(string("Length must be positive")); 

  if(width < 0)
    error_func(string("Width must be positive")); 

  cout << "Created rectangle" << endl;
} 

void error_func(string error)
{
  // do some cleanup
  cout << "ERROR: " << error << endl; 
  exit(1); 
}

Assume that create_sphere, create_rect and error_func are provided by the library. I can modify error_func as required but not the other functions (since there are many such functions).

Now when an error is encountered, I would like to go back to the while loop in main so that I can keep accepting other commands.

rkumar310
  • 78
  • 6
  • 1
    Show the code, please! – László Papp May 04 '14 at 04:52
  • 1
    maybe you might `throw` some exception instead of ̀ exit`-ing ? – Basile Starynkevitch May 04 '14 at 04:54
  • @LaszloPapp, I have provided an example code. – rkumar310 May 04 '14 at 05:22
  • Sounds like you are looking for a modal messagebox with its own event loop. Most UI frameworks provide such as a library function. To get back to the main application, bypassing the code that caused the error, you could throw an exception. – Ben Voigt May 04 '14 at 05:22
  • @BenVoigt: what?! Where did the OP mention "messagebox"? Also, throwing an exception is generally speaking... a bad idea in this case in qt software projects. – László Papp May 04 '14 at 05:24
  • @LaszloPapp: To report the error. – Ben Voigt May 04 '14 at 05:25
  • @LaszloPapp: If you disagree with throwing an exception in these exceptional conditions, what would you do instead? `longjmp`? The requirement is to not return to the caller where the error happened. There are only a few ways of accomplishing that, and of those, exceptions are the most compatible with C++. – Ben Voigt May 04 '14 at 05:26
  • @BenVoigt: as usual, I think you are trying to solve a phantasy-made problem, not the OP's. I really do not see how exception would be any useful here. – László Papp May 04 '14 at 05:31
  • @Laszlo: His library detects an error and calls his `reportError` function. How does he reach the main event loop again, without returning, without infinite recursion, and without leaking all the objects with automatic lifetime? I suppose the operations could run on a worker thread which terminates itself when there is an error. But that would still leak. Exceptions are the only mechanism C++ provides for this. – Ben Voigt May 04 '14 at 05:33
  • @BenVoigt: I still do not see how exception would be of any help. As I already suggested to him, returning with an error code is the most appropriate way here. `Exceptions are the only mechanism C++ provides for this.` -> Haha, nice joke. :) – László Papp May 04 '14 at 05:35
  • @LaszloPapp: Did you just skip the part of the specification that said "equivalent to exit system call (**so that the error function never returns** to the code which generated the error)" ? – Ben Voigt May 04 '14 at 05:36
  • @BenVoigt: Can you please explain how can I throw an exception from `error_func` so that control is passed to the main loop? – rkumar310 May 04 '14 at 06:26
  • @rkumar: I suppose from your description that the complex code that fails is called from one of the UI event handlers? This handler would use `try { do_complex_thing() } catch (const return_to_ui&) {}`. Then your error function will do `throw return_to_ui();` You'll need `struct return_to_ui {};`, which probably ought to be a unique type not used for any other purpose. In this way, your error handler can jump through an arbitrary number of function calls directly back to UI code, without having to return from them one-at-a-time with error checking. – Ben Voigt May 05 '14 at 03:34
  • @rkumar: It will work well if the code is written for exception-safety (using standard containers and smart pointers), since destructors are automatically called during exception handling. If exception safety has been ignored in the intermediate layers, you're likely to see memory leaks or worse. Note that exception safety doesn't mean that the exception should be caught in the intermediate layers, just that destructors of automatic variables clean up appropriately. – Ben Voigt May 05 '14 at 03:35
  • @rkumar: You also have to decide whether the error will be reported to the user by the error function, or the UI handler that called the whole thing in the first place. In the latter case, you should stick information about the error into the exception object, possibly derive from `std::exception` and use its `what()` description. – Ben Voigt May 05 '14 at 03:43

3 Answers3

1

I am basically looking for a function call equivalent to exit system call (so that the error function never returns to the code which generated the error) except that I do not want the application to exit but instead just go to an idle state and wait for calls from the user interface.

Basically, you are looking for an event loop. The typical minimal Qt program is as follows:

#include <QApplication>
#include <QMainWindow>

int main(int argc, char **argv)
{
    QApplication(argc, argv);
    QMainWindow w;
    w.show();
    return application.exec(); // What you want instead of exit
}

Now, you could replace QMainWindow with your own class, and declare a slot in that which gets called when you are trying to handle a command from the user interface.

#include <QWidget>

...

class MyWidget : public QWidget
{
    Q_OBJECT
    public:
        explicit MyWidget(QWidget *parent) : QWidget(parent)
        {
            connect(sender, SIGNAL(mySignal()), SLOT(handleCommand()));
        }
    public slots:
        void handleCommand()
        {
            // Handle your command here.
            // Print the error code.
            qDebug() << error_func(string("Radius must be positive"));
            // or simply:
            qDebug() << "Radius must be positive";
        } // Leaving the scope, and getting back to the event loop
}

As for the bogus library, well, if it exits, it does. There is not much you can do about that without fixint the library. It is a very bad behavior from most of the libraries.

The modification would be not to exit, but return an error code - which is a general practice in Qt software - and leave it with the application when to exit if they wish.

The application would not quit in your case. Again, It is a very bad idea for a library function to exit. Even Qt does not do except 1-2 times in a few million LOC.

I would suggest not to throw an exception. It is generally not common in Qt software, and you could make your software consistent by just using error codes like the rest of Qt does for you.

László Papp
  • 51,870
  • 39
  • 111
  • 135
  • I actually have something similar in my Qt code. However, the function handleCommand() eventually makes a call to the library function and if that library function calls exit(1) my application will exit as well. I would like to re-enter the main event loop if the library function encounters an error. – rkumar310 May 04 '14 at 05:26
  • @rkumar310: If it exits, it does. The modification would be not to exit, but return an error code and leave it with the application when to exit. It is a very bad idea for a library function to exit. Even Qt does not do except 1-2 times in a few million LOC. See the answer update at the end. – László Papp May 04 '14 at 05:37
  • I surely need help otherwise I would not be here. The original code is big and spans multiple files and I am not sure posting the entire code (if that were possible) would help. The std based example basically shows my problem in a simpler setting. I would like to modify `error_func` so that when it is called it just goes back to the while loop within `main` bypassing all the subsequent code in the function where error occured. Sorry if it caused any confusion. Of course the specifics would depend on the fact that I am using Qt and not a console application. – rkumar310 May 04 '14 at 05:39
  • I agree with you that it is a very bad idea for the library function to exit but unfortunately that's how the library is written. I am just trying to figure out if there is any way around it. – rkumar310 May 04 '14 at 05:42
  • @rkumar310: as I already wrote, modifying the library to return an error code or not calling the library. Both of those are demonstrated with two different QDebug statements in the snippet of my answer. – László Papp May 04 '14 at 05:43
  • Why do you need to complicate it unnecessarily, when you already have message handlers in Qt? See my answer for details. – Cool_Coder May 04 '14 at 06:27
  • @Cool_Coder: I do not see any complication. To me, this seems to be the right way. In fact, for a simple return, a message handler as long as yours seems to me the unnecessary complication. :) Actually, I am not even sure it solves the issue. – László Papp May 04 '14 at 06:29
  • @Laszlo: Modifying the library to return an error code instead of exiting would be a huge amount of work. I also did not quite understand what you mean when you say not to call the library. When I handle the command I will basically be calling other library functions which might just exit on error. – rkumar310 May 04 '14 at 06:30
  • @rkumar310: that is the only reasonable to do so. You would need to do that at the next occasion, etc... This is a badly designed library. Also, why is it difficult? Instead of exit, you just return with an integer. What is hard about it? – László Papp May 04 '14 at 06:32
  • @Laszlo: As you said this is a badly designed library. On some occasions, the error function is called from a constructor in which case I cannot return an integer! – rkumar310 May 04 '14 at 06:36
  • Why? You surely can call functions returning in a constructor? – László Papp May 04 '14 at 06:40
  • @Laszlo: I guess you can! I don't know what I was thinking. It does appear that a good solution in the long term would be to modify the library to return error codes. I guess I will get to it then! – rkumar310 May 04 '14 at 06:45
  • @rkumar: It's true, the way to report failure from a constructor is by throwing an exception, there's no other way to *not* return a newly created object. People who really want to avoid exceptions use two-phase construction, but that's going to be far more work and expert C++ programmers recommend against it, I recommend you read http://stackoverflow.com/q/7894209/103167 before using that approach. – Ben Voigt May 05 '14 at 03:41
  • @rkumar310: no problem, glad that you decided to do it right. – László Papp May 05 '14 at 03:46
  • @rkumar310: Do not get misled by that guy, he is not referring to the issue of your error function, but some external constructor. It is completely valid to throw an exception as well as having "isValid" or "error(String)" methods in a class. He seems to be trying to limit things still to his exception world, and pretend that nothing exists beyond that. – László Papp May 05 '14 at 03:52
0

Invent an error state (idle state) and make the function never fail. The error state should become visible and be resolvable by some means.

If you can not reach a resolvable error state, it might be possible to rollback to some prior (initial) state.

If the options above are not possible you have some serious failure (software, hardware, data) and you might terminate the program.

All above can be achieved with return values or a getter function (indicating the current state) and a setter manipulating the current state - an exit call is a poor solution in a library. If you have an unresolvable state or can not rollback to a prior state you might throw an exception, catch it in the user interface and terminate the program after displaying the issue.

0

You should install a message handler which will automatically reduce a lot of your work. Additionally it will help in reducing your debugging too. Here is my message handler for my Qt5 application. It will need a little tweaking if you are using Qt4:

QFile *logFile = NULL;//The file in which you will output the debug info to
QTextStream *logStream = NULL;//text stream for your log file
QMutex *mutex = NULL;//Always use mutex if you are multi threading your application
bool *debugMode = NULL;//it just a flag in case you want to turn off debugging
bool errorMsg = false;//use the value of this variable after QApplication::exec() if you need to show an error message

void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
 {
    if(((logFile != NULL) && (debugMode != NULL)))
    {
        mutex->lock();
         switch (type)
         {
         case QtDebugMsg:
             if(!*debugMode)
             {
                 mutex->unlock();
                 return;
             }
             *logStream << msg;
             logStream->flush();
             break;
         case QtWarningMsg:
             if(!((QString)context.function).contains("setGeometry"))
             {
                 *logStream << "\n*** Warning ***\n";
                 *logStream << msg << endl;
                 *logStream << "Category: " << context.category << endl;
                 *logStream << "File: " << context.file << endl;
                 *logStream << "Function: " << context.function << endl;
                 *logStream << "Line: " << context.line << endl;
                 *logStream << "Version: " << context.version;
                 *logStream << "\n*** Warning Complete ***\n";
                 logStream->flush();
                 errorMsg = true;
                 SessionManager::get_obj()->saveCurrentSession();
             }
             break;
         case QtCriticalMsg:
             *logStream << "\n*** Critical ***\n";
             *logStream << msg << endl;
             *logStream << "Category: " << context.category << endl;
             *logStream << "File: " << context.file << endl;
             *logStream << "Function: " << context.function << endl;
             *logStream << "Line: " << context.line << endl;
             *logStream << "Version: " << context.version;
             *logStream << "\n*** Critical Complete ***\n";
             logStream->flush();
             errorMsg = true;
             SessionManager::get_obj()->saveCurrentSession();
             break;
         case QtFatalMsg:
             *logStream << "\n*** Fatal ***\n";
             *logStream << msg << endl;
             *logStream << "Category: " << context.category << endl;
             *logStream << "File: " << context.file << endl;
             *logStream << "Function: " << context.function << endl;
             *logStream << "Line: " << context.line << endl;
             *logStream << "Version: " << context.version;
             *logStream << "\n*** Fatal Complete ***\n";
             logStream->flush();
             errorMsg = false;
             SessionManager::get_obj()->saveCurrentSession();
             ShowErrorMsg(SessionManager::getSessionName());
             exit(0);
         }
         mutex->unlock();
    }
 }

To install a message handler add the following code in the main() of your GUI.

qInstallMessageHandler(myMessageOutput);

You can ignore the check for setGeometry if you want to but I find that this warning is emitted unnecessarily. So you can keep it. Also you may want to have a Session Manager which will automatically save the current session whenever an error is encountered.

When you have done this, you can safely call qFatal() when you want to terminate your application, or else use qCritical() if you want some other functionality.

Cool_Coder
  • 4,888
  • 16
  • 57
  • 99