xref: /openbmc/sdbusplus/include/sdbusplus/async/stdexec/__detail/__query.hpp (revision 10d0b4b7d1498cfd5c3d37edea271a54d1984e41)
1 /*
2  * Copyright (c) 2025 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 <type_traits>
19 
20 #include "__config.hpp"
21 #include "__concepts.hpp"
22 #include "__execution_fwd.hpp"
23 #include "__tag_invoke.hpp"
24 #include "__type_traits.hpp"
25 
26 namespace stdexec {
27   // [exec.queries.queryable]
28   template <class T>
29   concept queryable = destructible<T>;
30 
31   template <class _Env, class _Query, class... _Args>
32   concept __member_queryable_with =
33     queryable<_Env>
34     && requires(const _Env& __env, const _Query& __query, __declfn<_Args&&>... __args) {
35          { __env.query(__query, __args()...) };
36        };
37 
38   template <class _Env, class _Query, class... _Args>
39   concept __nothrow_member_queryable_with =
40     __member_queryable_with<_Env, _Query, _Args...>
41     && requires(const _Env& __env, const _Query& __query, __declfn<_Args&&>... __args) {
42          { __env.query(__query, __args()...) } noexcept;
43        };
44 
45   template <class _Env, class _Qy, class... _Args>
46   using __member_query_result_t =
47     decltype(__declval<const _Env&>().query(__declval<const _Qy&>(), __declval<_Args>()...));
48 
49   constexpr __none_such __no_default{};
50 
51   template <class _Query, class _Env, class... _Args>
52   concept __has_validation = requires { _Query::template __validate<_Env, _Args...>(); };
53 
54   template <class _Query, auto _Default = __no_default, class _Transform = __q1<__midentity>>
55   struct __query // NOLINT(bugprone-crtp-constructor-accessibility)
56     : __query<_Query, __no_default, _Transform> {
57     using __query<_Query, __no_default, _Transform>::operator();
58 
59     template <class... _Args>
STDEXEC_ATTRIBUTEstdexec::__query60     STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
61     constexpr auto operator()(__ignore, _Args&&...) const noexcept //
62       -> __mcall1<_Transform, __mtypeof<_Default>> {
63       return _Default;
64     }
65   };
66 
67   template <class _Query, class _Transform>
68   struct __query<_Query, __no_default, _Transform> {
69     template <class Sig>
70     static inline constexpr _Query (*signature)(Sig) = nullptr;
71 
72     // Query with a .query member function:
73     template <class _Qy = _Query, class _Env, class... _Args>
74       requires __member_queryable_with<const _Env&, _Qy, _Args...>
STDEXEC_ATTRIBUTEstdexec::__query75     STDEXEC_ATTRIBUTE(always_inline, host, device)
76     constexpr auto operator()(const _Env& __env, _Args&&... __args) const
77       noexcept(__nothrow_member_queryable_with<_Env, _Qy, _Args...>)
78         -> __mcall1<_Transform, __member_query_result_t<_Env, _Qy, _Args...>> {
79       if constexpr (__has_validation<_Query, _Env, _Args...>) {
80         _Query::template __validate<_Env, _Args...>();
81       }
82       return __env.query(_Query(), static_cast<_Args&&>(__args)...);
83     }
84 
85     // Query with tag_invoke (legacy):
86     template <class _Qy = _Query, class _Env, class... _Args>
87       requires(!__member_queryable_with<const _Env&, _Qy, _Args...>)
88            && tag_invocable<_Qy, const _Env&, _Args...>
STDEXEC_ATTRIBUTEstdexec::__query89     STDEXEC_ATTRIBUTE(always_inline, host, device)
90     constexpr auto operator()(const _Env& __env, _Args&&... __args) const
91       noexcept(nothrow_tag_invocable<_Qy, const _Env&, _Args...>)
92         -> __mcall1<_Transform, tag_invoke_result_t<_Qy, const _Env&, _Args...>> {
93       if constexpr (__has_validation<_Query, _Env, _Args...>) {
94         _Query::template __validate<_Env, _Args...>();
95       }
96       return tag_invoke(_Query(), __env, static_cast<_Args&&>(__args)...);
97     }
98   };
99 
100   template <class _Env, class _Query, class... _Args>
101   concept __queryable_with = __callable<__query<_Query>, _Env&, _Args...>;
102 
103   template <class _Env, class _Query, class... _Args>
104   concept __nothrow_queryable_with = __nothrow_callable<__query<_Query>, _Env&, _Args...>;
105 
106   template <class _Env, class _Query, class... _Args>
107   using __query_result_t = __call_result_t<__query<_Query>, _Env&, _Args...>;
108 
109   template <class _Env, class _Query, class... _Args>
110   concept __statically_queryable_with_impl = requires(_Query __q, _Args&&... __args) {
111     std::remove_reference_t<_Env>::query(__q, static_cast<_Args&&>(__args)...);
112   };
113 
114   template <class _Env, class _Query, class... _Args>
115   concept __statically_queryable_with = __queryable_with<_Env, _Query, _Args...>
116                                      && __statically_queryable_with_impl<_Env, _Query, _Args...>;
117 
118   //////////////////////////////////////////////////////////////////////////////////////////////////
119   // [exec.queries]
120   namespace __queries {
121     template <class _Tp>
122     concept __is_bool_constant = requires { typename __mbool<_Tp::value>; };
123 
124     struct forwarding_query_t {
125       template <class _Query>
STDEXEC_ATTRIBUTEstdexec::__queries::forwarding_query_t126       STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
127       consteval auto operator()(_Query) const noexcept -> bool {
128         if constexpr (__queryable_with<_Query, forwarding_query_t>) {
129           return __query<forwarding_query_t>()(_Query());
130         } else {
131           return derived_from<_Query, forwarding_query_t>;
132         }
133       }
134     };
135 
136     struct query_or_t {
137       template <class _Query, class _Queryable, class _Default, class... _Args>
STDEXEC_ATTRIBUTEstdexec::__queries::query_or_t138       STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
139       constexpr auto operator()(_Query, _Queryable&&, _Default&& __default, _Args&&...) const
140         noexcept(__nothrow_move_constructible<_Default>) -> _Default {
141         return static_cast<_Default&&>(__default);
142       }
143 
144       template <class _Query, class _Queryable, class _Default, class... _Args>
145         requires __callable<_Query, _Queryable, _Args...>
STDEXEC_ATTRIBUTEstdexec::__queries::query_or_t146       STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
147       constexpr auto
148         operator()(_Query __query, _Queryable&& __queryable, _Default&&, _Args&&... __args) const
149         noexcept(__nothrow_callable<_Query, _Queryable, _Args...>)
150           -> __call_result_t<_Query, _Queryable, _Args...> {
151         return static_cast<_Query&&>(
152           __query)(static_cast<_Queryable&&>(__queryable), static_cast<_Args&&>(__args)...);
153       }
154     };
155   } // namespace __queries
156 
157   using __queries::forwarding_query_t;
158   using __queries::query_or_t;
159 
160   inline constexpr forwarding_query_t forwarding_query{};
161   inline constexpr query_or_t query_or{}; // NOT TO SPEC
162 
163   template <class _Tag>
164   concept __forwarding_query = forwarding_query(_Tag{});
165 } // namespace stdexec
166