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 < 201907L 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 >= 202002 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 ////////////////////////////////////////////////////////////////////////////////////////////////// 43 template <class _Fun, class... _As> 44 concept __callable = // 45 requires(_Fun&& __fun, _As&&... __as) { // 46 static_cast<_Fun&&>(__fun)(static_cast<_As&&>(__as)...); // 47 }; 48 template <class _Fun, class... _As> 49 concept __nothrow_callable = // 50 __callable<_Fun, _As...> && // 51 requires(_Fun&& __fun, _As&&... __as) { 52 { 53 static_cast<_Fun&&>(__fun)(static_cast<_As&&>(__as)...) 54 } noexcept; 55 }; 56 57 ////////////////////////////////////////////////////////////////////////////////////////////////// 58 template <class...> 59 struct __types; 60 61 template <class... _Ts> 62 concept __typename = requires { typename __types<_Ts...>; }; 63 64 ////////////////////////////////////////////////////////////////////////////////////////////////// 65 template <class _Ap, class _Bp> 66 concept __same_as = STDEXEC_IS_SAME(_Ap, _Bp); 67 68 // Handy concepts 69 template <class _Ty, class _Up> 70 concept __decays_to = __same_as<__decay_t<_Ty>, _Up>; 71 72 template <class _Ty, class _Up> 73 concept __not_decays_to = !__decays_to<_Ty, _Up>; 74 75 template <bool _TrueOrFalse> 76 concept __satisfies = _TrueOrFalse; 77 78 template <class...> 79 concept __true = true; 80 81 template <class _Cp> 82 concept __class = __true<int _Cp::*> && (!__same_as<const _Cp, _Cp>); 83 84 template <class _Ty, class... _As> 85 concept __one_of = (__same_as<_Ty, _As> || ...); 86 87 template <class _Ty, class... _Us> 88 concept __all_of = (__same_as<_Ty, _Us> && ...); 89 90 template <class _Ty, class... _Us> 91 concept __none_of = ((!__same_as<_Ty, _Us>)&&...); 92 93 template <class, template <class...> class> 94 constexpr bool __is_instance_of_ = false; 95 template <class... _As, template <class...> class _Ty> 96 constexpr bool __is_instance_of_<_Ty<_As...>, _Ty> = true; 97 98 template <class _Ay, template <class...> class _Ty> 99 concept __is_instance_of = __is_instance_of_<_Ay, _Ty>; 100 101 template <class _Ay, template <class...> class _Ty> 102 concept __is_not_instance_of = !__is_instance_of<_Ay, _Ty>; 103 } // namespace stdexec 104 105 namespace stdexec::__std_concepts 106 { 107 // Make sure we're using a same_as concept that doesn't instantiate std::is_same 108 template <class _Ap, class _Bp> 109 concept same_as = __same_as<_Ap, _Bp> && __same_as<_Bp, _Ap>; 110 111 #if STDEXEC_HAS_STD_CONCEPTS_HEADER() 112 113 using std::convertible_to; 114 using std::derived_from; 115 using std::equality_comparable; 116 using std::integral; 117 118 #else 119 120 template <class T> 121 concept integral = std::is_integral_v<T>; 122 123 template <class _Ap, class _Bp> 124 concept derived_from = // 125 STDEXEC_IS_BASE_OF(_Bp, _Ap) && // 126 STDEXEC_IS_CONVERTIBLE_TO(const volatile _Ap*, const volatile _Bp*); 127 128 template <class _From, class _To> 129 concept convertible_to = // 130 STDEXEC_IS_CONVERTIBLE_TO(_From, _To) && // 131 requires(_From (&__fun)()) { static_cast<_To>(__fun()); }; 132 133 template <class _Ty> 134 concept equality_comparable = // 135 requires(__cref_t<_Ty> __t) { 136 { 137 __t == __t 138 } -> convertible_to<bool>; 139 { 140 __t != __t 141 } -> convertible_to<bool>; 142 }; 143 #endif 144 } // namespace stdexec::__std_concepts 145 146 namespace stdexec 147 { 148 using namespace __std_concepts; 149 150 // Avoid using libstdc++'s object concepts because they instantiate a 151 // lot of templates. 152 #if STDEXEC_HAS_BUILTIN(__is_nothrow_destructible) || STDEXEC_MSVC() 153 template <class _Ty> 154 concept destructible = __is_nothrow_destructible(_Ty); 155 #else 156 template <class _Ty> 157 inline constexpr bool __destructible_ = // 158 requires(_Ty && (&__fn)() noexcept) { 159 { 160 __fn().~_Ty() 161 } noexcept; 162 }; 163 template <class _Ty> 164 inline constexpr bool __destructible_<_Ty&> = true; 165 template <class _Ty> 166 inline constexpr bool __destructible_<_Ty&&> = true; 167 template <class _Ty, std::size_t _Np> 168 inline constexpr bool __destructible_<_Ty[_Np]> = __destructible_<_Ty>; 169 170 template <class T> 171 concept destructible = __destructible_<T>; 172 #endif 173 174 template <class _Ty, class... _As> 175 concept constructible_from = // 176 destructible<_Ty> && // 177 STDEXEC_IS_CONSTRUCTIBLE(_Ty, _As...); 178 179 template <class _Ty> 180 concept default_initializable = // 181 constructible_from<_Ty> && // 182 requires { _Ty{}; } && // 183 requires { ::new _Ty; }; 184 185 template <class _Ty> 186 concept move_constructible = // 187 constructible_from<_Ty, _Ty>; 188 189 template <class _Ty> 190 concept copy_constructible = // 191 move_constructible<_Ty> // 192 && constructible_from<_Ty, const _Ty&>; 193 194 template <class _LHS, class _RHS> 195 concept assignable_from = // 196 same_as<_LHS, _LHS&> && // 197 // std::common_reference_with< 198 // const std::remove_reference_t<_LHS>&, 199 // const std::remove_reference_t<_RHS>&> && 200 requires(_LHS __lhs, _RHS&& __rhs) { 201 { 202 __lhs = static_cast<_RHS&&>(__rhs) 203 } -> same_as<_LHS>; 204 }; 205 206 namespace __swap 207 { 208 using std::swap; 209 210 template <class _Ty, class _Uy> 211 concept swappable_with = // 212 requires(_Ty&& __t, _Uy&& __u) { // 213 swap(static_cast<_Ty&&>(__t), static_cast<_Uy&&>(__u)); 214 }; 215 216 inline constexpr const auto __fn = // 217 []<class _Ty, swappable_with<_Ty> _Uy>(_Ty&& __t, _Uy&& __u) // 218 noexcept(noexcept(swap(static_cast<_Ty&&>(__t), static_cast<_Uy&&>(__u)))) { 219 swap(static_cast<_Ty&&>(__t), static_cast<_Uy&&>(__u)); 220 }; 221 } // namespace __swap 222 223 using __swap::swappable_with; 224 inline constexpr const auto& swap = __swap::__fn; 225 226 template <class _Ty> 227 concept swappable = // 228 requires(_Ty& a, _Ty& b) { swap(a, b); }; 229 230 template <class _Ty> 231 concept movable = // 232 std::is_object_v<_Ty> && // 233 move_constructible<_Ty> && // 234 assignable_from<_Ty&, _Ty> && // 235 swappable<_Ty>; 236 237 template <class _Ty> 238 concept copyable = // 239 copy_constructible<_Ty> && // 240 movable<_Ty> && // 241 assignable_from<_Ty&, _Ty&> && // 242 assignable_from<_Ty&, const _Ty&> && // 243 assignable_from<_Ty&, const _Ty>; 244 245 template <class _Ty> 246 concept semiregular = // 247 copyable<_Ty> && // 248 default_initializable<_Ty>; 249 250 template <class _Ty> 251 concept regular = // 252 semiregular<_Ty> && // 253 equality_comparable<_Ty>; 254 255 // Not exactly right, but close. 256 template <class _Ty> 257 concept __boolean_testable_ = convertible_to<_Ty, bool>; 258 259 template <class T, class U> 260 concept __partially_ordered_with = // 261 requires(__cref_t<T> t, __cref_t<U> u) { 262 { 263 t < u 264 } -> __boolean_testable_; 265 { 266 t > u 267 } -> __boolean_testable_; 268 { 269 t <= u 270 } -> __boolean_testable_; 271 { 272 t >= u 273 } -> __boolean_testable_; 274 { 275 u < t 276 } -> __boolean_testable_; 277 { 278 u > t 279 } -> __boolean_testable_; 280 { 281 u <= t 282 } -> __boolean_testable_; 283 { 284 u >= t 285 } -> __boolean_testable_; 286 }; 287 288 template <class _Ty> 289 concept totally_ordered = // 290 equality_comparable<_Ty> && // 291 __partially_ordered_with<_Ty, _Ty>; 292 293 template <class _Ty> 294 concept __movable_value = // 295 move_constructible<__decay_t<_Ty>> && // 296 constructible_from<__decay_t<_Ty>, _Ty>; 297 298 template <class _Ty> 299 concept __nothrow_movable_value = // 300 __movable_value<_Ty> && // 301 requires(_Ty&& __t) { 302 { 303 __decay_t<_Ty>{__decay_t<_Ty>{static_cast<_Ty&&>(__t)}} 304 } noexcept; 305 }; 306 307 template <class _Ty, class... _As> 308 concept __nothrow_constructible_from = 309 constructible_from<_Ty, _As...> && 310 STDEXEC_IS_NOTHROW_CONSTRUCTIBLE(_Ty, _As...); 311 312 template <class _Ty> 313 concept __nothrow_move_constructible = __nothrow_constructible_from<_Ty, _Ty>; 314 315 template <class _Ty> 316 concept __nothrow_copy_constructible = 317 __nothrow_constructible_from<_Ty, const _Ty&>; 318 319 template <class... _Ts> 320 concept __decay_copyable = (constructible_from<__decay_t<_Ts>, _Ts> && ...); 321 322 template <class... _Ts> 323 concept __nothrow_decay_copyable = 324 (__nothrow_constructible_from<__decay_t<_Ts>, _Ts> && ...); 325 326 template <class _Ty, class _Up> 327 concept __decays_to_derived_from = derived_from<__decay_t<_Ty>, _Up>; 328 } // namespace stdexec 329