1
2 // Copyright (C) 2008-2018 Lorenzo Caminiti
3 // Distributed under the Boost Software License, Version 1.0 (see accompanying
4 // file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt).
5 // See: http://www.boost.org/doc/libs/release/libs/contract/doc/html/index.html
6
7 // Test destructor subcontracting.
8
9 #include "../detail/oteststream.hpp"
10 #include "../detail/counter.hpp"
11 #include <boost/contract/destructor.hpp>
12 #include <boost/contract/base_types.hpp>
13 #include <boost/contract/assert.hpp>
14 #include <boost/contract/old.hpp>
15 #include <boost/contract/check.hpp>
16 #include <boost/preprocessor/control/iif.hpp>
17 #include <boost/detail/lightweight_test.hpp>
18 #include <sstream>
19
20 boost::contract::test::detail::oteststream out;
21
22 template<char Id>
23 struct t {
static_invariantt24 static void static_invariant() {
25 out << Id << "::static_inv" << std::endl;
26 BOOST_CONTRACT_ASSERT(l.value >= 0);
27 }
28
invariantt29 void invariant() const {
30 out << Id << "::inv" << std::endl;
31 BOOST_CONTRACT_ASSERT(k_ < 0);
32 }
33
34 struct l_tag;
35 typedef boost::contract::test::detail::counter<l_tag, int> l_type;
36 static l_type l;
37
tt38 explicit t() : k_(-1) { ++l.value; }
39
~tt40 virtual ~t() {
41 boost::contract::old_ptr<l_type> old_l;
42 boost::contract::check c = boost::contract::destructor(this)
43 .old([&] {
44 out << Id << "::dtor::old" << std::endl;
45 old_l = BOOST_CONTRACT_OLDOF(l_type::eval(l));
46 })
47 .postcondition([&old_l] {
48 out << Id << "::dtor::post" << std::endl;
49 BOOST_CONTRACT_ASSERT(t<Id>::l.value == old_l->value - 1);
50 })
51 ;
52 out << Id << "::dtor::body" << std::endl;
53 --l.value;
54 }
55
56 private:
57 int k_;
58 };
59 template<char Id> typename t<Id>::l_type t<Id>::l;
60
61 // Test deep inheritance (2 vertical levels), multiple inheritance (4
62 // horizontal levels), and that all public/protected/private part of
63 // subcontracting for destructors (not just public, because all access levels
64 // are part of C++ object destruction mechanism).
65 struct c
66 #define BASES public t<'d'>, protected t<'p'>, private t<'q'>, public t<'e'>
67 : BASES
68 {
69 typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types;
70 #undef BASES
71
static_invariantc72 static void static_invariant() {
73 out << "c::static_inv" << std::endl;
74 BOOST_CONTRACT_ASSERT(m.value >= 0);
75 }
76
invariantc77 void invariant() const {
78 out << "c::inv" << std::endl;
79 BOOST_CONTRACT_ASSERT(j_ < 0);
80 }
81
82 struct m_tag;
83 typedef boost::contract::test::detail::counter<m_tag, int> m_type;
84 static m_type m;
85
cc86 explicit c() : j_(-1) { ++m.value; }
87
~cc88 virtual ~c() {
89 boost::contract::old_ptr<m_type> old_m =
90 BOOST_CONTRACT_OLDOF(m_type::eval(m));
91 boost::contract::check c = boost::contract::destructor(this)
92 .old([] {
93 out << "c::dtor::old" << std::endl;
94 // Test old-of assignment above instead.
95 })
96 .postcondition([&old_m] {
97 out << "c::dtor::post" << std::endl;
98 BOOST_CONTRACT_ASSERT(c::m.value == old_m->value - 1);
99 })
100 ;
101 out << "c::dtor::body" << std::endl;
102 --m.value;
103 }
104
105 private:
106 int j_;
107 };
108 c::m_type c::m;
109
110 // Test not (fully) contracted base is not part of destructor subcontracting.
111 struct b {
static_invariantb112 static void static_invariant() { out << "b::static_inv" << std::endl; }
invariantb113 void invariant() const { out << "b::inv" << std::endl; }
114
bb115 explicit b() {}
~bb116 virtual ~b() {} // No contract.
117 };
118
119 struct a_n_tag; // Global decl so visible in MSVC10 lambdas.
120 typedef boost::contract::test::detail::counter<a_n_tag, int> a_n_type;
121
122 // Test destructor with both non-contracted and contracted bases.
123 struct a
124 #define BASES public b, public c
125 : BASES
126 {
127 typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types;
128 #undef BASES
129
static_invarianta130 static void static_invariant() {
131 out << "a::static_inv" << std::endl;
132 BOOST_CONTRACT_ASSERT(n.value >= 0);
133 }
134
invarianta135 void invariant() const {
136 out << "a::inv" << std::endl;
137 BOOST_CONTRACT_ASSERT(i_ < 0);
138 }
139
140 static a_n_type n;
141
aa142 explicit a() : i_(-1) {
143 ++i_; --i_; // To avoid a warning when all contracts off.
144 ++n.value;
145 }
146
~aa147 virtual ~a() {
148 boost::contract::old_ptr<a_n_type> old_n;
149 boost::contract::check c = boost::contract::destructor(this)
150 .old([&] {
151 out << "a::dtor::old" << std::endl;
152 old_n = BOOST_CONTRACT_OLDOF(a_n_type::eval(n));
153 })
154 .postcondition([&old_n] {
155 out << "a::dtor::post" << std::endl;
156 BOOST_CONTRACT_ASSERT(a::n.value == old_n->value - 1);
157 })
158 ;
159 out << "a::dtor::body" << std::endl;
160 --n.value;
161 }
162
163 private:
164 int i_;
165 };
166 a_n_type a::n;
167
main()168 int main() {
169 std::ostringstream ok;
170
171 {
172 a aa;
173 out.str("");
174 } // Call aa's destructor.
175 ok.str(""); ok
176 #ifndef BOOST_CONTRACT_NO_ENTRY_INVARIANTS
177 << "a::static_inv" << std::endl
178 << "a::inv" << std::endl
179 #endif
180 #ifndef BOOST_CONTRACT_NO_OLDS
181 << "a::dtor::old" << std::endl
182 #endif
183 << "a::dtor::body" << std::endl
184 // Test static inv, but not const inv, checked after destructor body.
185 #ifndef BOOST_CONTRACT_NO_EXIT_INVARIANTS
186 << "a::static_inv" << std::endl
187 #endif
188 #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS
189 << "a::dtor::post" << std::endl
190 #endif
191
192 #ifndef BOOST_CONTRACT_NO_ENTRY_INVARIANTS
193 << "c::static_inv" << std::endl
194 << "c::inv" << std::endl
195 #endif
196 #ifndef BOOST_CONTRACT_NO_OLDS
197 << "c::dtor::old" << std::endl
198 #endif
199 << "c::dtor::body" << std::endl
200 #ifndef BOOST_CONTRACT_NO_EXIT_INVARIANTS
201 << "c::static_inv" << std::endl
202 #endif
203 #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS
204 << "c::dtor::post" << std::endl
205 #endif
206
207 #ifndef BOOST_CONTRACT_NO_ENTRY_INVARIANTS
208 << "e::static_inv" << std::endl
209 << "e::inv" << std::endl
210 #endif
211 #ifndef BOOST_CONTRACT_NO_OLDS
212 << "e::dtor::old" << std::endl
213 #endif
214 << "e::dtor::body" << std::endl
215 #ifndef BOOST_CONTRACT_NO_EXIT_INVARIANTS
216 << "e::static_inv" << std::endl
217 #endif
218 #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS
219 << "e::dtor::post" << std::endl
220 #endif
221
222 // Test check also private bases (because part of C++ destruction).
223 #ifndef BOOST_CONTRACT_NO_ENTRY_INVARIANTS
224 << "q::static_inv" << std::endl
225 << "q::inv" << std::endl
226 #endif
227 #ifndef BOOST_CONTRACT_NO_OLDS
228 << "q::dtor::old" << std::endl
229 #endif
230 << "q::dtor::body" << std::endl
231 #ifndef BOOST_CONTRACT_NO_EXIT_INVARIANTS
232 << "q::static_inv" << std::endl
233 #endif
234 #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS
235 << "q::dtor::post" << std::endl
236 #endif
237
238 // Test check also protected bases (because part of C++ destruction).
239 #ifndef BOOST_CONTRACT_NO_ENTRY_INVARIANTS
240 << "p::static_inv" << std::endl
241 << "p::inv" << std::endl
242 #endif
243 #ifndef BOOST_CONTRACT_NO_OLDS
244 << "p::dtor::old" << std::endl
245 #endif
246 << "p::dtor::body" << std::endl
247 #ifndef BOOST_CONTRACT_NO_EXIT_INVARIANTS
248 << "p::static_inv" << std::endl
249 #endif
250 #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS
251 << "p::dtor::post" << std::endl
252 #endif
253
254 #ifndef BOOST_CONTRACT_NO_ENTRY_INVARIANTS
255 << "d::static_inv" << std::endl
256 << "d::inv" << std::endl
257 #endif
258 #ifndef BOOST_CONTRACT_NO_OLDS
259 << "d::dtor::old" << std::endl
260 #endif
261 << "d::dtor::body" << std::endl
262 #ifndef BOOST_CONTRACT_NO_EXIT_INVARIANTS
263 << "d::static_inv" << std::endl
264 #endif
265 #ifndef BOOST_CONTRACT_NO_POSTCONDITIONS
266 << "d::dtor::post" << std::endl
267 #endif
268 ;
269 BOOST_TEST(out.eq(ok.str()));
270
271 #ifndef BOOST_CONTRACT_NO_OLDS
272 #define BOOST_CONTRACT_TEST_old 1u
273 #else
274 #define BOOST_CONTRACT_TEST_old 0u
275 #endif
276
277 // Followings destroy only copies (actual objects are static data members).
278
279 BOOST_TEST_EQ(a_n_type::copies(), BOOST_CONTRACT_TEST_old);
280 BOOST_TEST_EQ(a_n_type::evals(), BOOST_CONTRACT_TEST_old);
281 BOOST_TEST_EQ(a_n_type::copies(), a_n_type::dtors()); // No leak.
282
283 BOOST_TEST_EQ(c::m_type::copies(), BOOST_CONTRACT_TEST_old);
284 BOOST_TEST_EQ(c::m_type::evals(), BOOST_CONTRACT_TEST_old);
285 BOOST_TEST_EQ(c::m_type::copies(), c::m_type::dtors()); // No leak.
286
287 BOOST_TEST_EQ(t<'d'>::l_type::copies(), BOOST_CONTRACT_TEST_old);
288 BOOST_TEST_EQ(t<'d'>::l_type::evals(), BOOST_CONTRACT_TEST_old);
289 BOOST_TEST_EQ(t<'d'>::l_type::copies(), t<'d'>::l_type::dtors()); // No leak
290
291 BOOST_TEST_EQ(t<'p'>::l_type::copies(), BOOST_CONTRACT_TEST_old);
292 BOOST_TEST_EQ(t<'p'>::l_type::evals(), BOOST_CONTRACT_TEST_old);
293 BOOST_TEST_EQ(t<'p'>::l_type::copies(), t<'p'>::l_type::dtors()); // No leak
294
295 BOOST_TEST_EQ(t<'q'>::l_type::copies(), BOOST_CONTRACT_TEST_old);
296 BOOST_TEST_EQ(t<'q'>::l_type::evals(), BOOST_CONTRACT_TEST_old);
297 BOOST_TEST_EQ(t<'q'>::l_type::copies(), t<'q'>::l_type::dtors()); // No leak
298
299 BOOST_TEST_EQ(t<'e'>::l_type::copies(), BOOST_CONTRACT_TEST_old);
300 BOOST_TEST_EQ(t<'e'>::l_type::evals(), BOOST_CONTRACT_TEST_old);
301 BOOST_TEST_EQ(t<'e'>::l_type::copies(), t<'e'>::l_type::dtors()); // No leak
302
303 #undef BOOST_CONTRACT_TEST_old
304 return boost::report_errors();
305 }
306
307