mirror of https://github.com/grpc/grpc.git
Add Match/Overload abstractions (#26640)
* match/overload abstraction * update projects * match should really not accept mutable args * typo * tests * usage comment * mutable version * build stuff * clang-format * add an escape hatch to avoid needing port_platform.h in files that do not need port_platform.h * unused args * Make it possible for a test to not depend on gpr * add tests * compile fix * sepellingpull/26659/head
parent
994ee5da0c
commit
0bd70a7e3e
11 changed files with 435 additions and 5 deletions
@ -0,0 +1,72 @@ |
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#ifndef GRPC_CORE_LIB_GPRPP_MATCH_H |
||||||
|
#define GRPC_CORE_LIB_GPRPP_MATCH_H |
||||||
|
|
||||||
|
// Portable code. port_platform.h is not required.
|
||||||
|
|
||||||
|
#include "absl/types/variant.h" |
||||||
|
#include "src/core/lib/gprpp/overload.h" |
||||||
|
|
||||||
|
namespace grpc_core { |
||||||
|
|
||||||
|
namespace detail { |
||||||
|
|
||||||
|
template <typename... Cases> |
||||||
|
struct MatchPointerExtractor { |
||||||
|
OverloadType<Cases...> cases; |
||||||
|
template <typename T> |
||||||
|
auto operator()(T& value) -> decltype(cases(&value)) { |
||||||
|
return cases(&value); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
/// Match on a variant.
|
||||||
|
/// Given variant \a value, and a set of callables \a fs, call the appropriate
|
||||||
|
/// callable based on the type contained in \a value.
|
||||||
|
///
|
||||||
|
/// Example (prints "hoorah"):
|
||||||
|
/// variant<int, string> v = 42;
|
||||||
|
/// Match(v,
|
||||||
|
/// [](int i) { puts("hoorah"); },
|
||||||
|
/// [](string s) { puts("boo"); });
|
||||||
|
template <typename... Fs, typename T0, typename... Ts> |
||||||
|
auto Match(const absl::variant<T0, Ts...>& value, Fs... fs) |
||||||
|
-> decltype(std::declval<OverloadType<Fs...>>()(std::declval<T0>())) { |
||||||
|
return absl::visit(Overload(std::move(fs)...), value); |
||||||
|
} |
||||||
|
|
||||||
|
/// A version of Match that takes a mutable pointer to a variant and calls its
|
||||||
|
/// overload callables with a mutable pointer to the current variant value.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// variant<int, string> v = 42;
|
||||||
|
/// MatchMutable(&v,
|
||||||
|
/// [](int* i) { *i = 1; },
|
||||||
|
/// [](string* s) { *s = "foo"; });
|
||||||
|
/// // v now contains 1.
|
||||||
|
template <typename... Fs, typename T0, typename... Ts> |
||||||
|
auto MatchMutable(absl::variant<T0, Ts...>* value, Fs... fs) |
||||||
|
-> decltype(std::declval<OverloadType<Fs...>>()(std::declval<T0*>())) { |
||||||
|
return absl::visit(detail::MatchPointerExtractor<Fs...>{OverloadType<Fs...>( |
||||||
|
std::move(fs)...)}, |
||||||
|
*value); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace grpc_core
|
||||||
|
|
||||||
|
#endif // GRPC_CORE_LIB_GPRPP_MATCH_H
|
@ -0,0 +1,59 @@ |
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#ifndef GRPC_CORE_LIB_GPRPP_OVERLOAD_H |
||||||
|
#define GRPC_CORE_LIB_GPRPP_OVERLOAD_H |
||||||
|
|
||||||
|
// Portable code. port_platform.h is not required.
|
||||||
|
|
||||||
|
#include <utility> |
||||||
|
|
||||||
|
namespace grpc_core { |
||||||
|
|
||||||
|
template <typename... Cases> |
||||||
|
struct OverloadType; |
||||||
|
// Compose one overload with N more -- use inheritance to leverage using and the
|
||||||
|
// empty base class optimization.
|
||||||
|
template <typename Case, typename... Cases> |
||||||
|
struct OverloadType<Case, Cases...> : public Case, |
||||||
|
public OverloadType<Cases...> { |
||||||
|
explicit OverloadType(Case&& c, Cases&&... cases) |
||||||
|
: Case(std::forward<Case>(c)), |
||||||
|
OverloadType<Cases...>(std::forward<Cases>(cases)...) {} |
||||||
|
using Case::operator(); |
||||||
|
using OverloadType<Cases...>::operator(); |
||||||
|
}; |
||||||
|
// Overload of a single case is just that case itself
|
||||||
|
template <typename Case> |
||||||
|
struct OverloadType<Case> : public Case { |
||||||
|
explicit OverloadType(Case&& c) : Case(std::forward<Case>(c)) {} |
||||||
|
using Case::operator(); |
||||||
|
}; |
||||||
|
|
||||||
|
/// Compose callables into a single callable.
|
||||||
|
/// e.g. given [](int i) { puts("a"); } and [](double d) { puts("b"); },
|
||||||
|
/// return a callable object like:
|
||||||
|
/// struct {
|
||||||
|
/// void operator()(int i) { puts("a"); }
|
||||||
|
/// void operator()(double i) { puts("b"); }
|
||||||
|
/// };
|
||||||
|
/// Preserves all captures.
|
||||||
|
template <typename... Cases> |
||||||
|
OverloadType<Cases...> Overload(Cases... cases) { |
||||||
|
return OverloadType<Cases...>(std::move(cases)...); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace grpc_core
|
||||||
|
|
||||||
|
#endif // GRPC_CORE_LIB_GPRPP_OVERLOAD_H
|
@ -0,0 +1,75 @@ |
|||||||
|
// 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/gprpp/match.h" |
||||||
|
#include <gtest/gtest.h> |
||||||
|
|
||||||
|
namespace grpc_core { |
||||||
|
namespace testing { |
||||||
|
|
||||||
|
TEST(MatchTest, Test) { |
||||||
|
EXPECT_EQ(Match( |
||||||
|
absl::variant<int, double>(1.9), [](int) -> int { abort(); }, |
||||||
|
[](double x) -> int { |
||||||
|
EXPECT_EQ(x, 1.9); |
||||||
|
return 42; |
||||||
|
}), |
||||||
|
42); |
||||||
|
EXPECT_EQ(Match( |
||||||
|
absl::variant<int, double>(3), |
||||||
|
[](int x) -> int { |
||||||
|
EXPECT_EQ(x, 3); |
||||||
|
return 42; |
||||||
|
}, |
||||||
|
[](double) -> int { abort(); }), |
||||||
|
42); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(MatchTest, TestVoidReturn) { |
||||||
|
bool triggered = false; |
||||||
|
Match( |
||||||
|
absl::variant<int, double>(1.9), [](int) { abort(); }, |
||||||
|
[&triggered](double x) { |
||||||
|
EXPECT_EQ(x, 1.9); |
||||||
|
triggered = true; |
||||||
|
}); |
||||||
|
EXPECT_TRUE(triggered); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(MatchTest, TestMutable) { |
||||||
|
absl::variant<int, double> v = 1.9; |
||||||
|
MatchMutable( |
||||||
|
&v, [](int*) { abort(); }, [](double* x) { *x = 0.0; }); |
||||||
|
EXPECT_EQ(v, (absl::variant<int, double>(0.0))); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(MatchTest, TestMutableWithReturn) { |
||||||
|
absl::variant<int, double> v = 1.9; |
||||||
|
EXPECT_EQ(MatchMutable( |
||||||
|
&v, [](int*) -> int { abort(); }, |
||||||
|
[](double* x) -> int { |
||||||
|
*x = 0.0; |
||||||
|
return 1; |
||||||
|
}), |
||||||
|
1); |
||||||
|
EXPECT_EQ(v, (absl::variant<int, double>(0.0))); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace testing
|
||||||
|
} // namespace grpc_core
|
||||||
|
|
||||||
|
int main(int argc, char** argv) { |
||||||
|
::testing::InitGoogleTest(&argc, argv); |
||||||
|
return RUN_ALL_TESTS(); |
||||||
|
} |
@ -0,0 +1,36 @@ |
|||||||
|
// 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/gprpp/overload.h" |
||||||
|
#include <gtest/gtest.h> |
||||||
|
|
||||||
|
namespace grpc_core { |
||||||
|
namespace testing { |
||||||
|
|
||||||
|
TEST(Overload, Test) { |
||||||
|
auto a = [](int x) { return x; }; |
||||||
|
auto b = [](std::string x) -> int { return x.length(); }; |
||||||
|
auto overload = Overload(a, b); |
||||||
|
EXPECT_EQ(overload(1), 1); |
||||||
|
EXPECT_EQ(overload("1"), 1); |
||||||
|
EXPECT_EQ(overload("abc"), 3); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace testing
|
||||||
|
} // namespace grpc_core
|
||||||
|
|
||||||
|
int main(int argc, char** argv) { |
||||||
|
::testing::InitGoogleTest(&argc, argv); |
||||||
|
return RUN_ALL_TESTS(); |
||||||
|
} |
Loading…
Reference in new issue