mirror of https://github.com/grpc/grpc.git
[promises] Add a promise-based match operator (#37981)
Closes #37981
COPYBARA_INTEGRATE_REVIEW=https://github.com/grpc/grpc/pull/37981 from ctiller:match-promise 8341b09901
PiperOrigin-RevId: 689223259
pull/37996/head
parent
53c7d45dd0
commit
8b7496172d
7 changed files with 292 additions and 0 deletions
@ -0,0 +1,105 @@ |
||||
// Copyright 2024 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_LIB_PROMISE_MATCH_PROMISE_H |
||||
#define GRPC_SRC_CORE_LIB_PROMISE_MATCH_PROMISE_H |
||||
|
||||
#include "absl/types/variant.h" |
||||
#include "src/core/lib/promise/detail/promise_factory.h" |
||||
#include "src/core/lib/promise/detail/promise_like.h" |
||||
#include "src/core/util/overload.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
namespace promise_detail { |
||||
|
||||
// This types job is to visit a supplied variant, and apply a mapping
|
||||
// Constructor from input types to promises, returning a variant full of
|
||||
// promises.
|
||||
template <typename Constructor, typename... Ts> |
||||
struct ConstructPromiseVariantVisitor { |
||||
// Factory functions supplied to the top level `Match` object, wrapped by
|
||||
// OverloadType to become overloaded members.
|
||||
Constructor constructor; |
||||
|
||||
// Helper function... only callable once.
|
||||
// Given a value, construct a Promise Factory that accepts that value type,
|
||||
// and uses the constructor type above to map from that type to a promise
|
||||
// returned by the factory.
|
||||
// We use the Promise Factory infrastructure to deal with all the common
|
||||
// variants of factory signatures that we've found to be convenient.
|
||||
template <typename T> |
||||
auto CallConstructorThenFactory(T x) { |
||||
OncePromiseFactory<T, Constructor> factory(std::move(constructor)); |
||||
return factory.Make(std::move(x)); |
||||
} |
||||
|
||||
// Polling operator.
|
||||
// Given a visited type T, construct a Promise Factory, use it, and then cast
|
||||
// the result into a variant type that covers ALL of the possible return types
|
||||
// given the input types listed in Ts...
|
||||
template <typename T> |
||||
auto operator()(T x) |
||||
-> absl::variant<promise_detail::PromiseLike< |
||||
decltype(CallConstructorThenFactory(std::declval<Ts>()))>...> { |
||||
return CallConstructorThenFactory(x); |
||||
} |
||||
}; |
||||
|
||||
// Visitor function for PromiseVariant - calls the poll operator on the inner
|
||||
// type
|
||||
class PollVisitor { |
||||
public: |
||||
template <typename T> |
||||
auto operator()(T& x) { |
||||
return x(); |
||||
} |
||||
}; |
||||
|
||||
// Helper type - given a variant V, provides the poll operator (which simply
|
||||
// visits the inner type on the variant with PollVisitor)
|
||||
template <typename V> |
||||
class PromiseVariant { |
||||
public: |
||||
explicit PromiseVariant(V variant) : variant_(std::move(variant)) {} |
||||
auto operator()() { return absl::visit(PollVisitor(), variant_); } |
||||
|
||||
private: |
||||
V variant_; |
||||
}; |
||||
|
||||
} // namespace promise_detail
|
||||
|
||||
// Match for promises
|
||||
// Like the Match function takes a variant of some set of types,
|
||||
// and a set of functions - one per variant type.
|
||||
// We use these functions as Promise Factories, and return a Promise that can be
|
||||
// polled selected by the type that was in the variant.
|
||||
template <typename... Fs, typename... Ts> |
||||
auto MatchPromise(absl::variant<Ts...> value, Fs... fs) { |
||||
// Construct a variant of promises using the factory functions fs, selected by
|
||||
// the type held by value.
|
||||
auto body = absl::visit( |
||||
promise_detail::ConstructPromiseVariantVisitor<OverloadType<Fs...>, |
||||
Ts...>{ |
||||
OverloadType<Fs...>(std::move(fs)...)}, |
||||
std::move(value)); |
||||
// Wrap that in a PromiseVariant that provides the promise API on the wrapped
|
||||
// variant.
|
||||
return promise_detail::PromiseVariant<decltype(body)>(std::move(body)); |
||||
} |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif // GRPC_SRC_CORE_LIB_PROMISE_MATCH_PROMISE_H
|
@ -0,0 +1,66 @@ |
||||
// Copyright 2021 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include "src/core/lib/promise/match_promise.h" |
||||
|
||||
#include <memory> |
||||
|
||||
#include "absl/strings/str_cat.h" |
||||
#include "gtest/gtest.h" |
||||
#include "src/core/lib/promise/promise.h" |
||||
#include "test/core/promise/poll_matcher.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
TEST(MatchPromiseTest, Works) { |
||||
struct Int { |
||||
int x; |
||||
}; |
||||
struct Float { |
||||
float x; |
||||
}; |
||||
using V = absl::variant<Int, Float, std::string>; |
||||
auto make_promise = [](V v) -> Promise<std::string> { |
||||
return MatchPromise( |
||||
std::move(v), |
||||
[](Float x) mutable { |
||||
return [n = 3, x]() mutable -> Poll<std::string> { |
||||
--n; |
||||
if (n > 0) return Pending{}; |
||||
return absl::StrCat(x.x); |
||||
}; |
||||
}, |
||||
[](Int x) { |
||||
return []() mutable -> Poll<std::string> { return Pending{}; }; |
||||
}, |
||||
[](std::string x) { return x; }); |
||||
}; |
||||
auto promise = make_promise(V(Float{3.0f})); |
||||
EXPECT_THAT(promise(), IsPending()); |
||||
EXPECT_THAT(promise(), IsPending()); |
||||
EXPECT_THAT(promise(), IsReady("3")); |
||||
promise = make_promise(V(Int{42})); |
||||
for (int i = 0; i < 10000; i++) { |
||||
EXPECT_THAT(promise(), IsPending()); |
||||
} |
||||
promise = make_promise(V("hello")); |
||||
EXPECT_THAT(promise(), IsReady("hello")); |
||||
} |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
int main(int argc, char** argv) { |
||||
::testing::InitGoogleTest(&argc, argv); |
||||
return RUN_ALL_TESTS(); |
||||
} |
Loading…
Reference in new issue