mirror of https://github.com/grpc/grpc.git
[promises] Add a switch primitive (#35424)
Remove the old `switch` library - this used to be an implementation detail of `Seq`, `TrySeq` - but has become unused.
Add a new user facing primitive `Switch` that fills a similar role to `switch` in C++ - selecting a promise to execute based on a primitive discriminator - much like `If` allows selection based on a boolean discriminator now.
A future change will optimize this to actually lower the `Switch` into an actual `switch` statement, but for right now I want to get the functionality in.
Closes #35424
COPYBARA_INTEGRATE_REVIEW=https://github.com/grpc/grpc/pull/35424 from ctiller:switchy 5308a914c6
PiperOrigin-RevId: 595140965
pull/35415/head
parent
1383c4629b
commit
75e5ebcb14
9 changed files with 241 additions and 1542 deletions
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,72 @@ |
||||
// Copyright 2023 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_SWITCH_H |
||||
#define GRPC_SRC_CORE_LIB_PROMISE_SWITCH_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <memory> |
||||
#include <utility> |
||||
|
||||
#include "src/core/lib/promise/detail/promise_factory.h" |
||||
#include "src/core/lib/promise/if.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
namespace promise_detail { |
||||
template <typename D, typename F> |
||||
struct Case { |
||||
D discriminator; |
||||
F factory; |
||||
}; |
||||
|
||||
template <typename F> |
||||
struct Default { |
||||
F factory; |
||||
}; |
||||
} // namespace promise_detail
|
||||
|
||||
template <typename D, typename PromiseFactory> |
||||
auto Case(D discriminator, PromiseFactory f) { |
||||
return promise_detail::Case<D, PromiseFactory>{discriminator, std::move(f)}; |
||||
} |
||||
|
||||
template <typename PromiseFactory> |
||||
auto Default(PromiseFactory f) { |
||||
return promise_detail::Default<PromiseFactory>{std::move(f)}; |
||||
} |
||||
|
||||
// Given a list of cases that result in promise factories, return a single
|
||||
// promise chosen by the discriminator (the first argument of this function).
|
||||
// e.g.:
|
||||
// Switch(1, Case<1>([] { return 43; }), Case<2>([] { return 44; }))
|
||||
// resolves to 43.
|
||||
// TODO(ctiller): consider writing a code-generator like we do for seq/join
|
||||
// so that this lowers into a C switch statement.
|
||||
template <typename D, typename F> |
||||
auto Switch(D, promise_detail::Default<F> def) { |
||||
return promise_detail::OncePromiseFactory<void, F>(std::move(def.factory)) |
||||
.Make(); |
||||
} |
||||
|
||||
template <typename D, typename F, typename... Others> |
||||
auto Switch(D discriminator, promise_detail::Case<D, F> c, Others... others) { |
||||
return If(discriminator == c.discriminator, std::move(c.factory), |
||||
Switch(discriminator, std::move(others)...)); |
||||
} |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif // GRPC_SRC_CORE_LIB_PROMISE_SWITCH_H
|
@ -0,0 +1,69 @@ |
||||
// Copyright 2023 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/switch.h" |
||||
|
||||
#include "gtest/gtest.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
TEST(SwitchTest, JustDefault) { |
||||
EXPECT_EQ(Switch(42, Default([] { return 1; }))(), Poll<int>(1)); |
||||
} |
||||
|
||||
TEST(SwitchTest, ThreeCases) { |
||||
auto test_switch = [](int d) { |
||||
return Switch(d, Case(1, [] { return 25; }), Case(2, [] { return 95; }), |
||||
Case(3, [] { return 68; }), Default([] { return 52; })); |
||||
}; |
||||
EXPECT_EQ(test_switch(0)(), Poll<int>(52)); |
||||
EXPECT_EQ(test_switch(1)(), Poll<int>(25)); |
||||
EXPECT_EQ(test_switch(2)(), Poll<int>(95)); |
||||
EXPECT_EQ(test_switch(3)(), Poll<int>(68)); |
||||
EXPECT_EQ(test_switch(4)(), Poll<int>(52)); |
||||
} |
||||
|
||||
TEST(SwitchTest, Pending) { |
||||
auto test_switch = [](int d) { |
||||
return Switch(d, Case(42, []() -> Poll<int> { return Pending{}; }), |
||||
Case(1, [] { return 25; }), Case(2, [] { return 95; }), |
||||
Case(3, [] { return 68; }), Default([] { return 52; })); |
||||
}; |
||||
EXPECT_EQ(test_switch(0)(), Poll<int>(52)); |
||||
EXPECT_EQ(test_switch(1)(), Poll<int>(25)); |
||||
EXPECT_EQ(test_switch(2)(), Poll<int>(95)); |
||||
EXPECT_EQ(test_switch(3)(), Poll<int>(68)); |
||||
EXPECT_EQ(test_switch(4)(), Poll<int>(52)); |
||||
EXPECT_EQ(test_switch(42)(), Poll<int>(Pending{})); |
||||
} |
||||
|
||||
TEST(SwitchTest, ThreeCasesFromEnum) { |
||||
enum class X : uint8_t { A, B, C }; |
||||
|
||||
auto test_switch = [](X d) { |
||||
return Switch(d, Case(X::A, [] { return 25; }), |
||||
Case(X::B, [] { return 95; }), Case(X::C, [] { return 68; }), |
||||
Default([] { return 52; })); |
||||
}; |
||||
EXPECT_EQ(test_switch(X::A)(), Poll<int>(25)); |
||||
EXPECT_EQ(test_switch(X::B)(), Poll<int>(95)); |
||||
EXPECT_EQ(test_switch(X::C)(), Poll<int>(68)); |
||||
} |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
int main(int argc, char** argv) { |
||||
::testing::InitGoogleTest(&argc, argv); |
||||
return RUN_ALL_TESTS(); |
||||
} |
@ -1,78 +0,0 @@ |
||||
#!/usr/bin/env python3 |
||||
|
||||
# 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. |
||||
|
||||
import sys |
||||
|
||||
|
||||
# utility: print a big comment block into a set of files |
||||
def put_banner(files, banner): |
||||
for f in files: |
||||
print("/*", file=f) |
||||
for line in banner: |
||||
print(" * %s" % line, file=f) |
||||
print(" */", file=f) |
||||
print("", file=f) |
||||
|
||||
|
||||
with open("src/core/lib/promise/detail/switch.h", "w") as H: |
||||
# copy-paste copyright notice from this file |
||||
with open(sys.argv[0]) as my_source: |
||||
copyright = [] |
||||
for line in my_source: |
||||
if line[0] != "#": |
||||
break |
||||
for line in my_source: |
||||
if line[0] == "#": |
||||
copyright.append(line) |
||||
break |
||||
for line in my_source: |
||||
if line[0] != "#": |
||||
break |
||||
copyright.append(line) |
||||
put_banner([H], [line[2:].rstrip() for line in copyright]) |
||||
|
||||
put_banner([H], ["Automatically generated by %s" % sys.argv[0]]) |
||||
|
||||
print("#ifndef GRPC_CORE_LIB_PROMISE_DETAIL_SWITCH_H", file=H) |
||||
print("#define GRPC_CORE_LIB_PROMISE_DETAIL_SWITCH_H", file=H) |
||||
print("", file=H) |
||||
print("#include <grpc/support/port_platform.h>", file=H) |
||||
print("", file=H) |
||||
print("#include <stdlib.h>", file=H) |
||||
print("", file=H) |
||||
print("namespace grpc_core {", file=H) |
||||
|
||||
for n in range(1, 33): |
||||
print("", file=H) |
||||
print( |
||||
"template <typename R, %s> R Switch(char idx, %s) {" |
||||
% ( |
||||
", ".join("typename F%d" % i for i in range(0, n)), |
||||
", ".join("F%d f%d" % (i, i) for i in range(0, n)), |
||||
), |
||||
file=H, |
||||
) |
||||
print(" switch (idx) {", file=H) |
||||
for i in range(0, n): |
||||
print(" case %d: return f%d();" % (i, i), file=H) |
||||
print(" }", file=H) |
||||
print(" abort();", file=H) |
||||
print("}", file=H) |
||||
|
||||
print("", file=H) |
||||
print("}", file=H) |
||||
print("", file=H) |
||||
print("#endif // GRPC_CORE_LIB_PROMISE_DETAIL_SWITCH_H", file=H) |
Loading…
Reference in new issue