After reading manasij7479's thread on throwing exceptions in constructors, I got to thinking about the related issue in general of deciding whether to throw an exception or return boolean values from certain functions. It then dawned on me that there may be a way to "have one's cake and eat it too": design a class that throws an exception if unchecked, or simply reports it's status otherwise. So here's what I've come up with so far (just keep in mind that the code is only a few hours old, so it probably contains bugs):
Code:
/*
A possible candidate for propagating state-integrity from functions.
Note: the const-ness of rvalues of this class is NOT preserved
(necessary to make everything work properly, unfortunately)
*/
class status
{
public:
enum
{
/*
Give a few of options for the most common naming choices
*/
success,
good = success,
failure,
fail = failure,
bad = fail,
/*
User-defined codes should start at status::define
*/
define
};
status(int code = success, bool throws = true)
{
(*this)(code, throws);
}
status(bool state, bool throws = true)
{
(*this)(state, throws);
}
status(const status& other)
{
*this = other;
}
status& operator () (int code, bool throws = true)
{
this->code = code;
this->throws = throws;
return *this;
}
status& operator () (bool state, bool throws = true)
{
return (*this)(state ? success : failure, throws);
}
operator bool ()
{
throws = false;
return code == success;
}
operator int ()
{
throws = false;
return code;
}
bool operator == (const status& other)
{
/*
We'll use this type of comparison in the event that
derived classes decide to use bitmasks for error codes
*/
return code & other.code;
}
bool operator != (const status& other)
{
return !(*this == other);
}
status& operator = (const status& other)
{
this->code = other.code;
this->throws = true;
const_cast<status&>(other).throws = false;
return *this;
}
virtual ~status()
{
if(code != success && throws)
throw status(this->code, false);
}
/*
Probably wouldn't hurt to make this public?
*/
int code;
protected:
bool throws;
};
And here's an example of it being used:
Code:
#include <iostream>
class example
{
public:
status broken()
{
return false;
/*
Alternately:
*/
return status::fail;
/*
...But NOT this, as we would be passing an int (not a bool) to the constructor:
*/
return 0;
}
static void report(int pos, bool state)
{
std::cout << "(" << pos << "): " << (state ? "success" : "fail") << std::endl;
}
};
int main(void)
{
example test;
try
{
/*
Status NOT checked, so exception is thrown (but eventually caught)
*/
test.broken();
}
catch(status&)
{
example::report(1, false);
}
/*
If 'state' is never checked by the time it goes out of scope, an exception will be thrown
*/
status state = test.broken();
/*
Okay, 'state' is checked, so NO exception is thrown
*/
example::report(2, state);
/*
Status checked, so NO exception is thrown
*/
example::report(3, test.broken());
/*
Status NOT checked, so exception is thrown (but not caught, so program terminates)
*/
test.broken();
/*
We never get this far, of course
*/
example::report(4, false);
}
Any comments, suggestions, or bug-fixes would be appreciated!