[llvm] 6d6f35e - [ADT] Add makeVisitor to STLExtras.h
Scott Linder via llvm-commits
llvm-commits at lists.llvm.org
Mon Jun 28 13:07:36 PDT 2021
Author: Scott Linder
Date: 2021-06-28T20:07:11Z
New Revision: 6d6f35eb7b92c6dd4478834497752f4e963db16d
URL: https://github.com/llvm/llvm-project/commit/6d6f35eb7b92c6dd4478834497752f4e963db16d
DIFF: https://github.com/llvm/llvm-project/commit/6d6f35eb7b92c6dd4478834497752f4e963db16d.diff
LOG: [ADT] Add makeVisitor to STLExtras.h
Relands patch reverted by 61242c0addb120294211d24a97ed89837418cb36
The original patch mistakenly included unrelated tests.
Adds a utility to combine multiple Callables into a single Callable.
This is useful to make constructing a visitor for `std::visit`-like
functions more natural; functions like this will be added in future
patches.
Intended to supercede https://reviews.llvm.org/D99560 by
perfectly-forwarding the combined Callables.
Reviewed By: dblaikie
Differential Revision: https://reviews.llvm.org/D100670
Added:
Modified:
llvm/include/llvm/ADT/STLExtras.h
llvm/unittests/ADT/STLExtrasTest.cpp
Removed:
################################################################################
diff --git a/llvm/include/llvm/ADT/STLExtras.h b/llvm/include/llvm/ADT/STLExtras.h
index 430da0f42348b..372907be8a11d 100644
--- a/llvm/include/llvm/ADT/STLExtras.h
+++ b/llvm/include/llvm/ADT/STLExtras.h
@@ -1299,6 +1299,58 @@ using is_one_of = disjunction<std::is_same<T, Ts>...>;
template <typename T, typename... Ts>
using are_base_of = conjunction<std::is_base_of<T, Ts>...>;
+namespace detail {
+template <typename... Ts> struct Visitor;
+
+template <typename HeadT, typename... TailTs>
+struct Visitor<HeadT, TailTs...> : remove_cvref_t<HeadT>, Visitor<TailTs...> {
+ explicit constexpr Visitor(HeadT &&Head, TailTs &&...Tail)
+ : remove_cvref_t<HeadT>(std::forward<HeadT>(Head)),
+ Visitor<TailTs...>(std::forward<TailTs>(Tail)...) {}
+ using remove_cvref_t<HeadT>::operator();
+ using Visitor<TailTs...>::operator();
+};
+
+template <typename HeadT> struct Visitor<HeadT> : remove_cvref_t<HeadT> {
+ explicit constexpr Visitor(HeadT &&Head)
+ : remove_cvref_t<HeadT>(std::forward<HeadT>(Head)) {}
+ using remove_cvref_t<HeadT>::operator();
+};
+} // namespace detail
+
+/// Returns an opaquely-typed Callable object whose operator() overload set is
+/// the sum of the operator() overload sets of each CallableT in CallableTs.
+///
+/// The type of the returned object derives from each CallableT in CallableTs.
+/// The returned object is constructed by invoking the appropriate copy or move
+/// constructor of each CallableT, as selected by overload resolution on the
+/// corresponding argument to makeVisitor.
+///
+/// Example:
+///
+/// \code
+/// auto visitor = makeVisitor([](auto) { return "unhandled type"; },
+/// [](int i) { return "int"; },
+/// [](std::string s) { return "str"; });
+/// auto a = visitor(42); // `a` is now "int".
+/// auto b = visitor("foo"); // `b` is now "str".
+/// auto c = visitor(3.14f); // `c` is now "unhandled type".
+/// \endcode
+///
+/// Example of making a visitor with a lambda which captures a move-only type:
+///
+/// \code
+/// std::unique_ptr<FooHandler> FH = /* ... */;
+/// auto visitor = makeVisitor(
+/// [FH{std::move(FH)}](Foo F) { return FH->handle(F); },
+/// [](int i) { return i; },
+/// [](std::string s) { return atoi(s); });
+/// \endcode
+template <typename... CallableTs>
+constexpr decltype(auto) makeVisitor(CallableTs &&...Callables) {
+ return detail::Visitor<CallableTs...>(std::forward<CallableTs>(Callables)...);
+}
+
//===----------------------------------------------------------------------===//
// Extra additions for arrays
//===----------------------------------------------------------------------===//
diff --git a/llvm/unittests/ADT/STLExtrasTest.cpp b/llvm/unittests/ADT/STLExtrasTest.cpp
index 512c594d86322..2c2b649030880 100644
--- a/llvm/unittests/ADT/STLExtrasTest.cpp
+++ b/llvm/unittests/ADT/STLExtrasTest.cpp
@@ -764,4 +764,116 @@ TEST(STLExtras, Unique) {
EXPECT_EQ(3, V[3]);
}
+TEST(STLExtrasTest, MakeVisitorOneCallable) {
+ auto IdentityLambda = [](auto X) { return X; };
+ auto IdentityVisitor = makeVisitor(IdentityLambda);
+ EXPECT_EQ(IdentityLambda(1), IdentityVisitor(1));
+ EXPECT_EQ(IdentityLambda(2.0f), IdentityVisitor(2.0f));
+ EXPECT_TRUE((std::is_same<decltype(IdentityLambda(IdentityLambda)),
+ decltype(IdentityLambda)>::value));
+ EXPECT_TRUE((std::is_same<decltype(IdentityVisitor(IdentityVisitor)),
+ decltype(IdentityVisitor)>::value));
+}
+
+TEST(STLExtrasTest, MakeVisitorTwoCallables) {
+ auto Visitor =
+ makeVisitor([](int) { return "int"; }, [](std::string) { return "str"; });
+ EXPECT_EQ(Visitor(42), "int");
+ EXPECT_EQ(Visitor("foo"), "str");
+}
+
+TEST(STLExtrasTest, MakeVisitorCallableMultipleOperands) {
+ auto Second = makeVisitor([](int I, float F) { return F; },
+ [](float F, int I) { return I; });
+ EXPECT_EQ(Second(1.f, 1), 1);
+ EXPECT_EQ(Second(1, 1.f), 1.f);
+}
+
+TEST(STLExtrasTest, MakeVisitorDefaultCase) {
+ {
+ auto Visitor = makeVisitor([](int I) { return I + 100; },
+ [](float F) { return F * 2; },
+ [](auto) { return "unhandled type"; });
+ EXPECT_EQ(Visitor(24), 124);
+ EXPECT_EQ(Visitor(2.f), 4.f);
+ EXPECT_EQ(Visitor(2.), "unhandled type");
+ EXPECT_EQ(Visitor(Visitor), "unhandled type");
+ }
+ {
+ auto Visitor = makeVisitor([](auto) { return "unhandled type"; },
+ [](int I) { return I + 100; },
+ [](float F) { return F * 2; });
+ EXPECT_EQ(Visitor(24), 124);
+ EXPECT_EQ(Visitor(2.f), 4.f);
+ EXPECT_EQ(Visitor(2.), "unhandled type");
+ EXPECT_EQ(Visitor(Visitor), "unhandled type");
+ }
+}
+
+template <bool Moveable, bool Copyable>
+struct Functor : Counted<Moveable, Copyable> {
+ using Counted<Moveable, Copyable>::Counted;
+ void operator()() {}
+};
+
+TEST(STLExtrasTest, MakeVisitorLifetimeSemanticsPRValue) {
+ int Copies = 0;
+ int Moves = 0;
+ int Destructors = 0;
+ {
+ auto V = makeVisitor(Functor<true, false>(Copies, Moves, Destructors));
+ (void)V;
+ EXPECT_EQ(0, Copies);
+ EXPECT_EQ(1, Moves);
+ EXPECT_EQ(1, Destructors);
+ }
+ EXPECT_EQ(0, Copies);
+ EXPECT_EQ(1, Moves);
+ EXPECT_EQ(2, Destructors);
+}
+
+TEST(STLExtrasTest, MakeVisitorLifetimeSemanticsRValue) {
+ int Copies = 0;
+ int Moves = 0;
+ int Destructors = 0;
+ {
+ Functor<true, false> F(Copies, Moves, Destructors);
+ {
+ auto V = makeVisitor(std::move(F));
+ (void)V;
+ EXPECT_EQ(0, Copies);
+ EXPECT_EQ(1, Moves);
+ EXPECT_EQ(0, Destructors);
+ }
+ EXPECT_EQ(0, Copies);
+ EXPECT_EQ(1, Moves);
+ EXPECT_EQ(1, Destructors);
+ }
+ EXPECT_EQ(0, Copies);
+ EXPECT_EQ(1, Moves);
+ EXPECT_EQ(2, Destructors);
+}
+
+TEST(STLExtrasTest, MakeVisitorLifetimeSemanticsLValue) {
+ int Copies = 0;
+ int Moves = 0;
+ int Destructors = 0;
+ {
+ Functor<true, true> F(Copies, Moves, Destructors);
+ {
+ auto V = makeVisitor(F);
+ (void)V;
+ EXPECT_EQ(1, Copies);
+ EXPECT_EQ(0, Moves);
+ EXPECT_EQ(0, Destructors);
+ }
+ EXPECT_EQ(1, Copies);
+ EXPECT_EQ(0, Moves);
+ EXPECT_EQ(1, Destructors);
+ }
+ EXPECT_EQ(1, Copies);
+ EXPECT_EQ(0, Moves);
+ EXPECT_EQ(2, Destructors);
+}
+
} // namespace
More information about the llvm-commits
mailing list