1//
2// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
3//
4// Distributed under the Boost Software License, Version 1.0. (See accompanying
5// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6//
7// Official repository: https://github.com/boostorg/beast
8//
9
10// Test that header file is self-contained.
11#include <boost/beast/core/bind_handler.hpp>
12
13#include "test_handler.hpp"
14
15#include <boost/beast/core/detail/type_traits.hpp>
16#include <boost/beast/_experimental/unit_test/suite.hpp>
17#include <boost/beast/_experimental/test/stream.hpp>
18#include <boost/asio/io_context.hpp>
19#include <boost/asio/bind_executor.hpp>
20#include <boost/asio/executor_work_guard.hpp>
21#include <boost/asio/dispatch.hpp>
22#include <boost/asio/defer.hpp>
23#include <boost/asio/post.hpp>
24#include <boost/asio/strand.hpp>
25#include <boost/bind/placeholders.hpp>
26#include <boost/bind/std_placeholders.hpp>
27#include <boost/core/exchange.hpp>
28#include <memory>
29#include <string>
30
31namespace boost {
32namespace beast {
33
34class bind_handler_test : public unit_test::suite
35{
36public:
37 template<class... Args>
38 struct handler
39 {
40 void
41 operator()(Args const&...) const
42 {
43 }
44 };
45
46 struct copyable
47 {
48 template<class... Args>
49 void
50 operator()(Args&&...) const
51 {
52 }
53 };
54
55 struct move_only
56 {
57 move_only() = default;
58 move_only(move_only&&) = default;
59 move_only(move_only const&) = delete;
60 void operator()() const{};
61 };
62
63 // A move-only parameter
64 template<std::size_t I>
65 struct move_arg
66 {
67 move_arg() = default;
68 move_arg(move_arg&&) = default;
69 move_arg(move_arg const&) = delete;
70 void operator()() const
71 {
72 };
73 };
74
75 void
76 callback(int v)
77 {
78 BEAST_EXPECT(v == 42);
79 }
80
81 class test_executor
82 {
83 bind_handler_test& s_;
84
85#if defined(BOOST_ASIO_NO_TS_EXECUTORS)
86 net::any_io_executor ex_;
87
88 // Storing the blocking property as a member is not strictly necessary,
89 // as we could simply forward the calls
90 // require(ex_, blocking.possibly)
91 // and
92 // require(ex_, blocking.never)
93 // to the underlying executor, and then
94 // query(ex_, blocking)
95 // when required. This forwarding approach is used here for the
96 // outstanding_work property.
97 net::execution::blocking_t blocking_;
98
99#else // defined(BOOST_ASIO_NO_TS_EXECUTORS)
100 net::io_context::executor_type ex_;
101#endif // defined(BOOST_ASIO_NO_TS_EXECUTORS)
102 public:
103 test_executor(
104 test_executor const&) = default;
105
106 test_executor(
107 bind_handler_test& s,
108 net::io_context& ioc)
109 : s_(s)
110 , ex_(ioc.get_executor())
111#if defined(BOOST_ASIO_NO_TS_EXECUTORS)
112 , blocking_(net::execution::blocking.possibly)
113#endif
114 {
115 }
116
117 bool operator==(
118 test_executor const& other) const noexcept
119 {
120 return ex_ == other.ex_;
121 }
122
123 bool operator!=(
124 test_executor const& other) const noexcept
125 {
126 return ex_ != other.ex_;
127 }
128
129#if defined(BOOST_ASIO_NO_TS_EXECUTORS)
130
131 net::execution_context&
132 query(
133 net::execution::context_t c) const noexcept
134 {
135 return net::query(ex_, c);
136 }
137
138 net::execution::blocking_t
139 query(
140 net::execution::blocking_t) const noexcept
141 {
142 return blocking_;
143 }
144
145 net::execution::outstanding_work_t
146 query(
147 net::execution::outstanding_work_t w) const noexcept
148 {
149 return net::query(ex_, w);
150 }
151
152 test_executor
153 require(
154 net::execution::blocking_t::possibly_t b) const
155 {
156 test_executor new_ex(*this);
157 new_ex.blocking_ = b;
158 return new_ex;
159 }
160
161 test_executor
162 require(
163 net::execution::blocking_t::never_t b) const
164 {
165 test_executor new_ex(*this);
166 new_ex.blocking_ = b;
167 return new_ex;
168 }
169
170 test_executor prefer(net::execution::outstanding_work_t::untracked_t w) const
171 {
172 test_executor new_ex(*this);
173 new_ex.ex_ = net::prefer(ex_, w);
174 return new_ex;
175 }
176
177 test_executor prefer(net::execution::outstanding_work_t::tracked_t w) const
178 {
179 test_executor new_ex(*this);
180 new_ex.ex_ = net::prefer(ex_, w);
181 return new_ex;
182 }
183
184 template<class F>
185 void execute(F&& f) const
186 {
187 if (blocking_ == net::execution::blocking.possibly)
188 {
189 s_.on_invoke();
190 ex_.execute(std::forward<F>(f));
191 }
192 else
193 {
194 // shouldn't be called since the enclosing
195 // networking wrapper only uses dispatch
196 BEAST_FAIL();
197 }
198 }
199#endif
200#if !defined(BOOST_ASIO_NO_TS_EXECUTORS)
201 net::execution_context&
202 context() const noexcept
203 {
204 return ex_.context();
205 }
206
207 void
208 on_work_started() const noexcept
209 {
210 ex_.on_work_started();
211 }
212
213 void
214 on_work_finished() const noexcept
215 {
216 ex_.on_work_finished();
217 }
218
219 template<class F, class Alloc>
220 void
221 dispatch(F&& f, Alloc const& a)
222 {
223 s_.on_invoke();
224 net::prefer(ex_,
225 net::execution::blocking.possibly,
226 net::execution::allocator(a)).execute(std::forward<F>(f));
227 // previously equivalent to
228 // ex_.dispatch(std::forward<F>(f), a);
229 }
230
231 template<class F, class Alloc>
232 void
233 post(F&&, Alloc const&)
234 {
235 // shouldn't be called since the enclosing
236 // networking wrapper only uses dispatch
237 BEAST_FAIL();
238 }
239
240 template<class F, class Alloc>
241 void
242 defer(F&&, Alloc const&)
243 {
244 // shouldn't be called since the enclosing
245 // networking wrapper only uses dispatch
246 BEAST_FAIL();
247 }
248#endif // !defined(BOOST_ASIO_NO_TS_EXECUTORS)
249 };
250
251#if defined(BOOST_ASIO_NO_TS_EXECUTORS)
252 BOOST_STATIC_ASSERT(net::execution::is_executor<test_executor>::value);
253#else
254 BOOST_STATIC_ASSERT(net::is_executor<test_executor>::value);
255#endif
256
257 class test_cb
258 {
259 bool fail_ = true;
260
261 public:
262 test_cb() = default;
263 test_cb(test_cb const&) = delete;
264 test_cb(test_cb&& other)
265 : fail_(boost::exchange(
266 t&: other.fail_, u: false))
267 {
268 }
269
270 ~test_cb()
271 {
272 BEAST_EXPECT(! fail_);
273 }
274
275 void
276 operator()()
277 {
278 fail_ = false;
279 BEAST_EXPECT(true);
280 }
281
282 void
283 operator()(int v)
284 {
285 fail_ = false;
286 BEAST_EXPECT(v == 42);
287 }
288
289 void
290 operator()(int v, string_view s)
291 {
292 fail_ = false;
293 BEAST_EXPECT(v == 42);
294 BEAST_EXPECT(s == "s");
295 }
296
297 void
298 operator()(int v, string_view s, move_arg<1>)
299 {
300 fail_ = false;
301 BEAST_EXPECT(v == 42);
302 BEAST_EXPECT(s == "s");
303 }
304
305 void
306 operator()(int v, string_view s, move_arg<1>, move_arg<2>)
307 {
308 fail_ = false;
309 BEAST_EXPECT(v == 42);
310 BEAST_EXPECT(s == "s");
311 }
312
313 void
314 operator()(error_code, std::size_t n)
315 {
316 fail_ = false;
317 BEAST_EXPECT(n == 256);
318 }
319
320 void
321 operator()(
322 error_code, std::size_t n, string_view s)
323 {
324 boost::ignore_unused(s);
325 fail_ = false;
326 BEAST_EXPECT(n == 256);
327 }
328
329 void
330 operator()(std::shared_ptr<int> const& sp)
331 {
332 fail_ = false;
333 BEAST_EXPECT(sp.get() != nullptr);
334 }
335 };
336
337#if 0
338 // These functions should fail to compile
339
340 void
341 failStdBind()
342 {
343 std::bind(bind_handler(test_cb{}));
344 }
345
346 void
347 failStdBindFront()
348 {
349 std::bind(bind_front_handler(test_cb{}));
350 }
351#endif
352
353 //--------------------------------------------------------------------------
354
355 bool invoked_;
356
357 void
358 on_invoke()
359 {
360 invoked_ = true;
361 }
362
363 template<class F>
364 void
365 testHooks(net::io_context& ioc, F&& f)
366 {
367 invoked_ = false;
368 net::post(ioc, std::forward<F>(f));
369 ioc.run();
370 ioc.restart();
371 BEAST_EXPECT(invoked_);
372 }
373
374 //--------------------------------------------------------------------------
375
376 void
377 testBindHandler()
378 {
379 using m1 = move_arg<1>;
380 using m2 = move_arg<2>;
381
382 {
383 using namespace std::placeholders;
384
385 // 0-ary
386 bind_handler(handler: test_cb{})();
387
388 // 1-ary
389 bind_handler(handler: test_cb{}, args: 42)();
390 bind_handler(handler: test_cb{}, args: _1)(42);
391 bind_handler(handler: test_cb{}, args: _2)(0, 42);
392
393 // 2-ary
394 bind_handler(handler: test_cb{}, args: 42, args: "s")();
395 bind_handler(handler: test_cb{}, args: 42, args: "s")(0);
396 bind_handler(handler: test_cb{}, args: _1, args: "s")(42);
397 bind_handler(handler: test_cb{}, args: 42, args: _1) ("s");
398 bind_handler(handler: test_cb{}, args: _1, args: _2)(42, "s");
399 bind_handler(handler: test_cb{}, args: _1, args: _2)(42, "s", "X");
400 bind_handler(handler: test_cb{}, args: _2, args: _1)("s", 42);
401 bind_handler(handler: test_cb{}, args: _3, args: _2)("X", "s", 42);
402
403 // 3-ary
404 bind_handler(handler: test_cb{}, args: 42, args: "s")(m1{});
405 bind_handler(handler: test_cb{}, args: 42, args: "s", args: _1)(m1{});
406 bind_handler(handler: test_cb{}, args: 42, args: _1, args: m1{})("s");
407
408 // 4-ary
409 bind_handler(handler: test_cb{}, args: 42, args: "s")(m1{}, m2{});
410 bind_handler(handler: test_cb{}, args: 42, args: "s", args: m1{})(m2{});
411 bind_handler(handler: test_cb{}, args: 42, args: "s", args: m1{}, args: m2{})();
412 bind_handler(handler: test_cb{}, args: 42, args: _1, args: m1{})("s", m2{});
413 bind_handler(handler: test_cb{}, args: _3, args: _1, args: m1{})("s", m2{}, 42);
414 }
415
416 {
417 using namespace boost::placeholders;
418
419 // 0-ary
420 bind_handler(handler: test_cb{})();
421
422 // 1-ary
423 bind_handler(handler: test_cb{}, args: 42)();
424 bind_handler(handler: test_cb{}, args: _1)(42);
425 bind_handler(handler: test_cb{}, args: _2)(0, 42);
426
427 // 2-ary
428 bind_handler(handler: test_cb{}, args: 42, args: "s")();
429 bind_handler(handler: test_cb{}, args: 42, args: "s")(0);
430 bind_handler(handler: test_cb{}, args: _1, args: "s")(42);
431 bind_handler(handler: test_cb{}, args: 42, args: _1) ("s");
432 bind_handler(handler: test_cb{}, args: _1, args: _2)(42, "s");
433 bind_handler(handler: test_cb{}, args: _1, args: _2)(42, "s", "X");
434 bind_handler(handler: test_cb{}, args: _2, args: _1)("s", 42);
435 bind_handler(handler: test_cb{}, args: _3, args: _2)("X", "s", 42);
436
437 // 3-ary
438 bind_handler(handler: test_cb{}, args: 42, args: "s")(m1{});
439 bind_handler(handler: test_cb{}, args: 42, args: "s", args: _1)(m1{});
440 bind_handler(handler: test_cb{}, args: 42, args: _1, args: m1{})("s");
441
442 // 4-ary
443 bind_handler(handler: test_cb{}, args: 42, args: "s")(m1{}, m2{});
444 bind_handler(handler: test_cb{}, args: 42, args: "s", args: m1{})(m2{});
445 bind_handler(handler: test_cb{}, args: 42, args: "s", args: m1{}, args: m2{})();
446 bind_handler(handler: test_cb{}, args: 42, args: _1, args: m1{})("s", m2{});
447 bind_handler(handler: test_cb{}, args: _3, args: _1, args: m1{})("s", m2{}, 42);
448 }
449
450 // perfect forwarding
451 {
452 std::shared_ptr<int> const sp =
453 std::make_shared<int>(args: 42);
454 {
455 bind_handler(handler: test_cb{}, args: sp)();
456 BEAST_EXPECT(sp.get() != nullptr);
457 }
458 {
459 bind_handler(handler: test_cb{})(sp);
460 BEAST_EXPECT(sp.get() != nullptr);
461 }
462 }
463
464 // associated executor
465 {
466 net::io_context ioc;
467 testHooks(ioc, f: bind_handler(handler: net::bind_executor(
468 ex: test_executor(*this, ioc), t: test_cb{})));
469 }
470
471 // asio_handler_invoke
472 {
473 // make sure things compile, also can set a
474 // breakpoint in asio_handler_invoke to make sure
475 // it is instantiated.
476 net::io_context ioc;
477 net::strand<
478 net::io_context::executor_type> s{
479 ioc.get_executor()};
480 net::post(ex: s,
481 token: bind_handler(handler: test_cb{}, args: 42));
482 ioc.run();
483 }
484
485 // legacy hooks
486 legacy_handler::test(
487 f: [](legacy_handler h)
488 {
489 return bind_handler(handler&: h);
490 });
491 }
492
493 void
494 testBindFrontHandler()
495 {
496 using m1 = move_arg<1>;
497 using m2 = move_arg<2>;
498
499 // 0-ary
500 bind_front_handler(handler: test_cb{})();
501
502 // 1-ary
503 bind_front_handler(handler: test_cb{}, args: 42)();
504 bind_front_handler(handler: test_cb{})(42);
505
506 // 2-ary
507 bind_front_handler(handler: test_cb{}, args: 42, args: "s")();
508 bind_front_handler(handler: test_cb{}, args: 42)("s");
509 bind_front_handler(handler: test_cb{})(42, "s");
510
511 // 3-ary
512 bind_front_handler(handler: test_cb{}, args: 42, args: "s", args: m1{})();
513 bind_front_handler(handler: test_cb{}, args: 42, args: "s")(m1{});
514 bind_front_handler(handler: test_cb{}, args: 42)("s", m1{});
515 bind_front_handler(handler: test_cb{})(42, "s", m1{});
516
517 // 4-ary
518 bind_front_handler(handler: test_cb{}, args: 42, args: "s", args: m1{}, args: m2{})();
519 bind_front_handler(handler: test_cb{}, args: 42, args: "s", args: m1{})(m2{});
520 bind_front_handler(handler: test_cb{}, args: 42, args: "s")(m1{}, m2{});
521 bind_front_handler(handler: test_cb{}, args: 42)("s", m1{}, m2{});
522 bind_front_handler(handler: test_cb{})(42, "s", m1{}, m2{});
523
524 error_code ec;
525 std::size_t n = 256;
526
527 // void(error_code, size_t)
528 bind_front_handler(handler: test_cb{}, args&: ec, args&: n)();
529
530 // void(error_code, size_t)(string_view)
531 bind_front_handler(handler: test_cb{}, args&: ec, args&: n)("s");
532
533 // perfect forwarding
534 {
535 std::shared_ptr<int> const sp =
536 std::make_shared<int>(args: 42);
537 bind_front_handler(handler: test_cb{}, args: sp)();
538 BEAST_EXPECT(sp.get() != nullptr);
539 }
540
541 // associated executor
542 {
543 net::io_context ioc;
544
545 testHooks(ioc, f: bind_front_handler(handler: net::bind_executor(
546 ex: test_executor(*this, ioc), t: test_cb{})
547 ));
548 testHooks(ioc, f: bind_front_handler(handler: net::bind_executor(
549 ex: test_executor(*this, ioc), t: test_cb{}),
550 args: 42));
551 testHooks(ioc, f: bind_front_handler(handler: net::bind_executor(
552 ex: test_executor(*this, ioc), t: test_cb{}),
553 args: 42, args: "s"));
554 testHooks(ioc, f: bind_front_handler(handler: net::bind_executor(
555 ex: test_executor(*this, ioc), t: test_cb{}),
556 args: 42, args: "s", args: m1{}));
557 testHooks(ioc, f: bind_front_handler(handler: net::bind_executor(
558 ex: test_executor(*this, ioc), t: test_cb{}),
559 args: 42, args: "s", args: m1{}, args: m2{}));
560 testHooks(ioc, f: bind_front_handler(handler: net::bind_executor(
561 ex: test_executor(*this, ioc), t: test_cb{}),
562 args&: ec, args&: n));
563 }
564
565 // legacy hooks
566 legacy_handler::test(
567 f: [](legacy_handler h)
568 {
569 return bind_front_handler(handler&: h);
570 });
571 legacy_handler::test(
572 f: [](legacy_handler h)
573 {
574 return bind_front_handler(
575 handler&: h, args: error_code{}, args: std::size_t{});
576 });
577 }
578
579
580 //--------------------------------------------------------------------------
581
582 template <class AsyncReadStream, class ReadHandler>
583 void
584 signal_aborted (AsyncReadStream& stream, ReadHandler&& handler)
585 {
586 net::post(
587 stream.get_executor(),
588 bind_handler (std::forward <ReadHandler> (handler),
589 net::error::operation_aborted, 0));
590 }
591
592 template <class AsyncReadStream, class ReadHandler>
593 void
594 signal_eof (AsyncReadStream& stream, ReadHandler&& handler)
595 {
596 net::post(
597 stream.get_executor(),
598 bind_front_handler (std::forward<ReadHandler> (handler),
599 net::error::eof, 0));
600 }
601
602 void
603 testJavadocs()
604 {
605 BEAST_EXPECT((
606 &bind_handler_test::signal_aborted<
607 test::stream, handler<error_code, std::size_t>>));
608
609 BEAST_EXPECT((
610 &bind_handler_test::signal_eof<
611 test::stream, handler<error_code, std::size_t>>));
612 }
613
614 //--------------------------------------------------------------------------
615
616 void
617 run() override
618 {
619 testBindHandler();
620 testBindFrontHandler();
621 testJavadocs();
622 }
623};
624
625BEAST_DEFINE_TESTSUITE(beast,core,bind_handler);
626
627} // beast
628} // boost
629

source code of boost/libs/beast/test/beast/core/bind_handler.cpp