1 /* 2 * Copyright (c) 2023 NVIDIA Corporation 3 * 4 * Licensed under the Apache License Version 2.0 with LLVM Exceptions 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * https://llvm.org/LICENSE.txt 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 #pragma once 17 18 #if __cpp_concepts < 2019'07L 19 # error This library requires support for C++20 concepts 20 #endif 21 22 #include "__config.hpp" 23 #include "__type_traits.hpp" 24 25 #include <version> 26 27 // Perhaps the stdlib lacks support for concepts though: 28 #if __has_include(<concepts>) && __cpp_lib_concepts >= 2020'02L 29 # define STDEXEC_HAS_STD_CONCEPTS_HEADER() 1 30 #else 31 # define STDEXEC_HAS_STD_CONCEPTS_HEADER() 0 32 #endif 33 34 #if STDEXEC_HAS_STD_CONCEPTS_HEADER() 35 # include <concepts> 36 #else 37 # include <type_traits> 38 #endif 39 40 namespace stdexec { 41 ////////////////////////////////////////////////////////////////////////////////////////////////// 42 template <class _Fun, class... _As> 43 concept __callable = requires(_Fun&& __fun, _As&&... __as) { 44 static_cast<_Fun &&>(__fun)(static_cast<_As &&>(__as)...); 45 }; 46 template <class _Fun, class... _As> 47 concept __nothrow_callable = __callable<_Fun, _As...> && requires(_Fun&& __fun, _As&&... __as) { 48 { static_cast<_Fun &&>(__fun)(static_cast<_As &&>(__as)...) } noexcept; 49 }; 50 51 ////////////////////////////////////////////////////////////////////////////////////////////////// 52 template <class...> 53 struct __types; 54 55 template <class... _Ts> 56 concept __typename = requires { 57 typename __types<_Ts...>; // NOLINT 58 }; 59 60 ////////////////////////////////////////////////////////////////////////////////////////////////// 61 template <class _Ap, class _Bp> 62 concept __same_as = STDEXEC_IS_SAME(_Ap, _Bp); 63 64 // Handy concepts 65 template <class _Ty, class _Up> 66 concept __decays_to = __same_as<__decay_t<_Ty>, _Up>; 67 68 template <class _Ty, class _Up> 69 concept __not_decays_to = !__decays_to<_Ty, _Up>; 70 71 template <bool _TrueOrFalse> 72 concept __satisfies = _TrueOrFalse; 73 74 template <class...> 75 concept __true = true; 76 77 template <class _Cp> 78 concept __class = __true<int _Cp::*> && (!__same_as<const _Cp, _Cp>); 79 80 template <class _Ty, class... _As> 81 concept __one_of = (__same_as<_Ty, _As> || ...); 82 83 template <class _Ty, class... _Us> 84 concept __all_of = (__same_as<_Ty, _Us> && ...); 85 86 template <class _Ty, class... _Us> 87 concept __none_of = ((!__same_as<_Ty, _Us>) && ...); 88 89 template <class, template <class...> class> 90 constexpr bool __is_instance_of_ = false; 91 template <class... _As, template <class...> class _Ty> 92 constexpr bool __is_instance_of_<_Ty<_As...>, _Ty> = true; 93 94 template <class _Ay, template <class...> class _Ty> 95 concept __is_instance_of = __is_instance_of_<_Ay, _Ty>; 96 97 template <class _Ay, template <class...> class _Ty> 98 concept __is_not_instance_of = !__is_instance_of<_Ay, _Ty>; 99 } // namespace stdexec 100 101 namespace stdexec::__std_concepts { 102 // Make sure we're using a same_as concept that doesn't instantiate std::is_same 103 template <class _Ap, class _Bp> 104 concept same_as = __same_as<_Ap, _Bp> && __same_as<_Bp, _Ap>; 105 106 #if STDEXEC_HAS_STD_CONCEPTS_HEADER() 107 108 using std::integral; 109 using std::derived_from; 110 using std::convertible_to; 111 using std::equality_comparable; 112 113 #else 114 115 template <class T> 116 concept integral = std::is_integral_v<T>; 117 118 template <class _Ap, class _Bp> 119 concept derived_from = STDEXEC_IS_BASE_OF(_Bp, _Ap) 120 && STDEXEC_IS_CONVERTIBLE_TO(const volatile _Ap*, const volatile _Bp*); 121 122 template <class _From, class _To> 123 concept convertible_to = STDEXEC_IS_CONVERTIBLE_TO(_From, _To) 124 && requires(_From (&__fun)()) { static_cast<_To>(__fun()); }; 125 126 template <class _Ty> 127 concept equality_comparable = requires(__cref_t<_Ty> __t) { 128 { __t == __t } -> convertible_to<bool>; 129 { __t != __t } -> convertible_to<bool>; 130 }; 131 #endif 132 } // namespace stdexec::__std_concepts 133 134 namespace stdexec { 135 using namespace __std_concepts; 136 137 // Avoid using libstdc++'s object concepts because they instantiate a 138 // lot of templates. 139 #if STDEXEC_HAS_BUILTIN(__is_nothrow_destructible) || STDEXEC_MSVC() 140 template <class _Ty> 141 concept destructible = __is_nothrow_destructible(_Ty); 142 #else 143 template <class _Ty> 144 inline constexpr bool __destructible_ = requires(_Ty && (&__fn)() noexcept) { 145 { __fn().~_Ty() } noexcept; 146 }; 147 template <class _Ty> 148 inline constexpr bool __destructible_<_Ty&> = true; 149 template <class _Ty> 150 inline constexpr bool __destructible_<_Ty&&> = true; 151 template <class _Ty, std::size_t _Np> 152 inline constexpr bool __destructible_<_Ty[_Np]> = __destructible_<_Ty>; 153 154 template <class T> 155 concept destructible = __destructible_<T>; 156 #endif 157 158 template <class _Ty, class... _As> 159 concept constructible_from = destructible<_Ty> && STDEXEC_IS_CONSTRUCTIBLE(_Ty, _As...); 160 161 template <class _Ty> 162 concept default_initializable = constructible_from<_Ty> && requires { _Ty{}; } 163 && requires { ::new _Ty; }; 164 165 template <class _Ty> 166 concept move_constructible = constructible_from<_Ty, _Ty>; 167 168 template <class _Ty> 169 concept copy_constructible = move_constructible<_Ty> && constructible_from<_Ty, _Ty const &>; 170 171 template <class _LHS, class _RHS> 172 concept assignable_from = same_as<_LHS, _LHS&> && 173 // std::common_reference_with< 174 // const std::remove_reference_t<_LHS>&, 175 // const std::remove_reference_t<_RHS>&> && 176 requires(_LHS __lhs, _RHS&& __rhs) { 177 { __lhs = static_cast<_RHS &&>(__rhs) } -> same_as<_LHS>; 178 }; 179 180 namespace __swap { 181 using std::swap; 182 183 template <class _Ty, class _Uy> 184 concept swappable_with = requires(_Ty&& __t, _Uy&& __u) { 185 swap(static_cast<_Ty &&>(__t), static_cast<_Uy &&>(__u)); 186 }; 187 188 inline constexpr auto const __fn = 189 []<class _Ty, swappable_with<_Ty> _Uy>(_Ty&& __t, _Uy&& __u) noexcept( 190 noexcept(swap(static_cast<_Ty&&>(__t), static_cast<_Uy&&>(__u)))) { 191 swap(static_cast<_Ty&&>(__t), static_cast<_Uy&&>(__u)); 192 }; 193 } // namespace __swap 194 195 using __swap::swappable_with; 196 inline constexpr auto const & swap = __swap::__fn; 197 198 template <class _Ty> 199 concept swappable = requires(_Ty& a, _Ty& b) { swap(a, b); }; 200 201 template <class _Ty> 202 concept movable = std::is_object_v<_Ty> && move_constructible<_Ty> && assignable_from<_Ty&, _Ty> 203 && swappable<_Ty>; 204 205 template <class _Ty> 206 concept copyable = copy_constructible<_Ty> && movable<_Ty> && assignable_from<_Ty&, _Ty&> 207 && assignable_from<_Ty&, const _Ty&> && assignable_from<_Ty&, const _Ty>; 208 209 template <class _Ty> 210 concept semiregular = copyable<_Ty> && default_initializable<_Ty>; 211 212 template <class _Ty> 213 concept regular = semiregular<_Ty> && equality_comparable<_Ty>; 214 215 // Not exactly right, but close. 216 template <class _Ty> 217 concept __boolean_testable_ = convertible_to<_Ty, bool>; 218 219 template <class T, class U> 220 concept __partially_ordered_with = requires(__cref_t<T> t, __cref_t<U> u) { 221 { t < u } -> __boolean_testable_; 222 { t > u } -> __boolean_testable_; 223 { t <= u } -> __boolean_testable_; 224 { t >= u } -> __boolean_testable_; 225 { u < t } -> __boolean_testable_; 226 { u > t } -> __boolean_testable_; 227 { u <= t } -> __boolean_testable_; 228 { u >= t } -> __boolean_testable_; 229 }; 230 231 template <class _Ty> 232 concept totally_ordered = equality_comparable<_Ty> && __partially_ordered_with<_Ty, _Ty>; 233 234 template <class _Ty> 235 concept __movable_value = move_constructible<__decay_t<_Ty>> 236 && constructible_from<__decay_t<_Ty>, _Ty>; 237 238 template <class _Ty> 239 concept __nothrow_movable_value = __movable_value<_Ty> && requires(_Ty&& __t) { 240 { __decay_t<_Ty>{__decay_t<_Ty>{static_cast<_Ty &&>(__t)}} } noexcept; 241 }; 242 243 template <class _Ty, class... _As> 244 concept __nothrow_constructible_from = constructible_from<_Ty, _As...> 245 && STDEXEC_IS_NOTHROW_CONSTRUCTIBLE(_Ty, _As...); 246 247 template <class _Ty> 248 concept __nothrow_move_constructible = __nothrow_constructible_from<_Ty, _Ty>; 249 250 template <class _Ty> 251 concept __nothrow_copy_constructible = __nothrow_constructible_from<_Ty, const _Ty&>; 252 253 template <class... _Ts> 254 concept __decay_copyable = (constructible_from<__decay_t<_Ts>, _Ts> && ...); 255 256 template <class... _Ts> 257 using __decay_copyable_t = __mbool<__decay_copyable<_Ts...>>; 258 259 template <class... _Ts> 260 concept __nothrow_decay_copyable = (__nothrow_constructible_from<__decay_t<_Ts>, _Ts> && ...); 261 262 template <class... _Ts> 263 using __nothrow_decay_copyable_t = __mbool<__nothrow_decay_copyable<_Ts...>>; 264 265 template <class _Ty, class _Up> 266 concept __decays_to_derived_from = derived_from<__decay_t<_Ty>, _Up>; 267 } // namespace stdexec 268