Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ error handling pattern [closed]

We have a large code base with a lot of code which does this kind of thing:

bool DoSomething(CString Value)
{
   if(Value == "bad")
   {
       AfxMessageBox("description of error");
       return false;
   }
   return true;
}

or even just this:

bool DoSomething(CString Value)
{
   if(Value == "bad")
   {
      return false;
   }
   return true;
}

We have considered various alternatives:

  • Exceptions - Basically we’re sold on the cons as described here: http://www.codeproject.com/Articles/38449/C-Exceptions-Pros-and-Cons
  • Passing an additional string ref parm which is populated with error text - but it requires a pre-instantiation of the error string prior to the call, and adds bulk to the parm lists
  • Populating a ‘last_error’ member variable - this seems to suffer (imo) the drawbacks expressed here: Is GetLastError() kind of design pattern? Is it good mechanism?
  • Passing back an enum (or the like) which can be mapped to a description of the error with some other function - but this feels ‘heavy’ for small functions and objects, and also creates space between the place the error occurs and the place where the messages are maintained (though I suppose in multi-ligual environment they’d appreciate the centralization of the text).

So I wondered if we could create a set of classes like the following:

class CResult
{
protected:
   CResult()
   {
      // Don't have to initialize because the derived class will do it
   }
public:
   operator bool() { return m_Result; };

   bool m_Result;
   CString m_Message;
};

class CSuccess : public CResult
{
public:
   CSuccess()
   {
       m_Result = true;
   }
};

class CFailure : public CResult
{
public:
   CFailure(const CString & Message)
   {
      m_Result = false;
      m_Message = Message;
   }
};

Then the above code could look like this:

CResult DoSomething(CString Value)
{
   if(Value == "bad")
   {
      return CFailure("description of error");
   }
   return CSuccess();
}

What I like about it:

  • The code is still readable and the error message is maintained near the error condition
  • The programmer will be slightly more compelled to actually provide an error string on an error condition (yes, they could provide blanks but that would seem a more egregious mistake, imo)
  • The caller doesn’t have to create any specialized variables prior to the function call, and can still treat the function result like a bool - or in a case where the error is not ignored, easily retrieve an explanation
  • The next programmer can what error model is in use just by looking at the function definition

The main drawback I see is that there is higher overhead on success, in that an object and a string and a bool will all be instantiated - but a lot of the times in our app the code in question is not performance sensitive, such as validating user input, etc.

Am I missing some other big drawback? Is there an even better solution?

like image 623
hunter Avatar asked Oct 18 '25 14:10

hunter


1 Answers

It can be useful to separate "errors" in two categories:

Fatal errors

These are the kind of errors from which a recovery make no sense.

void check(bool cond, const string& msg)
{
  if (!cond)
  {
    // eventually log it somewhere
    std::cerr << "Fatal: " << msg << std::endl;
    exit(1);
  }
}

Exceptional errors

These are the kind of errors that you can recover from and keep the program in a running state.

void check_ex(bool cond, const string& msg)
{
  if (!cond)
  {
    // eventually log it somewhere
    throw std::runtime_error(msg);
  }
}
like image 67
Gigi Avatar answered Oct 20 '25 05:10

Gigi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!