fork download
  1. #include <iostream>
  2. #include <stdexcept>
  3.  
  4. // HasConditions is a class with a logical invariant but for whatever reason sometimes we want to let
  5. // users muck around, but only if they make sure the invariant holds afterwards.
  6. // To do this, an instance of class HCAccessor provides access to the innwards of a HasConditions and
  7. // checks that the HasConditions class invariant holds when the HCAccessor drops out of scope.
  8.  
  9. class HCAccessor;
  10.  
  11. class HasConditions
  12. {
  13. // class "mostly-invariant"
  14. // 7 < payload_ <42
  15. int payload_;
  16.  
  17. bool valid() const
  18. {
  19. if (!(7 < payload_) || !(payload_ < 42))
  20. return false;
  21. else
  22. return true;
  23. }
  24.  
  25. public:
  26. HasConditions(const int payload)
  27. : payload_(payload)
  28. {
  29. if (!valid())
  30. {
  31. // This exception is not thrown
  32. std::cout << "Trying to construct a HasConditions with invalid payload" << std::endl;
  33. throw std::runtime_error("can't construct");
  34. }
  35. }
  36.  
  37. friend class HCAccessor;
  38. };
  39.  
  40. class HCAccessor
  41. {
  42. HasConditions& hc_;
  43.  
  44. public:
  45. HCAccessor(HasConditions& hc)
  46. : hc_(hc)
  47. {}
  48.  
  49. HCAccessor(HCAccessor& other)
  50. : hc_(other.hc_)
  51. {}
  52.  
  53. ~HCAccessor()
  54. {
  55. // conventional wisdom is to not throw from a destructor but this class
  56. // doesn't manage resources, doesn't have any clean up to do, and it's
  57. // only job is to possible throw an exception
  58. if (!hc_.valid())
  59. {
  60. // This exception will be thrown just once.
  61. std::cout << "HCAccessor leaving scope with HasConditions in invalid state. PANIC!" << std::endl;
  62. throw std::runtime_error("you broke it!");
  63. }
  64. }
  65.  
  66. void payload(const int newval)
  67. {
  68. hc_.payload_ = newval;
  69. }
  70.  
  71. int payload() const
  72. {
  73. return hc_.payload_;
  74. }
  75. };
  76.  
  77.  
  78. void play_nice()
  79. {
  80. HasConditions hc{30};
  81. HCAccessor hca(hc);
  82.  
  83. hca.payload(-5);
  84. hca.payload(100);
  85. hca.payload(30);
  86. }
  87.  
  88. void break_it()
  89. {
  90. HasConditions hc{30};
  91. HCAccessor hca(hc);
  92.  
  93. hca.payload(-5);
  94. }
  95.  
  96. void wrap_play_nice()
  97. {
  98. std::cout << "calling play_nice()" << std::endl;
  99. try
  100. {
  101. play_nice();
  102. }
  103. catch (...)
  104. {
  105. std::cout << "it broke";
  106. return;
  107. }
  108. std::cout << "all is well" << std::endl;
  109. return;
  110. }
  111.  
  112. void wrap_break_it()
  113. {
  114. std::cout << "calling break_it()" << std::endl;
  115. try
  116. {
  117. // exception thrown from here causes call to std::terminate()
  118. std::cout << "calling break_it()" << std::endl;
  119. break_it();
  120. // the function never returns
  121. std::cout << "returned from break_it()" << std::endl;
  122. }
  123. catch (...)
  124. {
  125. std::cout << "it broke";
  126. return;
  127. }
  128. std::cout << "all is well" << std::endl;
  129. return;
  130. }
  131.  
  132.  
  133. int main()
  134. {
  135. try
  136. {
  137. throw std::runtime_error("sanity check: does try-catch work at all?");
  138. }
  139. catch (const std::runtime_error& re)
  140. {
  141. std::cout << re.what() << std::endl;
  142. std::cout << "yes it does, swallowing..." << std::endl;
  143. }
  144.  
  145. wrap_play_nice();
  146. wrap_break_it();
  147. }
Runtime error #stdin #stdout #stderr 0s 3432KB
stdin
Standard input is empty
stdout
sanity check: does try-catch work at all?
yes it does, swallowing...
calling play_nice()
all is well
calling break_it()
calling break_it()
HCAccessor leaving scope with HasConditions in invalid state. PANIC!
stderr
terminate called after throwing an instance of 'std::runtime_error'
  what():  you broke it!