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