1 // 2 // execution/connect.hpp 3 // ~~~~~~~~~~~~~~~~~~~~~ 4 // 5 // Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6 // 7 // Distributed under the Boost Software License, Version 1.0. (See accompanying 8 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 // 10 11 #ifndef BOOST_ASIO_EXECUTION_CONNECT_HPP 12 #define BOOST_ASIO_EXECUTION_CONNECT_HPP 13 14 #if defined(_MSC_VER) && (_MSC_VER >= 1200) 15 # pragma once 16 #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 17 18 #include <boost/asio/detail/config.hpp> 19 #include <boost/asio/detail/type_traits.hpp> 20 #include <boost/asio/execution/detail/as_invocable.hpp> 21 #include <boost/asio/execution/detail/as_operation.hpp> 22 #include <boost/asio/execution/detail/as_receiver.hpp> 23 #include <boost/asio/execution/executor.hpp> 24 #include <boost/asio/execution/operation_state.hpp> 25 #include <boost/asio/execution/receiver.hpp> 26 #include <boost/asio/execution/sender.hpp> 27 #include <boost/asio/traits/connect_member.hpp> 28 #include <boost/asio/traits/connect_free.hpp> 29 30 #include <boost/asio/detail/push_options.hpp> 31 32 #if defined(GENERATING_DOCUMENTATION) 33 34 namespace boost { 35 namespace asio { 36 namespace execution { 37 38 /// A customisation point that connects a sender to a receiver. 39 /** 40 * The name <tt>execution::connect</tt> denotes a customisation point object. 41 * For some subexpressions <tt>s</tt> and <tt>r</tt>, let <tt>S</tt> be a type 42 * such that <tt>decltype((s))</tt> is <tt>S</tt> and let <tt>R</tt> be a type 43 * such that <tt>decltype((r))</tt> is <tt>R</tt>. The expression 44 * <tt>execution::connect(s, r)</tt> is expression-equivalent to: 45 * 46 * @li <tt>s.connect(r)</tt>, if that expression is valid, if its type 47 * satisfies <tt>operation_state</tt>, and if <tt>S</tt> satisfies 48 * <tt>sender</tt>. 49 * 50 * @li Otherwise, <tt>connect(s, r)</tt>, if that expression is valid, if its 51 * type satisfies <tt>operation_state</tt>, and if <tt>S</tt> satisfies 52 * <tt>sender</tt>, with overload resolution performed in a context that 53 * includes the declaration <tt>void connect();</tt> and that does not include 54 * a declaration of <tt>execution::connect</tt>. 55 * 56 * @li Otherwise, <tt>as_operation{s, r}</tt>, if <tt>r</tt> is not an instance 57 * of <tt>as_receiver<F, S></tt> for some type <tt>F</tt>, and if 58 * <tt>receiver_of<R> && executor_of<remove_cvref_t<S>, 59 * as_invocable<remove_cvref_t<R>, S>></tt> is <tt>true</tt>, where 60 * <tt>as_operation</tt> is an implementation-defined class equivalent to 61 * @code template <class S, class R> 62 * struct as_operation 63 * { 64 * remove_cvref_t<S> e_; 65 * remove_cvref_t<R> r_; 66 * void start() noexcept try { 67 * execution::execute(std::move(e_), 68 * as_invocable<remove_cvref_t<R>, S>{r_}); 69 * } catch(...) { 70 * execution::set_error(std::move(r_), current_exception()); 71 * } 72 * }; @endcode 73 * and <tt>as_invocable</tt> is a class template equivalent to the following: 74 * @code template<class R> 75 * struct as_invocable 76 * { 77 * R* r_; 78 * explicit as_invocable(R& r) noexcept 79 * : r_(std::addressof(r)) {} 80 * as_invocable(as_invocable && other) noexcept 81 * : r_(std::exchange(other.r_, nullptr)) {} 82 * ~as_invocable() { 83 * if(r_) 84 * execution::set_done(std::move(*r_)); 85 * } 86 * void operator()() & noexcept try { 87 * execution::set_value(std::move(*r_)); 88 * r_ = nullptr; 89 * } catch(...) { 90 * execution::set_error(std::move(*r_), current_exception()); 91 * r_ = nullptr; 92 * } 93 * }; 94 * @endcode 95 * 96 * @li Otherwise, <tt>execution::connect(s, r)</tt> is ill-formed. 97 */ 98 inline constexpr unspecified connect = unspecified; 99 100 /// A type trait that determines whether a @c connect expression is 101 /// well-formed. 102 /** 103 * Class template @c can_connect is a trait that is derived from 104 * @c true_type if the expression <tt>execution::connect(std::declval<S>(), 105 * std::declval<R>())</tt> is well formed; otherwise @c false_type. 106 */ 107 template <typename S, typename R> 108 struct can_connect : 109 integral_constant<bool, automatically_determined> 110 { 111 }; 112 113 /// A type trait to determine the result of a @c connect expression. 114 template <typename S, typename R> 115 struct connect_result 116 { 117 /// The type of the connect expression. 118 /** 119 * The type of the expression <tt>execution::connect(std::declval<S>(), 120 * std::declval<R>())</tt>. 121 */ 122 typedef automatically_determined type; 123 }; 124 125 /// A type alis to determine the result of a @c connect expression. 126 template <typename S, typename R> 127 using connect_result_t = typename connect_result<S, R>::type; 128 129 } // namespace execution 130 } // namespace asio 131 } // namespace boost 132 133 #else // defined(GENERATING_DOCUMENTATION) 134 135 namespace asio_execution_connect_fn { 136 137 using boost::asio::conditional; 138 using boost::asio::declval; 139 using boost::asio::enable_if; 140 using boost::asio::execution::detail::as_invocable; 141 using boost::asio::execution::detail::as_operation; 142 using boost::asio::execution::detail::is_as_receiver; 143 using boost::asio::execution::is_executor_of; 144 using boost::asio::execution::is_operation_state; 145 using boost::asio::execution::is_receiver; 146 using boost::asio::execution::is_sender; 147 using boost::asio::false_type; 148 using boost::asio::remove_cvref; 149 using boost::asio::traits::connect_free; 150 using boost::asio::traits::connect_member; 151 152 void connect(); 153 154 enum overload_type 155 { 156 call_member, 157 call_free, 158 adapter, 159 ill_formed 160 }; 161 162 template <typename S, typename R, typename = void> 163 struct call_traits 164 { 165 BOOST_ASIO_STATIC_CONSTEXPR(overload_type, overload = ill_formed); 166 BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept = false); 167 typedef void result_type; 168 }; 169 170 template <typename S, typename R> 171 struct call_traits<S, void(R), 172 typename enable_if< 173 ( 174 connect_member<S, R>::is_valid 175 && 176 is_operation_state<typename connect_member<S, R>::result_type>::value 177 && 178 is_sender<typename remove_cvref<S>::type>::value 179 ) 180 >::type> : 181 connect_member<S, R> 182 { 183 BOOST_ASIO_STATIC_CONSTEXPR(overload_type, overload = call_member); 184 }; 185 186 template <typename S, typename R> 187 struct call_traits<S, void(R), 188 typename enable_if< 189 ( 190 !connect_member<S, R>::is_valid 191 && 192 connect_free<S, R>::is_valid 193 && 194 is_operation_state<typename connect_free<S, R>::result_type>::value 195 && 196 is_sender<typename remove_cvref<S>::type>::value 197 ) 198 >::type> : 199 connect_free<S, R> 200 { 201 BOOST_ASIO_STATIC_CONSTEXPR(overload_type, overload = call_free); 202 }; 203 204 template <typename S, typename R> 205 struct call_traits<S, void(R), 206 typename enable_if< 207 ( 208 !connect_member<S, R>::is_valid 209 && 210 !connect_free<S, R>::is_valid 211 && 212 is_receiver<R>::value 213 && 214 conditional< 215 !is_as_receiver< 216 typename remove_cvref<R>::type 217 >::value, 218 is_executor_of< 219 typename remove_cvref<S>::type, 220 as_invocable<typename remove_cvref<R>::type, S> 221 >, 222 false_type 223 >::type::value 224 ) 225 >::type> 226 { 227 BOOST_ASIO_STATIC_CONSTEXPR(overload_type, overload = adapter); 228 BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept = false); 229 typedef as_operation<S, R> result_type; 230 }; 231 232 struct impl 233 { 234 #if defined(BOOST_ASIO_HAS_MOVE) 235 template <typename S, typename R> 236 BOOST_ASIO_CONSTEXPR typename enable_if< 237 call_traits<S, void(R)>::overload == call_member, 238 typename call_traits<S, void(R)>::result_type 239 >::type operator ()asio_execution_connect_fn::impl240 operator()(S&& s, R&& r) const 241 BOOST_ASIO_NOEXCEPT_IF(( 242 call_traits<S, void(R)>::is_noexcept)) 243 { 244 return BOOST_ASIO_MOVE_CAST(S)(s).connect(BOOST_ASIO_MOVE_CAST(R)(r)); 245 } 246 247 template <typename S, typename R> 248 BOOST_ASIO_CONSTEXPR typename enable_if< 249 call_traits<S, void(R)>::overload == call_free, 250 typename call_traits<S, void(R)>::result_type 251 >::type operator ()asio_execution_connect_fn::impl252 operator()(S&& s, R&& r) const 253 BOOST_ASIO_NOEXCEPT_IF(( 254 call_traits<S, void(R)>::is_noexcept)) 255 { 256 return connect(BOOST_ASIO_MOVE_CAST(S)(s), BOOST_ASIO_MOVE_CAST(R)(r)); 257 } 258 259 template <typename S, typename R> 260 BOOST_ASIO_CONSTEXPR typename enable_if< 261 call_traits<S, void(R)>::overload == adapter, 262 typename call_traits<S, void(R)>::result_type 263 >::type operator ()asio_execution_connect_fn::impl264 operator()(S&& s, R&& r) const 265 BOOST_ASIO_NOEXCEPT_IF(( 266 call_traits<S, void(R)>::is_noexcept)) 267 { 268 return typename call_traits<S, void(R)>::result_type( 269 BOOST_ASIO_MOVE_CAST(S)(s), BOOST_ASIO_MOVE_CAST(R)(r)); 270 } 271 #else // defined(BOOST_ASIO_HAS_MOVE) 272 template <typename S, typename R> 273 BOOST_ASIO_CONSTEXPR typename enable_if< 274 call_traits<S&, void(R&)>::overload == call_member, 275 typename call_traits<S&, void(R&)>::result_type 276 >::type 277 operator()(S& s, R& r) const 278 BOOST_ASIO_NOEXCEPT_IF(( 279 call_traits<S&, void(R&)>::is_noexcept)) 280 { 281 return s.connect(r); 282 } 283 284 template <typename S, typename R> 285 BOOST_ASIO_CONSTEXPR typename enable_if< 286 call_traits<const S&, void(R&)>::overload == call_member, 287 typename call_traits<const S&, void(R&)>::result_type 288 >::type 289 operator()(const S& s, R& r) const 290 BOOST_ASIO_NOEXCEPT_IF(( 291 call_traits<const S&, void(R&)>::is_noexcept)) 292 { 293 return s.connect(r); 294 } 295 296 template <typename S, typename R> 297 BOOST_ASIO_CONSTEXPR typename enable_if< 298 call_traits<S&, void(R&)>::overload == call_free, 299 typename call_traits<S&, void(R&)>::result_type 300 >::type 301 operator()(S& s, R& r) const 302 BOOST_ASIO_NOEXCEPT_IF(( 303 call_traits<S&, void(R&)>::is_noexcept)) 304 { 305 return connect(s, r); 306 } 307 308 template <typename S, typename R> 309 BOOST_ASIO_CONSTEXPR typename enable_if< 310 call_traits<const S&, void(R&)>::overload == call_free, 311 typename call_traits<const S&, void(R&)>::result_type 312 >::type 313 operator()(const S& s, R& r) const 314 BOOST_ASIO_NOEXCEPT_IF(( 315 call_traits<const S&, void(R&)>::is_noexcept)) 316 { 317 return connect(s, r); 318 } 319 320 template <typename S, typename R> 321 BOOST_ASIO_CONSTEXPR typename enable_if< 322 call_traits<S&, void(R&)>::overload == adapter, 323 typename call_traits<S&, void(R&)>::result_type 324 >::type 325 operator()(S& s, R& r) const 326 BOOST_ASIO_NOEXCEPT_IF(( 327 call_traits<S&, void(R&)>::is_noexcept)) 328 { 329 return typename call_traits<S&, void(R&)>::result_type(s, r); 330 } 331 332 template <typename S, typename R> 333 BOOST_ASIO_CONSTEXPR typename enable_if< 334 call_traits<const S&, void(R&)>::overload == adapter, 335 typename call_traits<const S&, void(R&)>::result_type 336 >::type 337 operator()(const S& s, R& r) const 338 BOOST_ASIO_NOEXCEPT_IF(( 339 call_traits<const S&, void(R&)>::is_noexcept)) 340 { 341 return typename call_traits<const S&, void(R&)>::result_type(s, r); 342 } 343 344 template <typename S, typename R> 345 BOOST_ASIO_CONSTEXPR typename enable_if< 346 call_traits<S&, void(const R&)>::overload == call_member, 347 typename call_traits<S&, void(const R&)>::result_type 348 >::type 349 operator()(S& s, const R& r) const 350 BOOST_ASIO_NOEXCEPT_IF(( 351 call_traits<S&, void(const R&)>::is_noexcept)) 352 { 353 return s.connect(r); 354 } 355 356 template <typename S, typename R> 357 BOOST_ASIO_CONSTEXPR typename enable_if< 358 call_traits<const S&, void(const R&)>::overload == call_member, 359 typename call_traits<const S&, void(const R&)>::result_type 360 >::type 361 operator()(const S& s, const R& r) const 362 BOOST_ASIO_NOEXCEPT_IF(( 363 call_traits<const S&, void(const R&)>::is_noexcept)) 364 { 365 return s.connect(r); 366 } 367 368 template <typename S, typename R> 369 BOOST_ASIO_CONSTEXPR typename enable_if< 370 call_traits<S&, void(const R&)>::overload == call_free, 371 typename call_traits<S&, void(const R&)>::result_type 372 >::type 373 operator()(S& s, const R& r) const 374 BOOST_ASIO_NOEXCEPT_IF(( 375 call_traits<S&, void(const R&)>::is_noexcept)) 376 { 377 return connect(s, r); 378 } 379 380 template <typename S, typename R> 381 BOOST_ASIO_CONSTEXPR typename enable_if< 382 call_traits<const S&, void(const R&)>::overload == call_free, 383 typename call_traits<const S&, void(const R&)>::result_type 384 >::type 385 operator()(const S& s, const R& r) const 386 BOOST_ASIO_NOEXCEPT_IF(( 387 call_traits<const S&, void(const R&)>::is_noexcept)) 388 { 389 return connect(s, r); 390 } 391 392 template <typename S, typename R> 393 BOOST_ASIO_CONSTEXPR typename enable_if< 394 call_traits<S&, void(const R&)>::overload == adapter, 395 typename call_traits<S&, void(const R&)>::result_type 396 >::type 397 operator()(S& s, const R& r) const 398 BOOST_ASIO_NOEXCEPT_IF(( 399 call_traits<S&, void(const R&)>::is_noexcept)) 400 { 401 return typename call_traits<S&, void(const R&)>::result_type(s, r); 402 } 403 404 template <typename S, typename R> 405 BOOST_ASIO_CONSTEXPR typename enable_if< 406 call_traits<const S&, void(const R&)>::overload == adapter, 407 typename call_traits<const S&, void(const R&)>::result_type 408 >::type 409 operator()(const S& s, const R& r) const 410 BOOST_ASIO_NOEXCEPT_IF(( 411 call_traits<const S&, void(const R&)>::is_noexcept)) 412 { 413 return typename call_traits<const S&, void(const R&)>::result_type(s, r); 414 } 415 #endif // defined(BOOST_ASIO_HAS_MOVE) 416 }; 417 418 template <typename T = impl> 419 struct static_instance 420 { 421 static const T instance; 422 }; 423 424 template <typename T> 425 const T static_instance<T>::instance = {}; 426 427 } // namespace asio_execution_connect_fn 428 namespace boost { 429 namespace asio { 430 namespace execution { 431 namespace { 432 433 static BOOST_ASIO_CONSTEXPR const asio_execution_connect_fn::impl& 434 connect = asio_execution_connect_fn::static_instance<>::instance; 435 436 } // namespace 437 438 template <typename S, typename R> 439 struct can_connect : 440 integral_constant<bool, 441 asio_execution_connect_fn::call_traits<S, void(R)>::overload != 442 asio_execution_connect_fn::ill_formed> 443 { 444 }; 445 446 #if defined(BOOST_ASIO_HAS_VARIABLE_TEMPLATES) 447 448 template <typename S, typename R> 449 constexpr bool can_connect_v = can_connect<S, R>::value; 450 451 #endif // defined(BOOST_ASIO_HAS_VARIABLE_TEMPLATES) 452 453 template <typename S, typename R> 454 struct is_nothrow_connect : 455 integral_constant<bool, 456 asio_execution_connect_fn::call_traits<S, void(R)>::is_noexcept> 457 { 458 }; 459 460 #if defined(BOOST_ASIO_HAS_VARIABLE_TEMPLATES) 461 462 template <typename S, typename R> 463 constexpr bool is_nothrow_connect_v 464 = is_nothrow_connect<S, R>::value; 465 466 #endif // defined(BOOST_ASIO_HAS_VARIABLE_TEMPLATES) 467 468 template <typename S, typename R> 469 struct connect_result 470 { 471 typedef typename asio_execution_connect_fn::call_traits< 472 S, void(R)>::result_type type; 473 }; 474 475 #if defined(BOOST_ASIO_HAS_ALIAS_TEMPLATES) 476 477 template <typename S, typename R> 478 using connect_result_t = typename connect_result<S, R>::type; 479 480 #endif // defined(BOOST_ASIO_HAS_ALIAS_TEMPLATES) 481 482 } // namespace execution 483 } // namespace asio 484 } // namespace boost 485 486 #endif // defined(GENERATING_DOCUMENTATION) 487 488 #include <boost/asio/detail/pop_options.hpp> 489 490 #endif // BOOST_ASIO_EXECUTION_CONNECT_HPP 491