Skip to content
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

Replace std::any with a custom solution. #20251

Merged
merged 1 commit into from
Feb 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 25 additions & 8 deletions src/google/protobuf/descriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
#ifndef GOOGLE_PROTOBUF_DESCRIPTOR_H__
#define GOOGLE_PROTOBUF_DESCRIPTOR_H__

#include <any>
#include <atomic>
#include <cstdint>
#include <iterator>
Expand Down Expand Up @@ -2455,11 +2454,20 @@ class PROTOBUF_EXPORT DescriptorPool {
friend class google::protobuf::descriptor_unittest::ValidationErrorTest;
friend class ::google::protobuf::compiler::CommandLineInterface;
friend class TextFormat;

struct MemoBase {
virtual ~MemoBase() = default;
};
template <typename T>
struct MemoData : MemoBase {
T value;
};

// Memoize a projection of a field. This is used to cache the results of
// calling a function on a field, used for expensive descriptor calculations.
template <typename Func>
auto MemoizeProjection(const FieldDescriptor* field, Func func) const {
using ResultT = decltype(func(field));
const auto& MemoizeProjection(const FieldDescriptor* field, Func func) const {
using ResultT = std::decay_t<decltype(func(field))>;
ABSL_DCHECK(field->file()->pool() == this);
static_assert(std::is_empty_v<Func>);
// This static bool is unique per-Func, so its address can be used as a key.
Expand All @@ -2469,15 +2477,21 @@ class PROTOBUF_EXPORT DescriptorPool {
absl::ReaderMutexLock lock(&field_memo_table_mutex_);
auto it = field_memo_table_.find(key);
if (it != field_memo_table_.end()) {
return std::any_cast<ResultT>(it->second);
return internal::DownCast<const MemoData<ResultT>&>(*it->second).value;
}
}
ResultT result = func(field);
auto result = std::make_unique<MemoData<ResultT>>();
result->value = func(field);
{
absl::MutexLock lock(&field_memo_table_mutex_);
field_memo_table_[key] = result;
auto& res = field_memo_table_[key];
// Only initialize the first time. We don't want to invalidate old
// references.
if (res == nullptr) {
res = std::move(result);
}
return internal::DownCast<const MemoData<ResultT>&>(*res).value;
}
return result;
}
// Return true if the given name is a sub-symbol of any non-package
// descriptor that already exists in the descriptor pool. (The full
Expand Down Expand Up @@ -2537,9 +2551,12 @@ class PROTOBUF_EXPORT DescriptorPool {
Symbol NewPlaceholderWithMutexHeld(absl::string_view name,
PlaceholderType placeholder_type) const;

#ifndef SWIG
mutable absl::Mutex field_memo_table_mutex_;
mutable absl::flat_hash_map<std::pair<const void*, const void*>, std::any>
mutable absl::flat_hash_map<std::pair<const void*, const void*>,
std::unique_ptr<MemoBase>>
field_memo_table_ ABSL_GUARDED_BY(field_memo_table_mutex_);
#endif // SWIG

// If fallback_database_ is nullptr, this is nullptr. Otherwise, this is a
// mutex which must be locked while accessing tables_.
Expand Down
14 changes: 10 additions & 4 deletions src/google/protobuf/descriptor_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <string>
#include <thread> // NOLINT
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>

Expand Down Expand Up @@ -11819,8 +11820,8 @@ TEST_F(DescriptorPoolFeaturesTest, ResolvesFeaturesFor) {
class DescriptorPoolMemoizationTest : public ::testing::Test {
protected:
template <typename Func>
auto MemoizeProjection(const DescriptorPool* pool,
const FieldDescriptor* field, Func func) {
const auto& MemoizeProjection(const DescriptorPool* pool,
const FieldDescriptor* field, Func func) {
return pool->MemoizeProjection(field, func);
};
};
Expand All @@ -11834,15 +11835,20 @@ TEST_F(DescriptorPoolMemoizationTest, MemoizeProjectionBasic) {
proto2_unittest::TestAllTypes message;
const Descriptor* descriptor = message.GetDescriptor();

auto name = DescriptorPoolMemoizationTest::MemoizeProjection(
const auto& name = DescriptorPoolMemoizationTest::MemoizeProjection(
descriptor->file()->pool(), descriptor->field(0), name_lambda);
auto dupe_name = DescriptorPoolMemoizationTest::MemoizeProjection(
const auto& dupe_name = DescriptorPoolMemoizationTest::MemoizeProjection(
descriptor->file()->pool(), descriptor->field(0), name_lambda);

ASSERT_EQ(counter, 1);
ASSERT_EQ(name, "proto2_unittest.TestAllTypes.optional_int32");
ASSERT_EQ(dupe_name, "proto2_unittest.TestAllTypes.optional_int32");

// Check that they are references aliasing the same object.
EXPECT_TRUE(
(std::is_same_v<decltype(name), const decltype(descriptor->name()) &>));
EXPECT_EQ(&name, &dupe_name);

auto other_name = DescriptorPoolMemoizationTest::MemoizeProjection(
descriptor->file()->pool(), descriptor->field(1), name_lambda);

Expand Down
Loading