blob: 79a97403b03532b4dc5fbd1648beccdf75ea069e [file] [log] [blame]
/*
* Copyright (c) 2021-2022 NVIDIA Corporation
*
* Licensed under the Apache License Version 2.0 with LLVM Exceptions
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* https://llvm.org/LICENSE.txt
*
* 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
#if __cpp_concepts < 201907L
#error This library requires support for C++20 concepts
#endif
#include "__detail/__concepts.hpp"
#include "__detail/__config.hpp"
#include "__detail/__meta.hpp"
#include "__detail/__type_traits.hpp"
#include <version>
// Perhaps the stdlib lacks support for concepts though:
#if __has_include(<concepts>) && __cpp_lib_concepts >= 202002
#define STDEXEC_HAS_STD_CONCEPTS_HEADER() 1
#else
#define STDEXEC_HAS_STD_CONCEPTS_HEADER() 0
#endif
#if STDEXEC_HAS_STD_CONCEPTS_HEADER()
#include <concepts>
#else
#include <type_traits>
#endif
namespace stdexec::__std_concepts
{
// Make sure we're using a same_as concept that doesn't instantiate std::is_same
template <class _Ap, class _Bp>
concept same_as = __same_as<_Ap, _Bp> && __same_as<_Bp, _Ap>;
#if STDEXEC_HAS_STD_CONCEPTS_HEADER()
using std::convertible_to;
using std::derived_from;
using std::equality_comparable;
using std::integral;
#else
template <class T>
concept integral = std::is_integral_v<T>;
template <class _Ap, class _Bp>
concept derived_from = //
STDEXEC_IS_BASE_OF(_Bp, _Ap) && //
STDEXEC_IS_CONVERTIBLE_TO(const volatile _Ap*, const volatile _Bp*);
template <class _From, class _To>
concept convertible_to = //
STDEXEC_IS_CONVERTIBLE_TO(_From, _To) && //
requires(_From (&__fun)()) { static_cast<_To>(__fun()); };
template <class _Ty>
concept equality_comparable = //
requires(__cref_t<_Ty> __t) {
{
__t == __t
} -> convertible_to<bool>;
{
__t != __t
} -> convertible_to<bool>;
};
#endif
} // namespace stdexec::__std_concepts
namespace stdexec
{
using namespace __std_concepts;
// Avoid using libstdc++'s object concepts because they instantiate a
// lot of templates.
#if STDEXEC_HAS_BUILTIN(__is_nothrow_destructible)
template <class _Ty>
concept destructible = __is_nothrow_destructible(_Ty);
#else
template <class _Ty>
inline constexpr bool __destructible_ = //
requires {
{
((_Ty && (*)() noexcept) nullptr)().~_Ty()
} noexcept;
};
template <class _Ty>
inline constexpr bool __destructible_<_Ty&> = true;
template <class _Ty>
inline constexpr bool __destructible_<_Ty&&> = true;
template <class _Ty, std::size_t _Np>
inline constexpr bool __destructible_<_Ty[_Np]> = __destructible_<_Ty>;
template <class T>
concept destructible = __destructible_<T>;
#endif
#if STDEXEC_HAS_BUILTIN(__is_constructible)
template <class _Ty, class... _As>
concept constructible_from = //
destructible<_Ty> && //
__is_constructible(_Ty, _As...);
#else
template <class _Ty, class... _As>
concept constructible_from = //
destructible<_Ty> && //
std::is_constructible_v<_Ty, _As...>;
#endif
template <class _Ty>
concept default_initializable = //
constructible_from<_Ty> && //
requires { _Ty{}; } && //
requires { ::new _Ty; };
template <class _Ty>
concept move_constructible = //
constructible_from<_Ty, _Ty>;
template <class _Ty>
concept copy_constructible = //
move_constructible<_Ty> //
&& constructible_from<_Ty, const _Ty&>;
template <class _LHS, class _RHS>
concept assignable_from = //
same_as<_LHS, _LHS&> && //
// std::common_reference_with<
// const std::remove_reference_t<_LHS>&,
// const std::remove_reference_t<_RHS>&> &&
requires(_LHS __lhs, _RHS&& __rhs) {
{
__lhs = ((_RHS&&)__rhs)
} -> same_as<_LHS>;
};
namespace __swap
{
using std::swap;
template <class _Ty, class _Uy>
concept swappable_with = //
requires(_Ty&& __t, _Uy&& __u) { //
swap((_Ty&&)__t, (_Uy&&)__u);
};
inline constexpr const auto __fn = //
[]<class _Ty, swappable_with<_Ty> _Uy>(_Ty&& __t, _Uy&& __u) noexcept(
noexcept(swap((_Ty&&)__t, (_Uy&&)__u))) {
swap((_Ty&&)__t, (_Uy&&)__u);
};
} // namespace __swap
using __swap::swappable_with;
inline constexpr const auto& swap = __swap::__fn;
template <class _Ty>
concept swappable = //
swappable_with<_Ty, _Ty>;
template <class _Ty>
concept movable = //
std::is_object_v<_Ty> && //
move_constructible<_Ty> && //
assignable_from<_Ty&, _Ty> && //
swappable<_Ty>;
template <class _Ty>
concept copyable = //
copy_constructible<_Ty> && //
movable<_Ty> && //
assignable_from<_Ty&, _Ty&> && //
assignable_from<_Ty&, const _Ty&> && //
assignable_from<_Ty&, const _Ty>;
template <class _Ty>
concept semiregular = //
copyable<_Ty> && //
default_initializable<_Ty>;
template <class _Ty>
concept regular = //
semiregular<_Ty> && //
equality_comparable<_Ty>;
// Not exactly right, but close.
template <class _Ty>
concept __boolean_testable_ = convertible_to<_Ty, bool>;
template <class T, class U>
concept __partially_ordered_with = //
requires(__cref_t<T> t, __cref_t<U> u) {
{
t < u
} -> __boolean_testable_;
{
t > u
} -> __boolean_testable_;
{
t <= u
} -> __boolean_testable_;
{
t >= u
} -> __boolean_testable_;
{
u < t
} -> __boolean_testable_;
{
u > t
} -> __boolean_testable_;
{
u <= t
} -> __boolean_testable_;
{
u >= t
} -> __boolean_testable_;
};
template <class _Ty>
concept totally_ordered = //
equality_comparable<_Ty> && //
__partially_ordered_with<_Ty, _Ty>;
template <class _Ty>
concept __movable_value = //
move_constructible<__decay_t<_Ty>> && //
constructible_from<__decay_t<_Ty>, _Ty>;
template <class _Ty>
concept __nothrow_movable_value = //
__movable_value<_Ty> && //
requires(_Ty&& __t) {
{
__decay_t<_Ty>{__decay_t<_Ty>{(_Ty&&)__t}}
} noexcept;
};
#if STDEXEC_HAS_BUILTIN(__is_nothrow_constructible)
template <class _Ty, class... _As>
concept __nothrow_constructible_from = constructible_from<_Ty, _As...> &&
__is_nothrow_constructible(_Ty, _As...);
#else
template <class _Ty, class... _As>
concept __nothrow_constructible_from =
constructible_from<_Ty, _As...> &&
std::is_nothrow_constructible_v<_Ty, _As...>;
#endif
template <class _Ty>
concept __nothrow_move_constructible = __nothrow_constructible_from<_Ty, _Ty>;
template <class _Ty>
concept __nothrow_copy_constructible =
__nothrow_constructible_from<_Ty, const _Ty&>;
template <class _Ty>
concept __decay_copyable = constructible_from<__decay_t<_Ty>, _Ty>;
template <class _Ty>
concept __nothrow_decay_copyable =
__nothrow_constructible_from<__decay_t<_Ty>, _Ty>;
template <class _Ty, class _Up>
concept __decays_to_derived_from = derived_from<__decay_t<_Ty>, _Up>;
} // namespace stdexec
// #if !STDEXEC_HAS_STD_CONCEPTS_HEADER()
// namespace std {
// using namespace stdexec::__std_concepts;
// }
// #endif