Thursday, April 20, 2006

C++ : Once false always false

I've recently written a useful little class that can be treated like a plain old bool type but has a couple of behavioural differences. It defaults to true on construction and once it is set to false, it can never be set to true. Here it is



class OnceFalseAlwaysFalse
{
public:
OnceFalseAlwaysFalse() : m_Value(true) {}
operator bool() { return m_Value; }
const bool operator ==(const OnceFalseAlwaysFalse &rhs)
{ return m_Value==rhs.m_Value; }
const bool operator !=(const OnceFalseAlwaysFalse &rhs)
{ return m_Value!=rhs.m_Value; }
const bool operator !(void)
{ return !m_Value; }
const OnceFalseAlwaysFalse &operator =(const bool & value)
{ if(m_Value) m_Value=value; return *this; }
private:
bool m_Value;
};


What the hell use is that? I hear you cry. Well, consider the following, fictitious example.



// Function to save as many pages as it can,
// returns false if any failed to print.
bool PrintDocument(void)
{
bool success = true;

if(!PrintPage(1))
success = false;
if(!PrintPage(2))
success = false;
if(!PrintPage(3))
success = false;

return success;
}


And this is the function using the OnceFalseAlwaysFalse class instead of a plain old bool...



bool PrintDocument(void)
{
OnceFalseAlwaysFalse success;
success = PrintPage(1);
success = PrintPage(2);
success = PrintPage(3);
return success;
}


A lot neater I think you'll agree. It takes out the multiple if tests needed to ensure that the overall success of the function is stored rather than simply the storing the success of the last operation. The class also illustrates one of Bjarne Stroustrups major intentions for the C++ language, i.e. that custom data types can be treated in exactly the same way as built-in types. This is achieved via the medium of operator overloading. For this class I have overloaded the =, !=, == and bool (implicit conversion to bool) operators. The last one is useful as it allows an instance of OnceFalseOnlyFalse to be returned by a function which is defined to return a bool, as many are.


Hope this can help you write some easier-to-read code!

2 comments:

CityCoder said...

This will make a nice addition to my toolbox for sure. The example is weak though, because the following would make more sense:


bool PrintDocument(void)
{
    return
        PrintPage(1) &&
        PrintPage(2) &&
        PrintPage(3);
}


or maybe:


bool PrintDocument(size_t const npages)
{
    size_t idx=0;
    while ( idx<npages && PrintPage(idx) )
        ++idx;
    return npages==idx;
}


or clearer:


bool PrintDocument(size_t const npages)
{
    for ( size_t idx=0; idx<npages; ++idx )
    {
        if ( !PrintPage(idx) )
            return false;
    }
    return true;
}


I understand what you are getting at though.

Knanshon said...

All valid points, I guess I keep blogging about solutions to specific problems and presenting them in as generic tools with all the project specific stuff taken out :) Still, might be useful ... I'll add disclaimers from now on :)

One obvious addition to the class is needed, it fell over almost straight away til I added this member...

operator bool() const { return m_Value; }