swap.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. /* Unit testing for outcomes
  2. (C) 2013-2019 Niall Douglas <http://www.nedproductions.biz/> (5 commits)
  3. Boost Software License - Version 1.0 - August 17th, 2003
  4. Permission is hereby granted, free of charge, to any person or organization
  5. obtaining a copy of the software and accompanying documentation covered by
  6. this license (the "Software") to use, reproduce, display, distribute,
  7. execute, and transmit the Software, and to prepare derivative works of the
  8. Software, and to permit third-parties to whom the Software is furnished to
  9. do so, all subject to the following:
  10. The copyright notices in the Software and this entire statement, including
  11. the above license grant, this restriction and the following disclaimer,
  12. must be included in all copies of the Software, in whole or in part, and
  13. all derivative works of the Software, unless such copies or derivative
  14. works are solely in the form of machine-executable object code generated by
  15. a source language processor.
  16. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
  19. SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
  20. FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
  21. ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  22. DEALINGS IN THE SOFTWARE.
  23. */
  24. #include <boost/outcome/outcome.hpp>
  25. #include <boost/test/unit_test.hpp>
  26. #include <boost/test/unit_test_monitor.hpp>
  27. #ifndef BOOST_NO_EXCEPTIONS
  28. #ifdef _MSC_VER
  29. #pragma warning(push)
  30. #pragma warning(disable: 4297) // function assumed not to throw an exception but does
  31. #endif
  32. #if defined(__GNUC__) && !defined(__clang__)
  33. #pragma GCC diagnostic push
  34. #pragma GCC diagnostic ignored "-Wterminate"
  35. #endif
  36. template <bool mc, bool ma> struct Throwy
  37. {
  38. int count{0}, inc{0}, id{0};
  39. Throwy() = default;
  40. Throwy(int c, int d, int i = 1) noexcept
  41. : count(c)
  42. , inc(i)
  43. , id(d)
  44. {
  45. }
  46. Throwy(const Throwy &) = delete;
  47. Throwy &operator=(const Throwy &) = delete;
  48. Throwy(Throwy &&o) noexcept(!mc)
  49. : count(o.count - o.inc)
  50. , inc(o.inc)
  51. , id(o.id) // NOLINT
  52. {
  53. if(mc)
  54. {
  55. std::cout << id << " move constructor count = " << count << std::endl;
  56. if(!count)
  57. {
  58. throw std::bad_alloc();
  59. }
  60. }
  61. o.count = 0;
  62. o.inc = 0;
  63. o.id = 0;
  64. }
  65. Throwy &operator=(Throwy &&o) noexcept(!ma)
  66. {
  67. count = o.count - o.inc;
  68. if(ma)
  69. {
  70. std::cout << o.id << " move assignment count = " << count << std::endl;
  71. if(!count)
  72. {
  73. throw std::bad_alloc();
  74. }
  75. }
  76. inc = o.inc;
  77. id = o.id;
  78. o.count = 0;
  79. o.inc = 0;
  80. o.id = 0;
  81. return *this;
  82. }
  83. ~Throwy() = default;
  84. };
  85. #if defined(__GNUC__) && !defined(__clang__)
  86. #pragma GCC diagnostic pop
  87. #endif
  88. #ifdef _MSC_VER
  89. #pragma warning(pop)
  90. #endif
  91. enum class ErrorCode
  92. {
  93. dummy
  94. };
  95. enum class ErrorCode2
  96. {
  97. dummy
  98. };
  99. template <bool mc, bool ma> using resulty1 = BOOST_OUTCOME_V2_NAMESPACE::result<Throwy<mc, ma>, ErrorCode, BOOST_OUTCOME_V2_NAMESPACE::policy::all_narrow>;
  100. template <bool mc, bool ma> using resulty2 = BOOST_OUTCOME_V2_NAMESPACE::result<ErrorCode, Throwy<mc, ma>, BOOST_OUTCOME_V2_NAMESPACE::policy::all_narrow>;
  101. template <bool mc, bool ma> using outcomey1 = BOOST_OUTCOME_V2_NAMESPACE::outcome<ErrorCode, Throwy<mc, ma>, ErrorCode2, BOOST_OUTCOME_V2_NAMESPACE::policy::all_narrow>;
  102. template <bool mc, bool ma> using outcomey2 = BOOST_OUTCOME_V2_NAMESPACE::outcome<ErrorCode, ErrorCode2, Throwy<mc, ma>, BOOST_OUTCOME_V2_NAMESPACE::policy::all_narrow>;
  103. #endif
  104. BOOST_OUTCOME_AUTO_TEST_CASE(works_outcome_swap, "Tests that the outcome swaps as intended")
  105. {
  106. using namespace BOOST_OUTCOME_V2_NAMESPACE;
  107. { // Does swap actually swap?
  108. outcome<std::string> a("niall"), b("douglas");
  109. BOOST_CHECK(a.value() == "niall");
  110. BOOST_CHECK(b.value() == "douglas");
  111. swap(a, b);
  112. BOOST_CHECK(a.value() == "douglas");
  113. BOOST_CHECK(b.value() == "niall");
  114. a = boost::system::errc::not_enough_memory;
  115. swap(a, b);
  116. BOOST_CHECK(a.value() == "niall");
  117. BOOST_CHECK(b.error() == boost::system::errc::not_enough_memory);
  118. BOOST_CHECK(!a.has_lost_consistency());
  119. BOOST_CHECK(!b.has_lost_consistency());
  120. }
  121. #ifndef BOOST_NO_EXCEPTIONS
  122. { // Is noexcept propagated?
  123. using nothrow_t = Throwy<false, false>;
  124. using nothrow = resulty1<false, false>;
  125. static_assert(std::is_nothrow_move_constructible<nothrow_t>::value, "throwy not correct!");
  126. static_assert(std::is_nothrow_move_assignable<nothrow_t>::value, "throwy not correct!");
  127. static_assert(std::is_nothrow_move_constructible<nothrow>::value, "type not correct!");
  128. static_assert(std::is_nothrow_move_assignable<nothrow>::value, "type not correct!");
  129. static_assert(detail::is_nothrow_swappable<nothrow_t>::value, "is_nothrow_swappable is not correct!");
  130. static_assert(noexcept(nothrow(0, 0)), "type has a throwing value constructor!");
  131. nothrow a(1, 78), b(1, 65);
  132. a.swap(b);
  133. static_assert(noexcept(a.swap(b)), "type has a throwing swap!");
  134. }
  135. { // Is noexcept propagated?
  136. using nothrow_t = Throwy<false, false>;
  137. using nothrow = resulty2<false, false>;
  138. static_assert(std::is_nothrow_move_constructible<nothrow_t>::value, "throwy not correct!");
  139. static_assert(std::is_nothrow_move_assignable<nothrow_t>::value, "throwy not correct!");
  140. static_assert(std::is_nothrow_move_constructible<nothrow>::value, "type not correct!");
  141. static_assert(std::is_nothrow_move_assignable<nothrow>::value, "type not correct!");
  142. static_assert(detail::is_nothrow_swappable<nothrow_t>::value, "is_nothrow_swappable is not correct!");
  143. static_assert(noexcept(nothrow(0, 0)), "type has a throwing value constructor!");
  144. nothrow a(1, 78), b(1, 65);
  145. a.swap(b);
  146. static_assert(noexcept(a.swap(b)), "type has a throwing swap!");
  147. }
  148. { // Does swap implement the strong guarantee?
  149. using throwy_t = Throwy<true, true>;
  150. using throwy = resulty1<true, true>;
  151. static_assert(!std::is_nothrow_move_constructible<throwy_t>::value, "throwy not correct!");
  152. static_assert(!std::is_nothrow_move_assignable<throwy_t>::value, "throwy not correct!");
  153. static_assert(!std::is_nothrow_move_constructible<throwy>::value, "type not correct!");
  154. static_assert(!std::is_nothrow_move_assignable<throwy>::value, "type not correct!");
  155. static_assert(!detail::is_nothrow_swappable<throwy_t>::value, "is_nothrow_swappable is not correct!");
  156. {
  157. throwy a(3, 78), b(4, 65);
  158. a.swap(b);
  159. static_assert(!noexcept(a.swap(b)), "type has a non-throwing swap!");
  160. BOOST_CHECK(a.value().id == 65);
  161. BOOST_CHECK(b.value().id == 78);
  162. try
  163. {
  164. a.swap(b); // fails on first assignment
  165. BOOST_REQUIRE(false);
  166. }
  167. catch(const std::bad_alloc & /*unused*/)
  168. {
  169. BOOST_CHECK(a.value().id == 65); // ensure it is perfectly restored
  170. BOOST_CHECK(b.value().id == 78);
  171. }
  172. BOOST_CHECK(!a.has_lost_consistency());
  173. BOOST_CHECK(!b.has_lost_consistency());
  174. }
  175. std::cout << std::endl;
  176. {
  177. throwy a(2, 78), b(3, 65); // fails on second assignment, cannot restore
  178. try
  179. {
  180. a.swap(b);
  181. BOOST_REQUIRE(false);
  182. }
  183. catch(const std::bad_alloc & /*unused*/)
  184. {
  185. BOOST_CHECK(a.has_lost_consistency()); // both must be marked tainted
  186. BOOST_CHECK(b.has_lost_consistency());
  187. }
  188. }
  189. std::cout << std::endl;
  190. }
  191. { // Does swap implement the strong guarantee?
  192. using throwy_t = Throwy<true, true>;
  193. using throwy = resulty2<true, true>;
  194. static_assert(!std::is_nothrow_move_constructible<throwy_t>::value, "throwy not correct!");
  195. static_assert(!std::is_nothrow_move_assignable<throwy_t>::value, "throwy not correct!");
  196. static_assert(!std::is_nothrow_move_constructible<throwy>::value, "type not correct!");
  197. static_assert(!std::is_nothrow_move_assignable<throwy>::value, "type not correct!");
  198. static_assert(!detail::is_nothrow_swappable<throwy_t>::value, "is_nothrow_swappable is not correct!");
  199. {
  200. throwy a(3, 78), b(4, 65);
  201. a.swap(b);
  202. static_assert(!noexcept(a.swap(b)), "type has a non-throwing swap!");
  203. BOOST_CHECK(a.error().id == 65);
  204. BOOST_CHECK(b.error().id == 78);
  205. try
  206. {
  207. a.swap(b); // fails on first assignment
  208. BOOST_REQUIRE(false);
  209. }
  210. catch(const std::bad_alloc & /*unused*/)
  211. {
  212. BOOST_CHECK(a.error().id == 65); // ensure it is perfectly restored
  213. BOOST_CHECK(b.error().id == 78);
  214. }
  215. BOOST_CHECK(!a.has_lost_consistency());
  216. BOOST_CHECK(!b.has_lost_consistency());
  217. }
  218. std::cout << std::endl;
  219. {
  220. throwy a(2, 78), b(3, 65); // fails on second assignment, cannot restore
  221. try
  222. {
  223. a.swap(b);
  224. BOOST_REQUIRE(false);
  225. }
  226. catch(const std::bad_alloc & /*unused*/)
  227. {
  228. BOOST_CHECK(a.has_lost_consistency()); // both must be marked tainted
  229. BOOST_CHECK(b.has_lost_consistency());
  230. }
  231. }
  232. std::cout << std::endl;
  233. }
  234. { // Is noexcept propagated?
  235. using nothrow_t = Throwy<false, false>;
  236. using nothrow = outcomey1<false, false>;
  237. static_assert(std::is_nothrow_move_constructible<nothrow_t>::value, "throwy not correct!");
  238. static_assert(std::is_nothrow_move_assignable<nothrow_t>::value, "throwy not correct!");
  239. static_assert(std::is_nothrow_move_constructible<nothrow>::value, "type not correct!");
  240. static_assert(std::is_nothrow_move_assignable<nothrow>::value, "type not correct!");
  241. static_assert(detail::is_nothrow_swappable<nothrow_t>::value, "is_nothrow_swappable is not correct!");
  242. static_assert(noexcept(nothrow(0, 0)), "type has a throwing value constructor!");
  243. nothrow a(1, 78), b(1, 65);
  244. a.swap(b);
  245. static_assert(noexcept(a.swap(b)), "type has a throwing swap!");
  246. }
  247. { // Is noexcept propagated?
  248. using nothrow_t = Throwy<false, false>;
  249. using nothrow = outcomey1<false, false>;
  250. static_assert(std::is_nothrow_move_constructible<nothrow_t>::value, "throwy not correct!");
  251. static_assert(std::is_nothrow_move_assignable<nothrow_t>::value, "throwy not correct!");
  252. static_assert(std::is_nothrow_move_constructible<nothrow>::value, "type not correct!");
  253. static_assert(std::is_nothrow_move_assignable<nothrow>::value, "type not correct!");
  254. static_assert(detail::is_nothrow_swappable<nothrow_t>::value, "is_nothrow_swappable is not correct!");
  255. static_assert(noexcept(nothrow(0, 0)), "type has a throwing value constructor!");
  256. nothrow a(1, 78), b(1, 65);
  257. a.swap(b);
  258. static_assert(noexcept(a.swap(b)), "type has a throwing swap!");
  259. }
  260. { // Does swap implement the strong guarantee?
  261. using throwy_t = Throwy<true, true>;
  262. using throwy = outcomey1<true, true>;
  263. static_assert(!std::is_nothrow_move_constructible<throwy_t>::value, "throwy not correct!");
  264. static_assert(!std::is_nothrow_move_assignable<throwy_t>::value, "throwy not correct!");
  265. static_assert(!std::is_nothrow_move_constructible<throwy>::value, "type not correct!");
  266. static_assert(!std::is_nothrow_move_assignable<throwy>::value, "type not correct!");
  267. static_assert(!detail::is_nothrow_swappable<throwy_t>::value, "is_nothrow_swappable is not correct!");
  268. {
  269. throwy a(3, 78), b(4, 65);
  270. a.swap(b);
  271. static_assert(!noexcept(a.swap(b)), "type has a non-throwing swap!");
  272. BOOST_CHECK(a.error().id == 65);
  273. BOOST_CHECK(b.error().id == 78);
  274. try
  275. {
  276. a.swap(b); // fails on first assignment
  277. BOOST_REQUIRE(false);
  278. }
  279. catch(const std::bad_alloc & /*unused*/)
  280. {
  281. BOOST_CHECK(a.error().id == 65); // ensure it is perfectly restored
  282. BOOST_CHECK(b.error().id == 78);
  283. }
  284. BOOST_CHECK(!a.has_lost_consistency());
  285. BOOST_CHECK(!b.has_lost_consistency());
  286. }
  287. std::cout << std::endl;
  288. {
  289. throwy a(2, 78), b(3, 65); // fails on second assignment, cannot restore
  290. try
  291. {
  292. a.swap(b);
  293. BOOST_REQUIRE(false);
  294. }
  295. catch(const std::bad_alloc & /*unused*/)
  296. {
  297. BOOST_CHECK(a.has_lost_consistency()); // both must be marked tainted
  298. BOOST_CHECK(b.has_lost_consistency());
  299. }
  300. }
  301. std::cout << std::endl;
  302. }
  303. { // Does swap implement the strong guarantee?
  304. using throwy_t = Throwy<true, true>;
  305. using throwy = outcomey2<true, true>;
  306. static_assert(!std::is_nothrow_move_constructible<throwy_t>::value, "throwy not correct!");
  307. static_assert(!std::is_nothrow_move_assignable<throwy_t>::value, "throwy not correct!");
  308. static_assert(!std::is_nothrow_move_constructible<throwy>::value, "type not correct!");
  309. static_assert(!std::is_nothrow_move_assignable<throwy>::value, "type not correct!");
  310. static_assert(!detail::is_nothrow_swappable<throwy_t>::value, "is_nothrow_swappable is not correct!");
  311. {
  312. throwy a(3, 78), b(4, 65);
  313. a.swap(b);
  314. static_assert(!noexcept(a.swap(b)), "type has a non-throwing swap!");
  315. BOOST_CHECK(a.exception().id == 65);
  316. BOOST_CHECK(b.exception().id == 78);
  317. try
  318. {
  319. a.swap(b); // fails on first assignment
  320. BOOST_REQUIRE(false);
  321. }
  322. catch(const std::bad_alloc & /*unused*/)
  323. {
  324. BOOST_CHECK(a.exception().id == 65); // ensure it is perfectly restored
  325. BOOST_CHECK(b.exception().id == 78);
  326. }
  327. BOOST_CHECK(!a.has_lost_consistency());
  328. BOOST_CHECK(!b.has_lost_consistency());
  329. }
  330. std::cout << std::endl;
  331. {
  332. throwy a(2, 78), b(3, 65); // fails on second assignment, cannot restore
  333. try
  334. {
  335. a.swap(b);
  336. BOOST_REQUIRE(false);
  337. }
  338. catch(const std::bad_alloc & /*unused*/)
  339. {
  340. BOOST_CHECK(a.has_lost_consistency()); // both must be marked tainted
  341. BOOST_CHECK(b.has_lost_consistency());
  342. }
  343. }
  344. std::cout << std::endl;
  345. }
  346. #endif
  347. }