1 /*
2  * Copyright (c) 2021-2024 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 #include "__concepts.hpp"
19 #include "__meta.hpp"
20 
21 namespace stdexec::__std_concepts
22 {
23 #if STDEXEC_HAS_STD_CONCEPTS_HEADER()
24 using std::invocable;
25 #else
26 template <class _Fun, class... _As>
27 concept invocable = //
28     requires(_Fun&& __f, _As&&... __as) {
29         std::invoke(static_cast<_Fun&&>(__f), static_cast<_As&&>(__as)...);
30     };
31 #endif
32 } // namespace stdexec::__std_concepts
33 
34 namespace std
35 {
36 using namespace stdexec::__std_concepts;
37 } // namespace std
38 
39 namespace stdexec
40 {
41 // [func.tag_invoke], tag_invoke
42 namespace __tag_invoke
43 {
44 void tag_invoke();
45 
46 // For handling queryables with a static constexpr query member function:
47 template <class _Tag, class _Env>
48     requires true // so this overload is preferred over the one below
tag_invoke(_Tag,const _Env &)49 STDEXEC_ATTRIBUTE((always_inline)) constexpr auto tag_invoke(
50     _Tag, const _Env&) noexcept -> __mconstant<_Env::query(_Tag())>
51 {
52     return {};
53 }
54 
55 // For handling queryables with a query member function:
56 template <class _Tag, class _Env>
57 STDEXEC_ATTRIBUTE((always_inline))
tag_invoke(_Tag,const _Env & __env)58 constexpr auto tag_invoke(_Tag, const _Env& __env) noexcept(
59     noexcept(__env.query(_Tag()))) -> decltype(__env.query(_Tag()))
60 {
61     return __env.query(_Tag());
62 }
63 
64 // NOT TO SPEC: Don't require tag_invocable to subsume invocable.
65 // std::invoke is more expensive at compile time than necessary,
66 // and results in diagnostics that are more verbose than necessary.
67 template <class _Tag, class... _Args>
68 concept tag_invocable = //
69     requires(_Tag __tag, _Args&&... __args) {
70         tag_invoke(static_cast<_Tag&&>(__tag), static_cast<_Args&&>(__args)...);
71     };
72 
73 template <class _Ret, class _Tag, class... _Args>
74 concept __tag_invocable_r = //
75     requires(_Tag __tag, _Args&&... __args) {
76         {
77             static_cast<_Ret>(tag_invoke(static_cast<_Tag&&>(__tag),
78                                          static_cast<_Args&&>(__args)...))
79         };
80     };
81 
82 // NOT TO SPEC: nothrow_tag_invocable subsumes tag_invocable
83 template <class _Tag, class... _Args>
84 concept nothrow_tag_invocable =
85     tag_invocable<_Tag, _Args...> && //
86     requires(_Tag __tag, _Args&&... __args) {
87         {
88             tag_invoke(static_cast<_Tag&&>(__tag),
89                        static_cast<_Args&&>(__args)...)
90         } noexcept;
91     };
92 
93 template <class _Tag, class... _Args>
94 using tag_invoke_result_t =
95     decltype(tag_invoke(__declval<_Tag>(), __declval<_Args>()...));
96 
97 template <class _Tag, class... _Args>
98 struct tag_invoke_result
99 {};
100 
101 template <class _Tag, class... _Args>
102     requires tag_invocable<_Tag, _Args...>
103 struct tag_invoke_result<_Tag, _Args...>
104 {
105     using type = tag_invoke_result_t<_Tag, _Args...>;
106 };
107 
108 struct tag_invoke_t
109 {
110     template <class _Tag, class... _Args>
111         requires tag_invocable<_Tag, _Args...>
112     STDEXEC_ATTRIBUTE((always_inline))
operator ()stdexec::__tag_invoke::tag_invoke_t113     constexpr auto operator()(_Tag __tag, _Args&&... __args) const
114         noexcept(nothrow_tag_invocable<_Tag, _Args...>)
115             -> tag_invoke_result_t<_Tag, _Args...>
116     {
117         return tag_invoke(static_cast<_Tag&&>(__tag),
118                           static_cast<_Args&&>(__args)...);
119     }
120 };
121 
122 } // namespace __tag_invoke
123 
124 using __tag_invoke::tag_invoke_t;
125 
126 namespace __ti
127 {
128 inline constexpr tag_invoke_t tag_invoke{};
129 } // namespace __ti
130 
131 using namespace __ti;
132 
133 template <auto& _Tag>
134 using tag_t = __decay_t<decltype(_Tag)>;
135 
136 using __tag_invoke::__tag_invocable_r;
137 using __tag_invoke::nothrow_tag_invocable;
138 using __tag_invoke::tag_invocable;
139 using __tag_invoke::tag_invoke_result;
140 using __tag_invoke::tag_invoke_result_t;
141 } // namespace stdexec
142