1 | // Boost.Signals library |
2 | |
3 | // Copyright Frank Mori Hess 2008-2009. |
4 | // Copyright Douglas Gregor 2001-2003. |
5 | // |
6 | // Use, modification and |
7 | // distribution is subject to the Boost Software License, Version |
8 | // 1.0. (See accompanying file LICENSE_1_0.txt or copy at |
9 | // http://www.boost.org/LICENSE_1_0.txt) |
10 | |
11 | // For more information, see http://www.boost.org |
12 | |
13 | #include <boost/bind/bind.hpp> |
14 | #include <boost/optional.hpp> |
15 | #include <boost/signals2.hpp> |
16 | #define BOOST_TEST_MODULE signal_test |
17 | #include <boost/test/included/unit_test.hpp> |
18 | #include <functional> |
19 | #include <iostream> |
20 | #include <typeinfo> |
21 | |
22 | using namespace boost::placeholders; |
23 | |
24 | template<typename T> |
25 | struct max_or_default { |
26 | typedef T result_type; |
27 | template<typename InputIterator> |
28 | typename InputIterator::value_type |
29 | operator()(InputIterator first, InputIterator last) const |
30 | { |
31 | boost::optional<T> max; |
32 | for (; first != last; ++first) |
33 | { |
34 | try |
35 | { |
36 | if(!max) max = *first; |
37 | else max = (*first > max.get())? *first : max; |
38 | } |
39 | catch(const boost::bad_weak_ptr &) |
40 | {} |
41 | } |
42 | if(max) return max.get(); |
43 | return T(); |
44 | } |
45 | }; |
46 | |
47 | struct make_int { |
48 | make_int(int n, int cn) : N(n), CN(cn) {} |
49 | |
50 | int operator()() { return N; } |
51 | int operator()() const { return CN; } |
52 | |
53 | int N; |
54 | int CN; |
55 | }; |
56 | |
57 | template<int N> |
58 | struct make_increasing_int { |
59 | make_increasing_int() : n(N) {} |
60 | |
61 | int operator()() const { return n++; } |
62 | |
63 | mutable int n; |
64 | }; |
65 | |
66 | static void |
67 | test_zero_args() |
68 | { |
69 | make_int i42(42, 41); |
70 | make_int i2(2, 1); |
71 | make_int i72(72, 71); |
72 | make_int i63(63, 63); |
73 | make_int i62(62, 61); |
74 | |
75 | { |
76 | boost::signals2::signal<int (), max_or_default<int> > s0; |
77 | |
78 | std::cout << "sizeof(signal) = " << sizeof(s0) << std::endl; |
79 | boost::signals2::connection c2 = s0.connect(slot: i2); |
80 | boost::signals2::connection c72 = s0.connect(group: 72, slot: i72); |
81 | boost::signals2::connection c62 = s0.connect(group: 60, slot: i62); |
82 | boost::signals2::connection c42 = s0.connect(slot: i42); |
83 | |
84 | BOOST_CHECK(s0() == 72); |
85 | |
86 | s0.disconnect(group: 72); |
87 | BOOST_CHECK(s0() == 62); |
88 | |
89 | c72.disconnect(); // Double-disconnect should be safe |
90 | BOOST_CHECK(s0() == 62); |
91 | |
92 | s0.disconnect(group: 72); // Triple-disconect should be safe |
93 | BOOST_CHECK(s0() == 62); |
94 | |
95 | // Also connect 63 in the same group as 62 |
96 | s0.connect(group: 60, slot: i63); |
97 | BOOST_CHECK(s0() == 63); |
98 | |
99 | // Disconnect all of the 60's |
100 | s0.disconnect(group: 60); |
101 | BOOST_CHECK(s0() == 42); |
102 | |
103 | c42.disconnect(); |
104 | BOOST_CHECK(s0() == 2); |
105 | |
106 | c2.disconnect(); |
107 | BOOST_CHECK(s0() == 0); |
108 | } |
109 | |
110 | { |
111 | boost::signals2::signal<int (), max_or_default<int> > s0; |
112 | boost::signals2::connection c2 = s0.connect(slot: i2); |
113 | boost::signals2::connection c72 = s0.connect(slot: i72); |
114 | boost::signals2::connection c62 = s0.connect(slot: i62); |
115 | boost::signals2::connection c42 = s0.connect(slot: i42); |
116 | |
117 | const boost::signals2::signal<int (), max_or_default<int> >& cs0 = s0; |
118 | BOOST_CHECK(cs0() == 72); |
119 | } |
120 | |
121 | { |
122 | make_increasing_int<7> i7; |
123 | make_increasing_int<10> i10; |
124 | |
125 | boost::signals2::signal<int (), max_or_default<int> > s0; |
126 | boost::signals2::connection c7 = s0.connect(slot: i7); |
127 | boost::signals2::connection c10 = s0.connect(slot: i10); |
128 | |
129 | BOOST_CHECK(s0() == 10); |
130 | BOOST_CHECK(s0() == 11); |
131 | } |
132 | } |
133 | |
134 | static void |
135 | test_one_arg() |
136 | { |
137 | boost::signals2::signal<int (int value), max_or_default<int> > s1; |
138 | |
139 | s1.connect(slot: std::negate<int>()); |
140 | s1.connect(slot: boost::bind(f: std::multiplies<int>(), a: 2, a: _1)); |
141 | |
142 | BOOST_CHECK(s1(1) == 2); |
143 | BOOST_CHECK(s1(-1) == 1); |
144 | } |
145 | |
146 | static void |
147 | test_signal_signal_connect() |
148 | { |
149 | typedef boost::signals2::signal<int (int value), max_or_default<int> > signal_type; |
150 | signal_type s1; |
151 | |
152 | s1.connect(slot: std::negate<int>()); |
153 | |
154 | BOOST_CHECK(s1(3) == -3); |
155 | |
156 | { |
157 | signal_type s2; |
158 | s1.connect(slot: s2); |
159 | s2.connect(slot: boost::bind(f: std::multiplies<int>(), a: 2, a: _1)); |
160 | s2.connect(slot: boost::bind(f: std::multiplies<int>(), a: -3, a: _1)); |
161 | |
162 | BOOST_CHECK(s2(-3) == 9); |
163 | BOOST_CHECK(s1(3) == 6); |
164 | |
165 | // disconnect by slot |
166 | s1.disconnect(slot: s2); |
167 | BOOST_CHECK(s1(3) == -3); |
168 | |
169 | // disconnect by reference wrapped slot |
170 | s1.connect(slot: s2); |
171 | BOOST_CHECK(s1(3) == 6); |
172 | s1.disconnect(slot: boost::ref(t&: s2)); |
173 | BOOST_CHECK(s1(3) == -3); |
174 | |
175 | // reconnect s2 to test auto-disconnect on destruction |
176 | s1.connect(slot: s2); |
177 | BOOST_CHECK(s1(3) == 6); |
178 | } // s2 goes out of scope and disconnects |
179 | BOOST_CHECK(s1(3) == -3); |
180 | } |
181 | |
182 | template<typename ResultType> |
183 | ResultType disconnecting_slot(const boost::signals2::connection &conn, int) |
184 | { |
185 | conn.disconnect(); |
186 | return ResultType(); |
187 | } |
188 | |
189 | #ifdef BOOST_NO_VOID_RETURNS |
190 | template<> |
191 | void disconnecting_slot<void>(const boost::signals2::connection &conn, int) |
192 | { |
193 | conn.disconnect(); |
194 | return; |
195 | } |
196 | #endif |
197 | |
198 | template<typename ResultType> |
199 | void test_extended_slot() |
200 | { |
201 | { |
202 | typedef boost::signals2::signal<ResultType (int)> signal_type; |
203 | typedef typename signal_type::extended_slot_type slot_type; |
204 | signal_type sig; |
205 | // attempting to work around msvc 7.1 bug by explicitly assigning to a function pointer |
206 | ResultType (*fp)(const boost::signals2::connection &conn, int) = &disconnecting_slot<ResultType>; |
207 | slot_type myslot(fp); |
208 | sig.connect_extended(myslot); |
209 | BOOST_CHECK(sig.num_slots() == 1); |
210 | sig(0); |
211 | BOOST_CHECK(sig.num_slots() == 0); |
212 | } |
213 | { // test 0 arg signal |
214 | typedef boost::signals2::signal<ResultType ()> signal_type; |
215 | typedef typename signal_type::extended_slot_type slot_type; |
216 | signal_type sig; |
217 | // attempting to work around msvc 7.1 bug by explicitly assigning to a function pointer |
218 | ResultType (*fp)(const boost::signals2::connection &conn, int) = &disconnecting_slot<ResultType>; |
219 | slot_type myslot(fp, _1, 0); |
220 | sig.connect_extended(myslot); |
221 | BOOST_CHECK(sig.num_slots() == 1); |
222 | sig(); |
223 | BOOST_CHECK(sig.num_slots() == 0); |
224 | } |
225 | // test disconnection by slot |
226 | { |
227 | typedef boost::signals2::signal<ResultType (int)> signal_type; |
228 | typedef typename signal_type::extended_slot_type slot_type; |
229 | signal_type sig; |
230 | // attempting to work around msvc 7.1 bug by explicitly assigning to a function pointer |
231 | ResultType (*fp)(const boost::signals2::connection &conn, int) = &disconnecting_slot<ResultType>; |
232 | slot_type myslot(fp); |
233 | sig.connect_extended(myslot); |
234 | BOOST_CHECK(sig.num_slots() == 1); |
235 | sig.disconnect(fp); |
236 | BOOST_CHECK(sig.num_slots() == 0); |
237 | } |
238 | } |
239 | |
240 | void increment_arg(int &value) |
241 | { |
242 | ++value; |
243 | } |
244 | |
245 | static void |
246 | test_reference_args() |
247 | { |
248 | typedef boost::signals2::signal<void (int &)> signal_type; |
249 | signal_type s1; |
250 | |
251 | s1.connect(slot: &increment_arg); |
252 | int value = 0; |
253 | s1(value); |
254 | BOOST_CHECK(value == 1); |
255 | } |
256 | |
257 | static void |
258 | test_typedefs_etc() |
259 | { |
260 | typedef boost::signals2::signal<int (double, long)> signal_type; |
261 | typedef signal_type::slot_type slot_type; |
262 | |
263 | BOOST_CHECK(typeid(signal_type::slot_result_type) == typeid(int)); |
264 | BOOST_CHECK(typeid(signal_type::result_type) == typeid(boost::optional<int>)); |
265 | BOOST_CHECK(typeid(signal_type::arg<0>::type) == typeid(double)); |
266 | BOOST_CHECK(typeid(signal_type::arg<1>::type) == typeid(long)); |
267 | BOOST_CHECK(typeid(signal_type::arg<0>::type) == typeid(signal_type::first_argument_type)); |
268 | BOOST_CHECK(typeid(signal_type::arg<1>::type) == typeid(signal_type::second_argument_type)); |
269 | BOOST_CHECK(typeid(signal_type::signature_type) == typeid(int (double, long))); |
270 | BOOST_CHECK(signal_type::arity == 2); |
271 | |
272 | BOOST_CHECK(typeid(slot_type::result_type) == typeid(signal_type::slot_result_type)); |
273 | BOOST_CHECK(typeid(slot_type::arg<0>::type) == typeid(signal_type::arg<0>::type)); |
274 | BOOST_CHECK(typeid(slot_type::arg<1>::type) == typeid(signal_type::arg<1>::type)); |
275 | BOOST_CHECK(typeid(slot_type::arg<0>::type) == typeid(slot_type::first_argument_type)); |
276 | BOOST_CHECK(typeid(slot_type::arg<1>::type) == typeid(slot_type::second_argument_type)); |
277 | BOOST_CHECK(typeid(slot_type::signature_type) == typeid(signal_type::signature_type)); |
278 | BOOST_CHECK(slot_type::arity == signal_type::arity); |
279 | |
280 | typedef boost::signals2::signal<void (short)> unary_signal_type; |
281 | BOOST_CHECK(typeid(unary_signal_type::slot_result_type) == typeid(void)); |
282 | BOOST_CHECK(typeid(unary_signal_type::argument_type) == typeid(short)); |
283 | BOOST_CHECK(typeid(unary_signal_type::slot_type::argument_type) == typeid(short)); |
284 | } |
285 | |
286 | class dummy_combiner |
287 | { |
288 | public: |
289 | typedef int result_type; |
290 | |
291 | dummy_combiner(result_type return_value): _return_value(return_value) |
292 | {} |
293 | template<typename SlotIterator> |
294 | result_type operator()(SlotIterator, SlotIterator) |
295 | { |
296 | return _return_value; |
297 | } |
298 | private: |
299 | result_type _return_value; |
300 | }; |
301 | |
302 | static void |
303 | test_set_combiner() |
304 | { |
305 | typedef boost::signals2::signal<int (), dummy_combiner> signal_type; |
306 | signal_type sig(dummy_combiner(0)); |
307 | BOOST_CHECK(sig() == 0); |
308 | BOOST_CHECK(sig.combiner()(0,0) == 0); |
309 | sig.set_combiner(dummy_combiner(1)); |
310 | BOOST_CHECK(sig() == 1); |
311 | BOOST_CHECK(sig.combiner()(0,0) == 1); |
312 | } |
313 | |
314 | static void |
315 | test_swap() |
316 | { |
317 | typedef boost::signals2::signal<int (), dummy_combiner> signal_type; |
318 | signal_type sig1(dummy_combiner(1)); |
319 | BOOST_CHECK(sig1() == 1); |
320 | signal_type sig2(dummy_combiner(2)); |
321 | BOOST_CHECK(sig2() == 2); |
322 | |
323 | sig1.swap(other&: sig2); |
324 | BOOST_CHECK(sig1() == 2); |
325 | BOOST_CHECK(sig2() == 1); |
326 | |
327 | using std::swap; |
328 | swap(sig1, sig2); |
329 | BOOST_CHECK(sig1() == 1); |
330 | BOOST_CHECK(sig2() == 2); |
331 | } |
332 | |
333 | void test_move() |
334 | { |
335 | #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) |
336 | typedef boost::signals2::signal<int (), dummy_combiner> signal_type; |
337 | signal_type sig1(dummy_combiner(1)); |
338 | BOOST_CHECK(sig1() == 1); |
339 | signal_type sig2(dummy_combiner(2)); |
340 | BOOST_CHECK(sig2() == 2); |
341 | |
342 | sig1 = std::move(sig2); |
343 | BOOST_CHECK(sig1() == 2); |
344 | |
345 | signal_type sig3(std::move(sig1)); |
346 | BOOST_CHECK(sig3() == 2); |
347 | #endif // !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) |
348 | } |
349 | |
350 | BOOST_AUTO_TEST_CASE(test_main) |
351 | { |
352 | test_zero_args(); |
353 | test_one_arg(); |
354 | test_signal_signal_connect(); |
355 | test_extended_slot<void>(); |
356 | test_extended_slot<int>(); |
357 | test_reference_args(); |
358 | test_typedefs_etc(); |
359 | test_set_combiner(); |
360 | test_swap(); |
361 | test_move(); |
362 | } |
363 | |