Introduce `TypeId` as a replacement for `typeid` for protobuf message types.

This class is preferred over using `typeid` for a few reasons:
 - It works with RTTI disabled.
 - It works for `DynamicMessage` types.
 - It works in custom vtable mode.

PiperOrigin-RevId: 645145751
pull/17162/head
Protobuf Team Bot 5 months ago committed by Copybara-Service
parent 6fb2049cfd
commit c826a69107
  1. 2
      src/google/protobuf/BUILD.bazel
  2. 4
      src/google/protobuf/message.cc
  3. 1
      src/google/protobuf/message.h
  4. 11
      src/google/protobuf/message_lite.cc
  5. 74
      src/google/protobuf/message_lite.h
  6. 6
      src/google/protobuf/message_unittest.cc

@ -1587,6 +1587,7 @@ cc_test(
data = [":testdata"],
deps = [
":arena",
":cc_lite_test_protos",
":cc_test_protos",
":internal_visibility",
":port",
@ -1600,6 +1601,7 @@ cc_test(
"//src/google/protobuf/testing",
"//src/google/protobuf/testing:file",
"//src/google/protobuf/util:differencer",
"@com_google_absl//absl/hash:hash_testing",
"@com_google_absl//absl/log:absl_check",
"@com_google_absl//absl/log:scoped_mock_log",
"@com_google_absl//absl/strings",

@ -206,8 +206,8 @@ size_t Message::SpaceUsedLong() const {
return GetClassData()->full().descriptor_methods->space_used_long(*this);
}
static std::string GetTypeNameImpl(const MessageLite& msg) {
return DownCastMessage<Message>(msg).GetDescriptor()->full_name();
absl::string_view Message::GetTypeNameImpl(const ClassData* data) {
return GetMetadataImpl(data->full()).descriptor->full_name();
}
static std::string InitializationErrorStringImpl(const MessageLite& msg) {

@ -389,6 +389,7 @@ class PROTOBUF_EXPORT Message : public MessageLite {
internal::CachedSize* cached_size) const;
// Reflection based version for reflection based types.
static absl::string_view GetTypeNameImpl(const ClassData* data);
static void MergeImpl(MessageLite& to, const MessageLite& from);
static void ClearImpl(MessageLite& msg);
static size_t ByteSizeLongImpl(const MessageLite& msg);

@ -92,16 +92,17 @@ const char* MessageLite::_InternalParse(const char* ptr,
}
std::string MessageLite::GetTypeName() const {
auto* data = GetClassData();
ABSL_DCHECK(data != nullptr);
return std::string(TypeId::Get(*this).name());
}
if (!data->is_lite) {
absl::string_view TypeId::name() const {
if (!data_->is_lite) {
// For !LITE messages, we use the descriptor method function.
return data->full().descriptor_methods->get_type_name(*this);
return data_->full().descriptor_methods->get_type_name(data_);
}
// For LITE messages, the type name is a char[] just beyond ClassData.
return reinterpret_cast<const char*>(data) + sizeof(ClassData);
return reinterpret_cast<const char*>(data_) + sizeof(MessageLite::ClassData);
}
void MessageLite::OnDemandRegisterArenaDtor(Arena* arena) {

@ -123,7 +123,6 @@ class PROTOBUF_EXPORT CachedSize {
};
// For MessageLite to friend.
class TypeId;
auto GetClassData(const MessageLite& msg);
class SwapFieldHelper;
@ -612,13 +611,14 @@ class PROTOBUF_EXPORT MessageLite {
// We use a secondary vtable for descriptor based methods. This way ClassData
// does not grow with the number of descriptor methods. This avoids extra
// costs in MessageLite.
struct ClassData;
struct ClassDataFull;
struct DescriptorMethods {
std::string (*get_type_name)(const MessageLite&);
absl::string_view (*get_type_name)(const ClassData* data);
std::string (*initialization_error_string)(const MessageLite&);
const internal::TcParseTableBase* (*get_tc_table)(const MessageLite&);
size_t (*space_used_long)(const MessageLite&);
};
struct ClassDataFull;
// Note: The order of arguments in the functions is chosen so that it has
// the same ABI as the member function that calls them. Eg the `this`
@ -771,6 +771,7 @@ class PROTOBUF_EXPORT MessageLite {
template <typename T>
static auto GetClassDataGenerated() {
static_assert(std::is_base_of<MessageLite, T>::value, "");
// We could speed this up if needed by avoiding the function call.
// In LTO this is likely inlined, so it might not matter.
static_assert(
@ -831,12 +832,12 @@ class PROTOBUF_EXPORT MessageLite {
friend class FastReflectionStringSetter;
friend class Message;
friend class Reflection;
friend class TypeId;
friend class internal::DescriptorPoolExtensionFinder;
friend class internal::ExtensionSet;
friend class internal::LazyField;
friend class internal::SwapFieldHelper;
friend class internal::TcParser;
friend class internal::TypeId;
friend class internal::UntypedMapBase;
friend class internal::WeakFieldMap;
friend class internal::WireFormatLite;
@ -877,34 +878,70 @@ class PROTOBUF_EXPORT MessageLite {
friend void internal::StrongReferenceToType();
};
namespace internal {
// A typeinfo equivalent for protobuf message types. Used for
// DynamicCastMessage.
// We might make this class public later on to have an alternative to
// `std::type_info` that works when RTTI is disabled.
// A `std::type_info` equivalent for protobuf message types.
// This class is preferred over using `typeid` for a few reasons:
// - It works with RTTI disabled.
// - It works for `DynamicMessage` types.
// - It works in custom vtable mode.
//
// Usage:
// - Instead of `typeid(Type)` use `TypeId::Get<Type>()`
// - Instead of `typeid(expr)` use `TypeId::Get(expr)`
//
// Supports all relationals including <=>, and supports hashing via
// `absl::Hash`.
class TypeId {
public:
constexpr explicit TypeId(const MessageLite::ClassData* data) : data_(data) {}
static TypeId Get(const MessageLite& msg) {
return TypeId(msg.GetClassData());
}
template <typename T>
static TypeId Get() {
return TypeId(MessageLite::GetClassDataGenerated<T>());
}
// Name of the message type.
// Equivalent to `.GetTypeName()` on the message.
absl::string_view name() const;
friend constexpr bool operator==(TypeId a, TypeId b) {
return a.data_ == b.data_;
}
friend constexpr bool operator!=(TypeId a, TypeId b) { return !(a == b); }
friend constexpr bool operator<(TypeId a, TypeId b) {
return a.data_ < b.data_;
}
friend constexpr bool operator>(TypeId a, TypeId b) {
return a.data_ > b.data_;
}
friend constexpr bool operator<=(TypeId a, TypeId b) {
return a.data_ <= b.data_;
}
friend constexpr bool operator>=(TypeId a, TypeId b) {
return a.data_ >= b.data_;
}
static TypeId Get(const MessageLite& msg) {
return TypeId(msg.GetClassData());
#if defined(__cpp_impl_three_way_comparison) && \
__cpp_impl_three_way_comparison >= 201907L
friend constexpr auto operator<=>(TypeId a, TypeId b) {
return a.data_ <=> b.data_;
}
#endif
template <typename T>
static TypeId Get() {
return TypeId(MessageLite::GetClassDataGenerated<T>());
template <typename H>
friend H AbslHashValue(H state, TypeId id) {
return H::combine(std::move(state), id.data_);
}
private:
constexpr explicit TypeId(const MessageLite::ClassData* data) : data_(data) {}
const MessageLite::ClassData* data_;
};
namespace internal {
inline auto GetClassData(const MessageLite& msg) { return msg.GetClassData(); }
template <bool alias>
@ -1006,7 +1043,7 @@ T* OnShutdownDelete(T* p) {
}
inline void AssertDownCast(const MessageLite& from, const MessageLite& to) {
ABSL_DCHECK(internal::TypeId::Get(from) == internal::TypeId::Get(to))
ABSL_DCHECK(TypeId::Get(from) == TypeId::Get(to))
<< "Cannot downcast " << from.GetTypeName() << " to " << to.GetTypeName();
}
@ -1037,8 +1074,7 @@ const T* DynamicCastMessage(const MessageLite* from) {
// We might avoid the call to T::GetClassData() altogether if T were to
// expose the class data pointer.
if (from == nullptr ||
internal::TypeId::Get<T>() != internal::TypeId::Get(*from)) {
if (from == nullptr || TypeId::Get<T>() != TypeId::Get(*from)) {
return nullptr;
}

@ -9,14 +9,20 @@
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#include <algorithm>
#include <vector>
#include <gtest/gtest.h>
#include "absl/hash/hash_testing.h"
#include "absl/log/absl_check.h"
#include "google/protobuf/arena.h"
#include "google/protobuf/explicitly_constructed.h"
#include "google/protobuf/has_bits.h"
#include "google/protobuf/internal_visibility.h"
#include "google/protobuf/message_lite.h"
#include "google/protobuf/unittest.pb.h"
#include "google/protobuf/unittest_import.pb.h"
#include "google/protobuf/unittest_lite.pb.h"
#define MESSAGE_TEST_NAME MessageTest
#define MESSAGE_FACTORY_TEST_NAME MessageFactoryTest

Loading…
Cancel
Save