1 // Copyright 2021 gRPC authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef GRPC_CORE_LIB_PROMISE_DETAIL_PROMISE_FACTORY_H
16 #define GRPC_CORE_LIB_PROMISE_DETAIL_PROMISE_FACTORY_H
17 
18 #include <grpc/support/port_platform.h>
19 
20 #include "absl/meta/type_traits.h"
21 
22 #include "src/core/lib/promise/detail/promise_like.h"
23 #include "src/core/lib/promise/poll.h"
24 
25 // PromiseFactory is an adaptor class.
26 //
27 // Where a Promise is a thing that's polled periodically, a PromiseFactory
28 // creates a Promise. Within this Promise/Activity framework, PromiseFactory's
29 // then provide the edges for computation -- invoked at state transition
30 // boundaries to provide the new steady state.
31 //
32 // A PromiseFactory formally is f(A) -> Promise<T> for some types A & T.
33 // This get a bit awkward and inapproprate to write however, and so the type
34 // contained herein can adapt various kinds of callable into the correct form.
35 // Of course a callable of a single argument returning a Promise will see an
36 // identity translation. One taking no arguments and returning a Promise
37 // similarly.
38 //
39 // A Promise passed to a PromiseFactory will yield a PromiseFactory that
40 // returns just that Promise.
41 //
42 // Generalizing slightly, a callable taking a single argument A and returning a
43 // Poll<T> will yield a PromiseFactory that captures it's argument A and
44 // returns a Poll<T>.
45 //
46 // Since various consumers of PromiseFactory run either repeatedly through an
47 // overarching Promises lifetime, or just once, and we can optimize just once
48 // by moving the contents of the PromiseFactory, two factory methods are
49 // provided: Once, that can be called just once, and Repeated, that can (wait
50 // for it) be called Repeatedly.
51 
52 namespace grpc_core {
53 namespace promise_detail {
54 
55 // Helper trait: given a T, and T x, is calling x() legal?
56 template <typename T, typename Ignored = void>
57 struct IsVoidCallable {
58   static constexpr bool value = false;
59 };
60 template <typename F>
61 struct IsVoidCallable<F, absl::void_t<decltype(std::declval<F>()())>> {
62   static constexpr bool value = true;
63 };
64 
65 // Given F(A,B,C,...), what's the return type?
66 template <typename T, typename Ignored = void>
67 struct ResultOfT;
68 template <typename F, typename... Args>
69 struct ResultOfT<F(Args...),
70                  absl::void_t<decltype(std::declval<RemoveCVRef<F>>()(
71                      std::declval<Args>()...))>> {
72   using T = decltype(std::declval<RemoveCVRef<F>>()(std::declval<Args>()...));
73 };
74 
75 template <typename T>
76 using ResultOf = typename ResultOfT<T>::T;
77 
78 // Captures the promise functor and the argument passed.
79 // Provides the interface of a promise.
80 template <typename F, typename Arg>
81 class Curried {
82  public:
83   Curried(F&& f, Arg&& arg)
84       : f_(std::forward<F>(f)), arg_(std::forward<Arg>(arg)) {}
85   Curried(const F& f, Arg&& arg) : f_(f), arg_(std::forward<Arg>(arg)) {}
86   using Result = decltype(std::declval<F>()(std::declval<Arg>()));
87   Result operator()() { return f_(arg_); }
88 
89  private:
90   GPR_NO_UNIQUE_ADDRESS F f_;
91   GPR_NO_UNIQUE_ADDRESS Arg arg_;
92 };
93 
94 // Promote a callable(A) -> T | Poll<T> to a PromiseFactory(A) -> Promise<T> by
95 // capturing A.
96 template <typename A, typename F>
97 absl::enable_if_t<!IsVoidCallable<ResultOf<F(A)>>::value,
98                   PromiseLike<Curried<RemoveCVRef<F>, A>>>
99 PromiseFactoryImpl(F&& f, A&& arg) {
100   return Curried<RemoveCVRef<F>, A>(std::forward<F>(f), std::forward<A>(arg));
101 }
102 
103 // Promote a callable() -> T|Poll<T> to a PromiseFactory(A) -> Promise<T>
104 // by dropping the argument passed to the factory.
105 template <typename A, typename F>
106 absl::enable_if_t<!IsVoidCallable<ResultOf<F()>>::value,
107                   PromiseLike<RemoveCVRef<F>>>
108 PromiseFactoryImpl(F f, A&&) {
109   return PromiseLike<F>(std::move(f));
110 }
111 
112 // Promote a callable() -> T|Poll<T> to a PromiseFactory() -> Promise<T>
113 template <typename F>
114 absl::enable_if_t<!IsVoidCallable<ResultOf<F()>>::value,
115                   PromiseLike<RemoveCVRef<F>>>
116 PromiseFactoryImpl(F f) {
117   return PromiseLike<F>(std::move(f));
118 }
119 
120 // Given a callable(A) -> Promise<T>, name it a PromiseFactory and use it.
121 template <typename A, typename F>
122 absl::enable_if_t<IsVoidCallable<ResultOf<F(A)>>::value,
123                   PromiseLike<decltype(std::declval<F>()(std::declval<A>()))>>
124 PromiseFactoryImpl(F&& f, A&& arg) {
125   return f(std::forward<A>(arg));
126 }
127 
128 // Given a callable() -> Promise<T>, promote it to a
129 // PromiseFactory(A) -> Promise<T> by dropping the first argument.
130 template <typename A, typename F>
131 absl::enable_if_t<IsVoidCallable<ResultOf<F()>>::value,
132                   PromiseLike<decltype(std::declval<F>()())>>
133 PromiseFactoryImpl(F&& f, A&&) {
134   return f();
135 }
136 
137 // Given a callable() -> Promise<T>, name it a PromiseFactory and use it.
138 template <typename F>
139 absl::enable_if_t<IsVoidCallable<ResultOf<F()>>::value,
140                   PromiseLike<decltype(std::declval<F>()())>>
141 PromiseFactoryImpl(F&& f) {
142   return f();
143 };
144 
145 template <typename A, typename F>
146 class PromiseFactory {
147  private:
148   GPR_NO_UNIQUE_ADDRESS F f_;
149 
150  public:
151   using Arg = A;
152   using Promise =
153       decltype(PromiseFactoryImpl(std::move(f_), std::declval<A>()));
154 
155   explicit PromiseFactory(F f) : f_(std::move(f)) {}
156 
157   Promise Once(Arg&& a) {
158     return PromiseFactoryImpl(std::move(f_), std::forward<Arg>(a));
159   }
160 
161   Promise Repeated(Arg&& a) const {
162     return PromiseFactoryImpl(f_, std::forward<Arg>(a));
163   }
164 };
165 
166 template <typename F>
167 class PromiseFactory<void, F> {
168  private:
169   GPR_NO_UNIQUE_ADDRESS F f_;
170 
171  public:
172   using Arg = void;
173   using Promise = decltype(PromiseFactoryImpl(std::move(f_)));
174 
175   explicit PromiseFactory(F f) : f_(std::move(f)) {}
176 
177   Promise Once() { return PromiseFactoryImpl(std::move(f_)); }
178 
179   Promise Repeated() const { return PromiseFactoryImpl(f_); }
180 };
181 
182 }  // namespace promise_detail
183 }  // namespace grpc_core
184 
185 #endif  // GRPC_CORE_LIB_PROMISE_DETAIL_PROMISE_FACTORY_H
186