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?