/************************************************************************************ * * D++, A Lightweight C++ library for Discord * * Copyright 2022 Craig Edwards and D++ contributors * (https://github.com/brainboxdotcc/DPP/graphs/contributors) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ************************************************************************************/ #pragma once #include #ifndef DPP_NO_CORO #if (defined(_LIBCPP_VERSION) and !defined(__cpp_impl_coroutine)) // if libc++ experimental implementation (LLVM < 14) # define STDCORO_EXPERIMENTAL_HEADER # define STDCORO_EXPERIMENTAL_NAMESPACE #endif #ifdef STDCORO_GLIBCXX_COMPAT # define __cpp_impl_coroutine 1 namespace std { namespace experimental { using namespace std; } } #endif #ifdef STDCORO_EXPERIMENTAL_HEADER # include #else # include #endif namespace dpp { /** * @brief Implementation details for internal use only. * * @attention This is only meant to be used by D++ internally. Support will not be given regarding the facilities in this namespace. */ namespace detail { #ifdef _DOXYGEN_ /** * @brief Alias for either std or std::experimental depending on compiler and library. Used by coroutine implementation. * * @todo Remove and use std when all supported libraries have coroutines in it */ namespace std_coroutine {} #else # ifdef STDCORO_EXPERIMENTAL_NAMESPACE namespace std_coroutine = std::experimental; # else namespace std_coroutine = std; # endif #endif #ifndef _DOXYGEN_ /** * @brief Concept to check if a type has a useable `operator co_await()` member */ template concept has_co_await_member = requires (T expr) { expr.operator co_await(); }; /** * @brief Concept to check if a type has a useable overload of the free function `operator co_await(expr)` */ template concept has_free_co_await = requires (T expr) { operator co_await(expr); }; /** * @brief Concept to check if a type has useable `await_ready()`, `await_suspend()` and `await_resume()` member functions. */ template concept has_await_members = requires (T expr) { expr.await_ready(); expr.await_suspend(); expr.await_resume(); }; /** * @brief Mimics the compiler's behavior of using co_await. That is, it returns whichever works first, in order : `expr.operator co_await();` > `operator co_await(expr)` > `expr` */ template requires (has_co_await_member) decltype(auto) co_await_resolve(T&& expr) noexcept(noexcept(expr.operator co_await())) { decltype(auto) awaiter = expr.operator co_await(); return awaiter; } /** * @brief Mimics the compiler's behavior of using co_await. That is, it returns whichever works first, in order : `expr.operator co_await();` > `operator co_await(expr)` > `expr` */ template requires (!has_co_await_member && has_free_co_await) decltype(auto) co_await_resolve(T&& expr) noexcept(noexcept(operator co_await(expr))) { decltype(auto) awaiter = operator co_await(static_cast(expr)); return awaiter; } /** * @brief Mimics the compiler's behavior of using co_await. That is, it returns whichever works first, in order : `expr.operator co_await();` > `operator co_await(expr)` > `expr` */ template requires (!has_co_await_member && !has_free_co_await) decltype(auto) co_await_resolve(T&& expr) noexcept { return static_cast(expr); } #else /** * @brief Concept to check if a type has a useable `operator co_await()` member * * @note This is actually a C++20 concept but Doxygen doesn't do well with them */ template inline constexpr bool has_co_await_member; /** * @brief Concept to check if a type has a useable overload of the free function `operator co_await(expr)` * * @note This is actually a C++20 concept but Doxygen doesn't do well with them */ template inline constexpr bool has_free_co_await; /** * @brief Concept to check if a type has useable `await_ready()`, `await_suspend()` and `await_resume()` member functions. * * @note This is actually a C++20 concept but Doxygen doesn't do well with them */ template inline constexpr bool has_await_members; /** * @brief Concept to check if a type can be used with co_await * * @note This is actually a C++20 concept but Doxygen doesn't do well with them */ template inline constexpr bool awaitable_type; /** * @brief Mimics the compiler's behavior of using co_await. That is, it returns whichever works first, in order : `expr.operator co_await();` > `operator co_await(expr)` > `expr` * * This function is conditionally noexcept, if the returned expression also is. */ decltype(auto) co_await_resolve(auto&& expr) {} #endif /** * @brief Convenience alias for the result of a certain awaitable's await_resume. */ template using awaitable_result = decltype(co_await_resolve(std::declval()).await_resume()); } // namespace detail /** * @brief Concept to check if a type can be used with co_await */ template concept awaitable_type = requires (T expr) { detail::co_await_resolve(expr).await_ready(); }; struct confirmation_callback_t; template class async; template #ifndef _DOXYGEN_ requires (!std::is_reference_v) #endif class task; template class coroutine; struct job; #ifdef DPP_CORO_TEST /** * @brief Allocation count of a certain type, for testing purposes. * * @todo Remove when coro is stable */ template inline int coro_alloc_count = 0; #endif } // namespace dpp #endif /* DPP_NO_CORO */