Skip to content

Commit

Permalink
Replace std::any with a custom solution. (#20251)
Browse files Browse the repository at this point in the history
`std::any` leads to compiler errors in some versions of gcc during
constructibility trait checks.

Now that we can guarantee it, return by reference to avoid extra costs in
copies.

PiperOrigin-RevId: 723478744

Co-authored-by: Protobuf Team Bot <protobuf-github-bot@google.com>
  • Loading branch information
zhangskz and protobuf-github-bot authored Feb 5, 2025
1 parent d9ac521 commit 6250d09
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 12 deletions.
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

0 comments on commit 6250d09

Please sign in to comment.