迁移指南
v30.0 中的变更
以下是对库版本进行的重大变更列表,以及如何更新代码以适应这些变更。
这涵盖了 v30.x 新闻公告 和 v30.0 发布说明 中宣布的重大变更。
用Fetched Deps替换了CMake Submodules
此前,我们默认的 CMake 行为是使用 Git 子模块获取固定版本的依赖项。指定 -Dprotobuf_ABSL_PROVIDER=package
会使我们的 CMake 配置查找 Abseil(以及 jsoncpp 和 gtest 的类似选项)的本地安装。这些选项已不再存在,默认行为是首先查找所有依赖项的安装,如果需要则回退到从 GitHub 获取固定版本。
要防止任何回退获取(类似于旧的 package
行为),您可以使用以下命令调用 CMake
cmake . -Dprotobuf_LOCAL_DEPENDENCIES_ONLY=ON
要始终从固定版本获取依赖项(类似于旧的默认行为),您可以使用以下命令调用 CMake
cmake . -Dprotobuf_FORCE_FETCH_DEPENDENCIES=ON
string_view 返回类型
以下描述符 API 的返回类型现在是 absl::string_view
,这有助于节省内存
MessageLite::GetTypeName
UnknownField::length_delimited
- 描述符 API 名称函数,例如
FieldDescriptor::full_name
我们预计未来的重大版本发布将继续将更多 API 迁移到 absl::string_view
。
在大多数情况下,您应尽量在安全的地方更新类型以使用 absl::string_view
,或在需要的地方显式复制到原始类型。如果此值作为函数返回值,您可能还需要更新调用方。
如果受影响的 API 方法返回的字符串正在被用作
类型 | 迁移 |
---|---|
| 显式转换为 或者,切换到性能更好的 |
| 迁移到 如果不可行(例如由于大量依赖项),复制到 |
| 如果可空,迁移到 否则,迁移到 调用 |
对于常用的容器和其他 API,您可能可以迁移到与 absl::string_view
兼容的变体。下面是一些常见示例。
类别 | 迁移前 | 迁移 |
---|---|---|
插入到 std::vector<std::string> 中 |
|
|
Map 或 Set 的插入 |
|
|
Map 或 Set 的查找 |
| 迁移到 Abseil 容器。 或者,定义一个透明比较器。
|
字符串连接 |
|
无论如何,都推荐出于性能原因使用这些方法。参见 https://abseil.io/tips/3。 |
另请参见 https://abseil.io/tips/1 获取关于使用 absl::string_view
的一般技巧。
Poison MSVC + Bazel
Windows 上的 Bazel 用户应切换到使用 clang-cl,通过将以下内容添加到他们的项目中,如本示例所示。
.bazelrc
common --enable_platform_specific_config build:windows
--extra_toolchains=@local_config_cc//:cc-toolchain-x64_windows-clang-cl
--extra_execution_platforms=//:x64_windows-clang-cl
MODULE.bazel
bazel_dep(name = "platforms", version = "0.0.10")
bazel_dep(name = "rules_cc", version = "0.0.17")
# For clang-cl configuration
cc_configure = use_extension("@rules_cc//cc:extensions.bzl", "cc_configure_extension")
use_repo(cc_configure, "local_config_cc")
WORKSPACE
load("//:protobuf_deps.bzl", "PROTOBUF_MAVEN_ARTIFACTS", "protobuf_deps")
protobuf_deps()
load("@rules_cc//cc:repositories.bzl", "rules_cc_dependencies", "rules_cc_toolchains")
rules_cc_dependencies()
rules_cc_toolchains()
BUILD
仅需要与 Bazel 8 兼容的用户。
platform(
name = "x64_windows-clang-cl",
constraint_values = [
"@platforms//cpu:x86_64",
"@platforms//os:windows",
"@bazel_tools//tools/cpp:clang-cl",
],
)
需要与 Bazel 7 和 8 兼容的用户。
platform(
name = "x64_windows-clang-cl",
constraint_values = [
"@platforms//cpu:x86_64",
"@platforms//os:windows",
# See https://github.com/bazelbuild/rules_cc/issues/330.
"@rules_cc//cc/private/toolchain:clang-cl",
],
)
用户还可以通过设置 opt-out 标志 --define=protobuf_allow_msvc=true
暂时忽略此错误,直到下一个重大版本发布。
另外,希望继续使用 MSVC 的用户可以切换到使用 CMake。这可以通过 Visual Studio 完成,或通过向 CMake 命令行提供 MSVC 生成器来完成。例如
cmake -G "Visual Studio 17 2022" -A Win64 .
从 FieldDescriptor Options 中移除了 ctype
我们停止暴露 FieldDescriptor
options 中的 ctype
。您可以使用在 v28 版本中添加的 FieldDescriptor::cpp_string_type()
API 来代替它。
修改了 Debug API 以去除敏感字段
Protobuf C++ 调试 API(包括 Protobuf AbslStringify, proto2::ShortFormat
, proto2::Utf8Format
, Message::DebugString
, Message::ShortDebugString
, Message::Utf8DebugString
)已更改为去除由 debug_redact
注解的敏感字段;这些 API 的输出包含一个每进程随机前缀,并且不再能被 Protobuf TextFormat Parsers 解析。对于大多数需要人类可读输出(例如日志记录)的情况,用户应采用新的去除敏感信息的调试格式,或者考虑切换到二进制格式进行序列化和反序列化。需要旧的、可反序列化格式的用户可以使用 TextFormat.printer().printToString(proto)
,但这不会去除敏感字段,因此应谨慎使用。
有关此内容的更多信息,请参阅2024 年 12 月 4 日发布的新闻文章。
移除了已弃用的 API
我们移除了以下已弃用的公共运行时 API,这些 API 已至少在一个次要或主要版本中被标记为弃用(例如 ABSL_DEPRECATED
),且已过时或被替换。
API: Arena::CreateMessage
替代方案: Arena::Create
API: Arena::GetArena
替代方案: value->GetArena()
API: RepeatedPtrField::ClearedCount
替代方案: 迁移到 Arenas(迁移指南)。
API: JsonOptions
替代方案: JsonPrintOptions
停止支持 C++14
根据基础 C++ 支持矩阵,此版本将最低支持的 C++ 版本从 14 提高到 17。
用户应升级到 C++17。
在 Arena 上清除 Oneof 消息后引入了 ASAN Poisoning
此变更添加了一项强化检查,影响使用 Arenas 的 C++ protobuf。分配在 protobuf arena 上的 Oneof 消息现在在调试模式下会被清除,并在 ASAN 模式下会被 Poison。调用 clear 后,将来尝试使用该内存区域将在 ASAN 中作为 use-after-free 错误导致崩溃。
此实现需要 C++17。
停止了我们的 C++ CocoaPods 发布
我们停止了自 v4.x.x 以来就已损坏的 C++ CocoaPods 发布。C++ 用户应直接使用我们的 GitHub release。
Python 中的变更
Python 的主要版本从 5.29.x 升级到 6.30.x。
停止支持 Python 3.8
最低支持的 Python 版本为 3.9。用户应升级。
移除了 bazel/system_python.bzl 别名
我们移除了旧的 bazel/system_python.bzl
别名。
移除对 system_python.bzl
的直接引用,改为使用 protobuf_deps.bzl
。如果需要直接引用,请使用 v5.27.0 中移至 python/dist/system_python.bzl
的版本。
字段设置器验证变更
Python 和 upb 的字段设置器现在在 edition 2023 下验证封闭枚举。使用无效值更新封闭枚举字段会生成错误。
移除了已弃用的 py_proto_library 宏
移除了 protobuf.bzl
中已弃用的内部 py_proto_library
Bazel 宏。它已被官方的 py_proto_library
替换,后者在 v29.x 中移至 protobuf 的 bazel/py_proto_library
。此实现此前在 v29.x 之前位于 rules_python
中。
移除已弃用的 API
我们移除了以下已至少在一个次要或主要版本中被标记为弃用的公共运行时 API。
反射方法
API: reflection.ParseMessage
, reflection.MakeClass
替代方案: message_factory.GetMessageClass()
RPC 服务接口
API: service.RpcException
, service.Service
, service.RpcController
, 和 service.RpcChannel
替代方案: 从 2.3.0 版本开始,RPC 实现不应尝试基于这些接口构建,而应提供代码生成器插件来生成特定于特定 RPC 实现的代码。
MessageFactory 和 SymbolDatabase 方法
API: MessageFactory.GetPrototype
, MessageFactory.CreatePrototype
, MessageFactory.GetMessages
, SymbolDatabase.GetPrototype
, SymbolDatabase.CreatePrototype
, 和 SymbolDatabase.GetMessages
替代方案: message_factory.GetMessageClass()
和 message_factory.GetMessageClassesForFiles()
。
GetDebugString
API: GetDebugString
替代方案
没有替代方案。它仅存在于不再发布的 Python C++ 中。纯 Python 或 UPB 中不支持此 API。
Python Map 字段的 setdefault 行为变更
setdefault
对于 ScalarMap
类似于 dict
,但键和值都必须设置。setdefault
对 MessageMaps
无效。
Python 嵌套消息类 __qualname__ 包含外部消息名称
Python 嵌套消息类 __qualname__
现在包含外部消息名称。此前,对于嵌套消息,__qualname__
与 __name__
的结果相同,即不包含外部消息名称。
例如
message Foo {
message Bar {
bool bool_field = 1;
}
}
nested = test_pb2.Foo.Bar()
self.assertEqual('Bar', nested.__class__.__name__)
self.assertEqual('Foo.Bar', nested.__class__.__qualname__) # It was 'Bar' before
Objective-C 中的变更
这是 Objective-C 的第一个重大版本发布.
Objective-C 的主要版本从 3.x.x 升级到 4.30.x。
重构了 Unknown Field Handling API,弃用了大多数现有 API
我们弃用了 GPBUnknownFieldSet
并将其替换为 GPBUnknownFields
。新类型保留了来自原始输入或 API 调用的未知字段的顺序,以确保在消息写回时保留顺序的任何语义含义。
作为此项工作的一部分,GPBUnknownField
类型也进行了 API 变更,几乎所有现有 API 都被弃用并添加了新的 API。
已弃用的属性 API
varintList
fixed32List
fixed64List
lengthDelimitedList
groupList
已弃用的修改 API
addVarint
addFixed32
addFixed64
addLengthDelimited
addGroup
已弃用的初始化器 initWithNumber:
。
新的属性 API
type
varint
fixed32
fixed64
lengthDelimited
group
此类型在其值中建模单个字段编号,而不是将给定字段编号的所有值分组。用于创建新字段的 API 是 GPBUnknownFields
类上的 add*
API。
我们还弃用了 -[GPBMessage unknownFields]
。取而代之的是新的 API,用于提取和更新消息的未知字段。
移除了已弃用的 API
我们移除了以下已至少在一个次要或主要版本中被标记为弃用的公共运行时 API。
GPBFileDescriptor
API: -[GPBFileDescriptor
syntax]
替代方案: 已废弃。
GPBMessage mergeFrom:extensionRegistry
API: -[GPBMessage mergeFrom:extensionRegistry:
]
替代方案: -[GPBMessage mergeFrom:extensionRegistry:error:
]
GPBDuration timeIntervalSince1970
API: -[GPBDuration timeIntervalSince1970
]
替代方案: -[GPBDuration timeInterval
]
GPBTextFormatForUnknownFieldSet
API: GPBTextFormatForUnknownFieldSet()
替代方案: 已废弃 - 使用 GPBTextFormatForMessage()
,它包含所有未知字段。
GPBUnknownFieldSet
API: GPBUnknownFieldSet
替代方案: GPBUnknownFields
GPBMessage unknownFields
API: GPBMessage unknownFields
property
替代方案: -[GPBUnknownFields initFromMessage:
], -[GPBMessage mergeUnknownFields:extensionRegistry:error:
], 和 -[GPBMessage clearUnknownFields
]
移除了旧 Gencode 的已弃用运行时 API
此版本移除了支持 3.22.x 版本之前 Objective-C gencode 的已弃用运行时方法。当旧的生成代码启动时,库还会发出运行时日志消息。
API: +[GPBFileDescriptor allocDescriptorForClass:file:fields:fieldCount:storageSize:flags:]
替代方案: 使用当前版本的库重新生成。
API: +[GPBFileDescriptor allocDescriptorForClass:rootClass:file:fields:fieldCount:storageSize:flags:]
替代方案: 使用当前版本的库重新生成。
API: +[GPBEnumDescriptor allocDescriptorForName:valueNames:values:count:enumVerifier:]
替代方案: 使用当前版本的库重新生成。
替代方案: 使用当前版本的库重新生成。
API: -[GPBExtensionDescriptor initWithExtensionDescription:]
替代方案: 使用当前版本的库重新生成。
Poison Pill 警告
我们更新了 poison pills,使其对在新的滚动升级策略下工作但会在下一个主要版本更新中破坏的旧 gencode + 新 runtime 组合发出警告。例如,Python 4.x.x gencode 应该可以在 5.x.x runtime 下工作,但会警告在 6.x.x runtime 下即将出现的破坏。
C# 和 Ruby 中 UTF-8 强制执行的变更
我们包含了一个修复程序,使 UTF-8 强制执行在不同语言中保持一致。在字符串字段中包含非 UTF-8 数据的用户可能会更早地看到 UTF-8 强制执行错误。
Ruby 和 PHP 在 JSON 解析中的错误
我们修复了根据 JSON 规范对数字字段中字符串进行 JSON 解析时存在的非一致性问题。
此修复没有伴随主要版本更新,但 Ruby 和 PHP 现在会对数字字段中的非数字字符串(例如 ""
, "12abc"
, "abc"
)引发错误。v29.x 对这些错误情况包含警告。
v22.0 中的编译器变更
JSON 字段名称冲突
我们在处理与 JSON 映射相关的字段名称冲突方面做了一些细微的改变。在 proto3 中,我们部分放松了限制,仅在字段名称产生区分大小写的 JSON 映射(原始名称的驼峰命名法)时给出错误。我们现在也检查 json_name
选项,并对区分大小写的冲突给出错误。在 proto2 中,我们稍微收紧了限制,如果两个 json_name
规范冲突,我们将给出错误。如果隐式 JSON 映射(驼峰命名法)存在冲突,我们将在 proto2 中给出警告。
我们提供了一个临时的消息/枚举选项,用于恢复旧的行为。如果立即重命名冲突字段不是一个可行的选择,请在特定的消息/枚举上设置 deprecated_legacy_json_field_conflicts
选项。此选项将在未来的版本中移除,但这为您提供了更多时间进行迁移。
v22.0 中的 C++ API 变更
4.22.0 版本对 C++ 运行时和 protoc 有重大变更,如 八月份公布的。
Autotools 停用
变更来源: PR #10132
在 v22.0 中,我们从 protobuf 编译器和 C++ 运行时中移除了所有 Autotools 支持。如果您使用 Autotools 构建其中任何一个,则必须迁移到 CMake 或 Bazel。我们提供了一些 专门说明 关于使用 CMake 设置 protobuf 的信息。
Abseil 依赖
变更来源: PR #10416
在 v22.0 中,我们明确依赖于 Abseil。这使我们能够移除大部分 stubs,这些 stubs 是从后来成为 Abseil 的旧内部代码分支出来的。存在一些细微的行为变化,但大多数对用户应该是透明的。一些值得注意的变更包括
string_view -
absl::string_view
在我们的许多 API 中取代了const std::string&
。这最常用于输入参数,用户应该不会注意到变化。在少数情况下(例如虚方法参数或返回类型),用户可能需要进行显式更改才能使用新的签名。表格 (tables) - 我们现在使用 Abseil 的
flat_hash_map
,flat_hash_set
,btree_map
, 和btree_set
,而不是 STL 的 sets/maps。这些容器效率更高,并支持 异构查找。这对于用户来说大部分应该是不可见的,但也可能导致一些与表格排序相关的细微行为变化。日志记录 (logging) - Abseil 的 日志库 与我们旧的日志代码非常相似,只是拼写略有不同(例如,
ABSL_CHECK
而不是GOOGLE_CHECK
)。最大的区别在于它不支持异常,并且当FATAL
断言失败时将始终崩溃。(之前我们有一个PROTOBUF_USE_EXCEPTIONS
标志可以切换到异常。)由于这些只在遇到严重问题时发生,我们认为无条件崩溃是合适的响应。日志变更来源: PR #11623
构建依赖 (Build dependency) - 新的构建依赖项总是可能对下游用户造成破坏。我们要求 Abseil LTS 20230125 或更高版本才能构建。
对于 Bazel 构建,当从您的
WORKSPACE
运行protobuf_deps
时,Abseil 将自动下载并构建一个固定的 LTS 版本。这应该是透明的,但如果您依赖于旧版本的 Abseil,则需要升级您的依赖。对于 CMake 构建,我们将首先查找由顶层 CMake 配置拉取的现有 Abseil 安装(参见说明)。否则,如果
protobuf_ABSL_PROVIDER
设置为module
(其默认值),我们将尝试从我们的 git 子模块构建和链接 Abseil。如果protobuf_ABSL_PROVIDER
设置为package
,我们将查找预安装的系统版本 Abseil。
GetCurrentTime 方法中的变更
在 Windows 上,GetCurrentTime()
是系统提供的一个宏的名称。在 v22.x 之前,Protobuf 错误地移除了 GetCurrentTime()
的宏定义。这使得 Windows 开发者在包含 <protobuf/util/time_util.h>
后无法使用该宏。从 v22.x 开始,Protobuf 保留了宏定义。这可能会破坏依赖于先前行为的客户代码,例如如果他们使用表达式 google::protobuf::util::TimeUtil::GetCurrentTime()
。
要将您的应用程序迁移到新行为,请更改您的代码以执行以下操作之一
- 如果定义了
GetCurrent
宏,则显式取消定义GetCurrentTime
宏 - 通过使用
(google::protobuf::util::TimeUtil::GetCurrentTime)()
或类似的表达式来阻止宏扩展
示例:取消定义宏
如果您不使用 Windows 的宏,请使用此方法。
之前
#include <google/protobuf/util/time_util.h>
void F() {
auto time = google::protobuf::util::TimeUtil::GetCurrentTime();
}
之后
#include <google/protobuf/util/time_util.h>
#ifdef GetCurrentTime
#undef GetCurrentTime
#endif
void F() {
auto time = google::protobuf::util::TimeUtil::GetCurrentTime();
}
示例 2:阻止宏扩展
之前
#include <google/protobuf/util/time_util.h>
void F() {
auto time = google::protobuf::util::TimeUtil::GetCurrentTime();
}
之后
#include <google/protobuf/util/time_util.h>
void F() {
auto time = (google::protobuf::util::TimeUtil::GetCurrentTime)();
}
C++20 支持
变更来源: PR #10796
为了支持 C++20,我们在 C++ 生成的 protobuf 代码中保留了新的关键字。与处理其他保留关键字一样,如果您将这些关键字用于任何字段、枚举或消息,我们将添加下划线后缀使其成为有效的 C++ 代码。例如,一个 concept
字段将生成一个 concept_()
getter。如果您现有的 proto 文件使用了这些关键字,则需要更新引用它们的 C++ 代码以添加相应的下划线。
Final 类
变更来源: PR #11604
作为强化 Protobuf 库中假设的一项更广泛工作的一部分,我们将一些从未打算被继承的类标记为 final
。没有已知的使用案例需要继承这些类,并且这样做可能会导致问题。如果您的代码正在继承这些类,目前没有缓解措施,但如果您认为您有使用继承的合理原因,可以提交一个问题。
容器静态断言
变更来源: PR #11550
作为强化 Protobuf 库中假设的一项更广泛工作的一部分,我们向 Map
, RepeatedField
, 和 RepeatedPtrField
容器添加了静态断言。这些断言确保您只使用预期类型与这些容器一起使用,如我们的文档所述。如果触发了这些静态断言,您应该迁移代码以使用 Abseil 或 STL 容器。std::vector
是 repeated field 容器的一个很好的直接替代品,而 std::unordered_map
或 absl::flat_hash_map
可以用于 Map
(前者提供类似的指针稳定性,而后者更高效)。
已清除元素弃用
RepeatedPtrField
中关于“已清除字段”的 API 已被弃用,并将在未来的重大版本发布中完全移除。这最初是作为在元素被清除后重用内存的优化添加的,但最终效果不佳。如果您正在使用此 API,应考虑迁移到 arenas 以获得更好的内存重用。
UnsafeArena 弃用
变更来源: PR #10325
作为移除 arena-unsafe API 的一项更广泛工作的一部分,我们隐藏了 RepeatedField::UnsafeArenaSwap
。这是我们目前移除的唯一一个,但在未来的版本中,我们将继续移除这些 API,并提供帮助函数来处理 arena 之间的有效借用模式。在单个 arena(或栈/堆)内,Swap
与 UnsafeArenaSwap
一样高效。其好处是,如果您不小心在不同的 arena 之间调用它,它不会导致无效内存操作。
Map Pair 升级
变更来源: PR #11625
对于 v22.0,我们开始清理 Map
API,使其与 Abseil 和 STL 更加一致。值得注意的是,我们将 MapPair
类替换为 std::pair
的别名。这对大多数用户应该是透明的,但如果您直接使用了该类,可能需要更新代码。
新的 JSON 解析器
变更来源: PR #10729
此版本我们重写了 C++ JSON 解析器。这应该是一个大部分隐藏的变更,但不可避免地,一些未记录的怪异行为可能已改变;请相应地进行测试。解析无效的 RFC-8219 JSON 文档(例如缺少引号或使用非标准布尔值的文档)已被弃用,并将在未来版本中移除。字段的序列化顺序现在保证与字段编号顺序一致,而之前则不那么确定。
作为此迁移的一部分,util/internal 下的所有文件已被删除。这些文件用于旧的解析器,且从未打算供外部使用。
Arena::Init
变更来源: PR #10623
Arena
中的 Init
方法是一段没有任何作用的代码,现已移除。如果您调用了此方法,您可能本意是直接使用一组 ArenaOptions
调用 Arena
构造函数。您应删除此调用或迁移到该构造函数。
ErrorCollector 迁移
变更来源: PR #11555
作为 Abseil 迁移的一部分,我们正从 const std::string&
转向 absl::string_view
。对于我们的三个错误收集器类,不破坏现有代码就无法完成此操作。在 v22.0 中,我们决定同时发布这两个变体,并将方法名从 AddError
和 AddWarning
重命名为 RecordError
和 RecordWarning
。旧的签名已被标记为弃用,并且效率会略低(由于字符串复制),但仍可正常工作。您应将这些方法迁移到新版本,因为 Add*
方法将在未来的重大版本发布中移除。