#include <iostream>
#include <stdexcept>
// HasConditions is a class with a logical invariant but for whatever reason sometimes we want to let
// users muck around, but only if they make sure the invariant holds afterwards.
// To do this, an instance of class HCAccessor provides access to the innwards of a HasConditions and
// checks that the HasConditions class invariant holds when the HCAccessor drops out of scope.
class HCAccessor;
class HasConditions
{
// class "mostly-invariant"
// 7 < payload_ <42
int payload_;
bool valid() const
{
if (!(7 < payload_) || !(payload_ < 42))
return false;
else
return true;
}
public:
HasConditions(const int payload)
: payload_(payload)
{
if (!valid())
{
// This exception is not thrown
std::cout << "Trying to construct a HasConditions with invalid payload" << std::endl;
throw std::runtime_error("can't construct");
}
}
friend class HCAccessor;
};
class HCAccessor
{
HasConditions& hc_;
public:
HCAccessor(HasConditions& hc)
: hc_(hc)
{}
HCAccessor(HCAccessor& other)
: hc_(other.hc_)
{}
~HCAccessor()
{
// conventional wisdom is to not throw from a destructor but this class
// doesn't manage resources, doesn't have any clean up to do, and it's
// only job is to possible throw an exception
if (!hc_.valid())
{
// This exception will be thrown just once.
std::cout << "HCAccessor leaving scope with HasConditions in invalid state. PANIC!" << std::endl;
throw std::runtime_error("you broke it!");
}
}
void payload(const int newval)
{
hc_.payload_ = newval;
}
int payload() const
{
return hc_.payload_;
}
};
void play_nice()
{
HasConditions hc{30};
HCAccessor hca(hc);
hca.payload(-5);
hca.payload(100);
hca.payload(30);
}
void break_it()
{
HasConditions hc{30};
HCAccessor hca(hc);
hca.payload(-5);
}
void wrap_play_nice()
{
std::cout << "calling play_nice()" << std::endl;
try
{
play_nice();
}
catch (...)
{
std::cout << "it broke";
return;
}
std::cout << "all is well" << std::endl;
return;
}
void wrap_break_it()
{
std::cout << "calling break_it()" << std::endl;
try
{
// exception thrown from here causes call to std::terminate()
std::cout << "calling break_it()" << std::endl;
break_it();
// the function never returns
std::cout << "returned from break_it()" << std::endl;
}
catch (...)
{
std::cout << "it broke";
return;
}
std::cout << "all is well" << std::endl;
return;
}
int main()
{
try
{
throw std::runtime_error("sanity check: does try-catch work at all?");
}
catch (const std::runtime_error& re)
{
std::cout << re.what() << std::endl;
std::cout << "yes it does, swallowing..." << std::endl;
}
wrap_play_nice();
wrap_break_it();
}