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