throw_on_failure.cpp 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. // Copyright (C) 2008-2018 Lorenzo Caminiti
  2. // Distributed under the Boost Software License, Version 1.0 (see accompanying
  3. // file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt).
  4. // See: http://www.boost.org/doc/libs/release/libs/contract/doc/html/index.html
  5. #include <boost/contract.hpp>
  6. #include <iostream>
  7. #include <cstring>
  8. #include <cassert>
  9. //[throw_on_failure_class_begin
  10. struct too_large_error {};
  11. template<unsigned MaxSize>
  12. class cstring
  13. #define BASES private boost::contract::constructor_precondition<cstring< \
  14. MaxSize> >
  15. : BASES
  16. {
  17. //]
  18. public:
  19. typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types;
  20. #undef BASES
  21. //[throw_on_failure_ctor
  22. public:
  23. /* implicit */ cstring(char const* chars) :
  24. boost::contract::constructor_precondition<cstring>([&] {
  25. BOOST_CONTRACT_ASSERT(chars); // Throw `assertion_failure`.
  26. // Or, throw user-defined exception.
  27. if(std::strlen(chars) > MaxSize) throw too_large_error();
  28. })
  29. {
  30. //]
  31. boost::contract::check c = boost::contract::constructor(this)
  32. .postcondition([&] {
  33. BOOST_CONTRACT_ASSERT(size() == std::strlen(chars));
  34. })
  35. ;
  36. size_ = std::strlen(chars);
  37. for(unsigned i = 0; i < size_; ++i) chars_[i] = chars[i];
  38. chars_[size_] = '\0';
  39. }
  40. //[throw_on_failure_dtor
  41. public:
  42. void invariant() const {
  43. if(size() > MaxSize) throw too_large_error(); // Throw user-defined ex.
  44. BOOST_CONTRACT_ASSERT(chars_); // Or, throw `assertion_failure`.
  45. BOOST_CONTRACT_ASSERT(chars_[size()] == '\0');
  46. }
  47. ~cstring() noexcept { // Exception specifiers apply to contract code.
  48. // Check invariants.
  49. boost::contract::check c = boost::contract::destructor(this);
  50. }
  51. //]
  52. unsigned size() const {
  53. // Check invariants.
  54. boost::contract::check c = boost::contract::public_function(this);
  55. return size_;
  56. }
  57. private:
  58. char chars_[MaxSize + 1];
  59. unsigned size_;
  60. //[throw_on_failure_class_end
  61. /* ... */
  62. };
  63. //]
  64. void bad_throwing_handler() { // For docs only (not actually used here).
  65. //[throw_on_failure_bad_handler
  66. /* ... */
  67. // Warning... might cause destructors to throw (unless declared noexcept).
  68. boost::contract::set_invariant_failure(
  69. [] (boost::contract::from) {
  70. throw; // Throw no matter if from destructor, etc.
  71. }
  72. );
  73. /* ... */
  74. //]
  75. }
  76. //[throw_on_failure_handlers
  77. int main() {
  78. boost::contract::set_precondition_failure(
  79. boost::contract::set_postcondition_failure(
  80. boost::contract::set_invariant_failure(
  81. boost::contract::set_old_failure(
  82. [] (boost::contract::from where) {
  83. if(where == boost::contract::from_destructor) {
  84. // Shall not throw from C++ destructors.
  85. std::clog << "ignored destructor contract failure" << std::endl;
  86. } else throw; // Re-throw (assertion_failure, user-defined, etc.).
  87. }
  88. ))));
  89. boost::contract::set_except_failure(
  90. [] (boost::contract::from) {
  91. // Already an active exception so shall not throw another...
  92. std::clog << "ignored exception guarantee failure" << std::endl;
  93. }
  94. );
  95. boost::contract::set_check_failure(
  96. [] {
  97. // But now CHECK shall not be used in destructor implementations.
  98. throw; // Re-throw (assertion_failure, user-defined, etc.).
  99. }
  100. );
  101. /* ... */
  102. //]
  103. {
  104. cstring<3> s("abc");
  105. assert(s.size() == 3);
  106. }
  107. #ifndef BOOST_CONTRACT_NO_PRECONDITIONS
  108. // These failures properly handled only when preconditions checked.
  109. try {
  110. char* c = 0;
  111. cstring<3> s(c);
  112. assert(false);
  113. } catch(boost::contract::assertion_failure const& error) {
  114. // OK (expected).
  115. std::clog << "ignored: " << error.what() << std::endl;
  116. } catch(...) { assert(false); }
  117. try {
  118. cstring<3> s("abcd");
  119. assert(false);
  120. } catch(too_large_error const&) {} // OK (expected).
  121. catch(...) { assert(false); }
  122. #endif
  123. return 0;
  124. }