-
Notifications
You must be signed in to change notification settings - Fork 15.4k
[LLVM][ADT] Add helper class for working with caches #171008
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
@llvm/pr-subscribers-llvm-adt Author: Victor Chernyakin (localspook) ChangesSee the doc comment for what it does. I'm planning to use this in an upcoming refactor of this function. Here's another, completely unrelated function it could simplify; this seems like a generally useful feature. Full diff: https://github.com/llvm/llvm-project/pull/171008.diff 2 Files Affected:
diff --git a/llvm/include/llvm/ADT/STLExtras.h b/llvm/include/llvm/ADT/STLExtras.h
index af0e4a36be1b1..4645b35b234fb 100644
--- a/llvm/include/llvm/ADT/STLExtras.h
+++ b/llvm/include/llvm/ADT/STLExtras.h
@@ -2610,6 +2610,30 @@ template <typename T> using has_sizeof = decltype(sizeof(T));
template <typename T>
constexpr bool is_incomplete_v = !is_detected<detail::has_sizeof, T>::value;
+/// A utility for working with maps that allows concisely expressing "perform
+/// and cache this expensive computation only if it isn't already cached".
+/// Use it like so:
+///
+/// ```cpp
+/// std::unordered_map<K, V> Cache;
+/// auto& Value = Cache.try_emplace(
+/// Key, llvm::defer {[] { /* heavy work */ }}).first->second;
+/// ```
+template <typename FnT> class defer {
+public:
+ constexpr defer(FnT &&Fn LLVM_LIFETIME_BOUND) : Fn(Fn) {}
+
+ template <typename U> constexpr operator U() {
+ return std::forward<FnT>(Fn)();
+ }
+
+private:
+ FnT &Fn;
+};
+
+// Silence -Wctad-maybe-unsupported.
+template <typename FnT> defer(FnT &&Fn LLVM_LIFETIME_BOUND) -> defer<FnT>;
+
} // end namespace llvm
namespace std {
diff --git a/llvm/unittests/ADT/STLExtrasTest.cpp b/llvm/unittests/ADT/STLExtrasTest.cpp
index 85567775e4ebd..18a78e625022c 100644
--- a/llvm/unittests/ADT/STLExtrasTest.cpp
+++ b/llvm/unittests/ADT/STLExtrasTest.cpp
@@ -1699,4 +1699,13 @@ struct Bar {};
static_assert(is_incomplete_v<Foo>, "Foo is incomplete");
static_assert(!is_incomplete_v<Bar>, "Bar is defined");
+TEST(STLExtrasTest, Defer) {
+ std::unordered_map<int, int> Cache;
+ const auto [It, Inserted]{Cache.try_emplace(1, defer{[] { return 10; }})};
+ ASSERT_TRUE(Inserted);
+ ASSERT_EQ(It->second, 10);
+ Cache.try_emplace(
+ 1, defer{[] { ASSERT_TRUE(false && "this should never be executed"); }});
+}
+
} // namespace
|
🐧 Linux x64 Test Results
✅ The build succeeded and all tests passed. |
|
An idea about naming: "elide". |
|
Oh that's interesting, yeah, this |
| /// auto& Value = Cache.try_emplace( | ||
| /// Key, llvm::defer {[] { /* heavy work */ }}).first->second; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't get it -- how is this different than passing the lambda directly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I see, it's invoked through the conversion operator. I'd definitely focus on this aspect in the explanation of what this does, in abstract of the intended use in emplace operations
| TEST(STLExtrasTest, Defer) { | ||
| std::unordered_map<int, int> Cache; | ||
| const auto [It, Inserted]{Cache.try_emplace(1, defer{[] { return 10; }})}; | ||
| ASSERT_TRUE(Inserted); | ||
| ASSERT_EQ(It->second, 10); | ||
| Cache.try_emplace(1, defer{[] { | ||
| assert(false && "this should never be executed"); | ||
| return 0; | ||
| }}); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would benefit from tests without unordered_map.
See the doc comment for how it works.
I'm planning to use this in an upcoming refactor of this function. Here's another, completely unrelated function it could simplify; this seems like a generally useful feature.