move.cpp 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  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 <vector>
  7. #include <utility>
  8. #include <cassert>
  9. //[move
  10. class circular_buffer :
  11. private boost::contract::constructor_precondition<circular_buffer> {
  12. public:
  13. void invariant() const {
  14. if(!moved()) { // Do not check (some) invariants for moved-from objects.
  15. BOOST_CONTRACT_ASSERT(index() < size());
  16. }
  17. // More invariants here that hold also for moved-from objects (e.g.,
  18. // all assertions necessary to successfully destroy moved-from objects).
  19. }
  20. // Move constructor.
  21. circular_buffer(circular_buffer&& other) :
  22. boost::contract::constructor_precondition<circular_buffer>([&] {
  23. BOOST_CONTRACT_ASSERT(!other.moved());
  24. })
  25. {
  26. boost::contract::check c = boost::contract::constructor(this)
  27. .postcondition([&] {
  28. BOOST_CONTRACT_ASSERT(!moved());
  29. BOOST_CONTRACT_ASSERT(other.moved());
  30. })
  31. ;
  32. move(std::forward<circular_buffer>(other));
  33. }
  34. // Move assignment.
  35. circular_buffer& operator=(circular_buffer&& other) {
  36. // Moved-from can be (move) assigned (so no pre `!moved()` here).
  37. boost::contract::check c = boost::contract::public_function(this)
  38. .precondition([&] {
  39. BOOST_CONTRACT_ASSERT(!other.moved());
  40. })
  41. .postcondition([&] {
  42. BOOST_CONTRACT_ASSERT(!moved());
  43. BOOST_CONTRACT_ASSERT(other.moved());
  44. })
  45. ;
  46. return move(std::forward<circular_buffer>(other));
  47. }
  48. ~circular_buffer() {
  49. // Moved-from can always be destroyed (in fact no preconditions).
  50. boost::contract::check c = boost::contract::destructor(this);
  51. }
  52. bool moved() const {
  53. boost::contract::check c = boost::contract::public_function(this);
  54. return moved_;
  55. }
  56. private:
  57. bool moved_;
  58. /* ... */
  59. //]
  60. public:
  61. explicit circular_buffer(std::vector<char> const& data,
  62. unsigned start = 0) :
  63. boost::contract::constructor_precondition<circular_buffer>([&] {
  64. BOOST_CONTRACT_ASSERT(start < data.size());
  65. }),
  66. moved_(false),
  67. data_(data),
  68. index_(start)
  69. {
  70. boost::contract::check c = boost::contract::constructor(this)
  71. .postcondition([&] {
  72. BOOST_CONTRACT_ASSERT(!moved());
  73. })
  74. ;
  75. }
  76. // Copy constructor.
  77. circular_buffer(circular_buffer const& other) :
  78. boost::contract::constructor_precondition<circular_buffer>([&] {
  79. BOOST_CONTRACT_ASSERT(!other.moved());
  80. })
  81. {
  82. boost::contract::check c = boost::contract::constructor(this)
  83. .postcondition([&] {
  84. BOOST_CONTRACT_ASSERT(!moved());
  85. })
  86. ;
  87. copy(other);
  88. }
  89. // Copy assignment.
  90. circular_buffer& operator=(circular_buffer const& other) {
  91. // Moved-from can be (copy) assigned (so no pre `!moved()` here).
  92. boost::contract::check c = boost::contract::public_function(this)
  93. .precondition([&] {
  94. BOOST_CONTRACT_ASSERT(!other.moved());
  95. })
  96. .postcondition([&] {
  97. BOOST_CONTRACT_ASSERT(!moved());
  98. })
  99. ;
  100. return copy(other);
  101. }
  102. char read() {
  103. boost::contract::check c = boost::contract::public_function(this)
  104. .precondition([&] {
  105. BOOST_CONTRACT_ASSERT(!moved());
  106. })
  107. ;
  108. unsigned i = index_++;
  109. if(index_ == data_.size()) index_ = 0; // Circular.
  110. return data_.at(i);
  111. }
  112. private:
  113. circular_buffer& copy(circular_buffer const& other) {
  114. data_ = other.data_;
  115. index_ = other.index_;
  116. moved_ = false;
  117. return *this;
  118. }
  119. circular_buffer& move(circular_buffer&& other) {
  120. data_ = std::move(other.data_);
  121. index_ = std::move(other.index_);
  122. moved_ = false;
  123. other.moved_ = true; // Mark moved-from object.
  124. return *this;
  125. }
  126. std::vector<char> data_;
  127. unsigned index_;
  128. public:
  129. unsigned index() const {
  130. boost::contract::check c = boost::contract::public_function(this);
  131. return index_;
  132. }
  133. unsigned size() const {
  134. boost::contract::check c = boost::contract::public_function(this);
  135. return data_.size();
  136. }
  137. };
  138. int main() {
  139. struct err {};
  140. boost::contract::set_precondition_failure(
  141. [] (boost::contract::from) { throw err(); });
  142. {
  143. circular_buffer x({'a', 'b', 'c', 'd'}, 2);
  144. assert(x.read() == 'c');
  145. circular_buffer y1 = x; // Copy constructor.
  146. assert(y1.read() == 'd');
  147. assert(x.read() == 'd');
  148. circular_buffer y2({'h'});
  149. y2 = x; // Copy assignment.
  150. assert(y2.read() == 'a');
  151. assert(x.read() == 'a');
  152. circular_buffer z1 = std::move(x); // Move constructor.
  153. assert(z1.read() == 'b');
  154. // Calling `x.read()` would fail `!moved()` precondition.
  155. x = y1; // Moved-from `x` can be copy assigned.
  156. assert(x.read() == 'a');
  157. assert(y1.read() == 'a');
  158. circular_buffer z2({'k'});
  159. z2 = std::move(x); // Move assignment.
  160. assert(z2.read() == 'b');
  161. // Calling `x.read()` would fail `!moved()` precondition.
  162. x = std::move(y2); // Moved-from `x` can be move assigned.
  163. assert(x.read() == 'b');
  164. // Calling `y2.read()` would fail `!moved()` precondition.
  165. } // Moved-from `y2` can be destroyed.
  166. return 0;
  167. }