1
2 #ifndef BOOST_CONTRACT_TEST_PUBLIC_FUNCTION_CONTRACTS_HPP_
3 #define BOOST_CONTRACT_TEST_PUBLIC_FUNCTION_CONTRACTS_HPP_
4
5 // Copyright (C) 2008-2018 Lorenzo Caminiti
6 // Distributed under the Boost Software License, Version 1.0 (see accompanying
7 // file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt).
8 // See: http://www.boost.org/doc/libs/release/libs/contract/doc/html/index.html
9
10 // Test public member function subcontracting (also with old and return values).
11
12 #include "../detail/oteststream.hpp"
13 #include "../detail/counter.hpp"
14 #include <boost/contract/public_function.hpp>
15 #include <boost/contract/base_types.hpp>
16 #include <boost/contract/assert.hpp>
17 #include <boost/contract/old.hpp>
18 #include <boost/contract/check.hpp>
19 #include <boost/contract/override.hpp>
20 #include <boost/config.hpp>
21 #include <string>
22
23 boost::contract::test::detail::oteststream out;
24
25 struct s_tag;
26 typedef boost::contract::test::detail::counter<s_tag, std::string> s_type;
27
28 struct except_error {};
29
30 struct result_type {
31 std::string value;
result_typeresult_type32 explicit result_type(std::string const& s) : value(s) {}
33
34 private: // Test non-copyable and non-default-constructible result.
35 result_type();
36 result_type(result_type const&);
37 result_type& operator=(result_type const&);
38 };
39
40 // Test base without additional bases and pure virtual.
41 template<char Id>
42 struct t {
static_invariantt43 static void static_invariant() { out << Id << "::static_inv" << std::endl; }
44
invariantt45 void invariant() const {
46 out << Id << "::inv" << std::endl;
47 BOOST_CONTRACT_ASSERT(z.value != "");
48 }
49
50 struct z_tag;
51 typedef boost::contract::test::detail::counter<z_tag, std::string> z_type;
52 z_type z;
53
tt54 t() { z.value.push_back(Id); }
55
56 virtual result_type& f(s_type& s, boost::contract::virtual_* v = 0) = 0;
57 };
58
59 template<char Id> // Requires: Only pass lower case Id so it'll never be 'X'.
f(s_type & s,boost::contract::virtual_ * v)60 result_type& t<Id>::f(s_type& s, boost::contract::virtual_* v) {
61 std::ostringstream r; r << "none-" << Id;
62 static result_type result(r.str());
63 boost::contract::old_ptr<z_type> old_z =
64 BOOST_CONTRACT_OLDOF(v, z_type::eval(z));
65 boost::contract::old_ptr<s_type> old_s;
66 boost::contract::check c = boost::contract::public_function(v, result, this)
67 .precondition([&] {
68 out << Id << "::f::pre" << std::endl;
69 BOOST_CONTRACT_ASSERT(s.value[0] == Id || s.value[0] == 'X');
70 })
71 .old([&] {
72 out << Id << "::f::old" << std::endl;
73 old_s = BOOST_CONTRACT_OLDOF(v, s_type::eval(s));
74 })
75 .postcondition([&] (result_type const& result) {
76 out << Id << "::f::post" << std::endl;
77 BOOST_CONTRACT_ASSERT(z.value == old_z->value + old_s->value);
78 BOOST_CONTRACT_ASSERT(s.value.find(old_z->value) !=
79 std::string::npos);
80 BOOST_CONTRACT_ASSERT(result.value == old_s->value);
81 })
82 .except([&] {
83 out << Id << "::f::except" << std::endl;
84 BOOST_CONTRACT_ASSERT(z.value == old_z->value);
85 BOOST_CONTRACT_ASSERT(s.value == old_s->value);
86 })
87 ;
88 out << "t<" << Id << ">::f::body" << std::endl;
89 if(s.value == "X") throw except_error();
90 return result;
91 }
92
93 // Test base with other bases, multiple inheritance, and no subcontracting from
94 // protected and private bases (even if fully contracted).
95 struct c
96 #define BASES public t<'d'>, protected t<'p'>, private t<'q'>, public t<'e'>
97 : BASES
98 {
99 typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types;
100 #undef BASES
101
static_invariantc102 static void static_invariant() { out << "c::static_inv" << std::endl; }
103
invariantc104 void invariant() const {
105 out << "c::inv" << std::endl;
106 BOOST_CONTRACT_ASSERT(y.value != "");
107 }
108
109 struct y_tag;
110 typedef boost::contract::test::detail::counter<y_tag, std::string> y_type;
111 y_type y;
112
cc113 c() { y.value = "c"; }
114
fc115 virtual result_type& f(s_type& s, boost::contract::virtual_* v = 0)
116 /* override */ {
117 static result_type result("none-c");
118 boost::contract::old_ptr<y_type> old_y =
119 BOOST_CONTRACT_OLDOF(v, y_type::eval(y));
120 boost::contract::old_ptr<s_type> old_s;
121 boost::contract::check c = boost::contract::public_function<
122 override_f>(v, result, &c::f, this, s)
123 .precondition([&] {
124 out << "c::f::pre" << std::endl;
125 BOOST_CONTRACT_ASSERT(s.value == "C" || s.value == "X");
126 })
127 .old([&] {
128 out << "c::f::old" << std::endl;
129 old_s = BOOST_CONTRACT_OLDOF(v, s_type::eval(s));
130 })
131 .postcondition([&] (result_type const& result) {
132 out << "c::f::post" << std::endl;
133 BOOST_CONTRACT_ASSERT(y.value == old_y->value + old_s->value);
134 BOOST_CONTRACT_ASSERT(s.value.find(old_y->value) !=
135 std::string::npos);
136 BOOST_CONTRACT_ASSERT(result.value == old_s->value);
137 })
138 .except([&] {
139 out << "c::f::except" << std::endl;
140 BOOST_CONTRACT_ASSERT(y.value == old_y->value);
141 BOOST_CONTRACT_ASSERT(s.value == old_s->value);
142 })
143 ;
144
145 out << "c::f::body" << std::endl;
146 if(s.value == "X") throw except_error();
147 std::string save_s = s.value;
148
149 std::string save = y.value;
150 y.value += save_s;
151 s.value = save;
152
153 save = t<'d'>::z.value;
154 t<'d'>::z.value += save_s;
155 s.value += save;
156
157 save = t<'e'>::z.value;
158 t<'e'>::z.value += save_s;
159 s.value += save;
160
161 result.value = save_s;
162 return result;
163 }
164 BOOST_CONTRACT_OVERRIDE(f)
165 };
166
167 // Test no subcontracting from not (fully) contracted base.
168 struct b {
static_invariantb169 static void static_invariant() { out << "b::static_inv" << std::endl; }
invariantb170 void invariant() const { out << "b::inv" << std::endl; }
171
~bb172 virtual ~b() {}
173
174 // No contract (no virtual_ so this is not actually overridden by a::f).
fb175 virtual result_type& f(s_type& s) {
176 static result_type result("none-b");
177 out << "b::f::body" << std::endl;
178 result.value = s.value;
179 return result;
180 }
181 };
182
183 // Test public function with both non-contracted and contracted bases.
184 struct a
185 #define BASES public b, public c
186 : BASES
187 {
188 typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types;
189 #undef BASES
190
static_invarianta191 static void static_invariant() { out << "a::static_inv" << std::endl; }
192
invarianta193 void invariant() const {
194 out << "a::inv" << std::endl;
195 BOOST_CONTRACT_ASSERT(x.value != "");
196 }
197
198 struct x_tag;
199 typedef boost::contract::test::detail::counter<x_tag, std::string> x_type;
200 x_type x;
201
aa202 a() { x.value = "a"; }
203
204 #if defined(BOOST_GCC)
205 #pragma GCC diagnostic push
206 #pragma GCC diagnostic ignored "-Woverloaded-virtual" // For a::f.
207 #elif defined(BOOST_CLANG)
208 #pragma clang diagnostic push
209 #pragma clang diagnostic ignored "-Woverloaded-virtual" // For a::f.
210 #endif
211
212 // Must use virtual_ even if no longer decl virtual for correct overloading.
213 // NOTE: This intentionally hides but does not override `b::f` (it overrides
214 // `c::f` instead). This generates warnings on some compilers (Clang, etc.).
fa215 result_type& f(s_type& s, boost::contract::virtual_* v = 0)
216 /* override */ {
217 static result_type result("none-a");
218 boost::contract::old_ptr<x_type> old_x =
219 BOOST_CONTRACT_OLDOF(v, x_type::eval(x));
220 boost::contract::old_ptr<s_type> old_s;
221 boost::contract::check c = boost::contract::public_function<
222 override_f>(v, result, &a::f, this, s)
223 .precondition([&] {
224 out << "a::f::pre" << std::endl;
225 BOOST_CONTRACT_ASSERT(s.value == "A" || s.value == "X");
226 })
227 .old([&] {
228 out << "a::f::old" << std::endl;
229 old_s = BOOST_CONTRACT_OLDOF(v, s_type::eval(s));
230 })
231 .postcondition([&] (result_type const& result) {
232 out << "a::f::post" << std::endl;
233 BOOST_CONTRACT_ASSERT(x.value == old_x->value + old_s->value);
234 BOOST_CONTRACT_ASSERT(s.value.find(old_x->value) !=
235 std::string::npos);
236 BOOST_CONTRACT_ASSERT(result.value == old_s->value);
237 })
238 .except([&] {
239 out << "a::f::except" << std::endl;
240 BOOST_CONTRACT_ASSERT(x.value == old_x->value);
241 BOOST_CONTRACT_ASSERT(s.value == old_s->value);
242 })
243 ;
244
245 out << "a::f::body" << std::endl;
246 if(s.value == "X") throw except_error();
247 std::string save_s = s.value;
248
249 std::string save = x.value;
250 x.value += save_s;
251 s.value = save;
252
253 save = y.value;
254 y.value += save_s;
255 s.value += save;
256
257 save = t<'d'>::z.value;
258 t<'d'>::z.value += save_s;
259 s.value += save;
260
261 save = t<'e'>::z.value;
262 t<'e'>::z.value += save_s;
263 s.value += save;
264
265 result.value = save_s;
266 return result;
267 }
268 BOOST_CONTRACT_OVERRIDE(f)
269
270 #if defined(BOOST_GCC)
271 #pragma GCC diagnostic pop
272 #elif defined(BOOST_CLANG)
273 #pragma clang diagnostic pop
274 #endif
275 };
276
277 #endif // #include guard
278
279