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