遷移指南

程式庫版本中重大變更的清單,以及如何更新您的程式碼以適應這些變更。

v30.0 版的變更

以下是程式庫版本中重大變更的清單,以及如何更新您的程式碼以適應這些變更。

這涵蓋了 v30.x 的最新消息公告v30.0 的版本資訊 中宣告的重大變更。

將 CMake 子模組替換為擷取的依附元件

先前,我們的預設 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 方法傳回的字串用作

類型遷移

std::string

明確轉換為 std::string 以保留現有的行為。

或者,切換至效能更高的 absl::string_view

const std::string&

遷移至 absl::string_view

如果不可行 (例如由於大量依附元件),則複製到 std::string 可能更容易。

const std::string*

const char*

如果可為 Null,則遷移至 std::optional<absl::string_view>

否則,遷移至 absl::string_view

呼叫 data() 時請小心,因為 absl::string_view 不保證以 Null 終止。

對於常見的容器和其他 API,您可能可以遷移至與 absl::string_view 相容的變體。以下是一些常見的範例。

類別遷移前遷移
插入到 std::vector<std::string>

push_back()

push_front()

push()

emplace_back()

emplace_front()

emplace()

針對 map 或 set 插入

set.insert(key)

map.insert({key, value})

map.insert({key,
{value_params...}})

set.emplace(key)

map.emplace(key, value)

map.try_emplace(key,
value_params...)

針對 map 或 set 查閱

find()

count()

contains()

遷移至 Abseil 容器。

或者,定義透明比較子。

std::set<std::string, std::less<>>
std::map<std::string, T, std::less<>>

請參閱 https://abseil.io/tips/144

字串串連

operator+

operator+=

absl::StrCat()

absl::StrAppend()

無論如何,基於效能考量,建議使用這些方法。請參閱 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",
    ],
)

使用者也可以暫時停用錯誤,方法是在下一個重大版本發佈之前,設定停用旗標 --define=protobuf_allow_msvc=true

或者,希望繼續使用 MSVC 的使用者可以切換為使用 CMake。這可以使用 Visual Studio 完成,或透過 CMake 命令列提供 MSVC 產生器。例如

cmake -G "Visual Studio 17 2022" -A Win64 .

從 FieldDescriptor 選項中移除 ctype

我們已停止公開 FieldDescriptor 選項中的 ctype。您可以使用 v28 版本 中新增的 FieldDescriptor::cpp_string_type() API 來取代它。

修改偵錯 API 以編輯敏感欄位

Protobuf C++ 偵錯 API (包括 Protobuf AbslStringify、proto2::ShortFormatproto2::Utf8FormatMessage::DebugStringMessage::ShortDebugStringMessage::Utf8DebugString) 已變更為編輯以 debug_redact 註解的敏感欄位;這些 API 的輸出包含每個程序隨機化的前置字串,且不再可由 Protobuf TextFormat 剖析器剖析。使用者應針對大多數需要人類可讀取輸出的情況 (例如記錄) 採用新的編輯偵錯格式,或考慮切換至二進位格式以進行序列化和還原序列化。需要舊的可還原序列化格式的使用者可以使用 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

取代項目: 遷移至 Arena (遷移指南)。

API: JsonOptions

取代項目: JsonPrintOptions

已停止支援 C++14

此版本已停止支援 C++ 14 作為最低支援版本,並根據 Foundational C++ Support matrix 將其提高至 17。

使用者應升級至 C++17。

在 Arena 上清除 Oneof 訊息後導入 ASAN Poisoning

此變更新增了強化檢查,會影響使用 Arena 的 C++ protobuf。現在會在偵錯中清除 protobuf arena 上配置的 Oneof 訊息,並在 ASAN 模式中進行 Poisoning。在呼叫 clear 之後,未來嘗試使用記憶體區域將導致 ASAN 中發生當機,因為使用後釋放錯誤。

此實作需要 C++17。

已停止我們的 C++ CocoaPods 版本

我們已停止我們的 C++ CocoaPods 版本,該版本自 v4.x.x 以來已損壞。C++ 使用者應改為直接使用我們的 GitHub 版本

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

欄位 Setter 驗證變更

Python 和 upb 的欄位 setter 現在會根據 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 已標示為已淘汰至少一個次要或主要版本。

反射方法

API: reflection.ParseMessagereflection.MakeClass

取代項目: message_factory.GetMessageClass()

RPC 服務介面

API: service.RpcExceptionservice.Serviceservice.RpcControllerservice.RpcChannel

取代項目: 從 2.3.0 版開始,RPC 實作不應嘗試以此為基礎建置,而應提供程式碼產生器外掛程式,以產生特定於特定 RPC 實作的程式碼。

MessageFactory 和 SymbolDatabase 方法

API: MessageFactory.GetPrototypeMessageFactory.CreatePrototypeMessageFactory.GetMessagesSymbolDatabase.GetPrototypeSymbolDatabase.CreatePrototypeSymbolDatabase.GetMessages

取代項目: message_factory.GetMessageClass()message_factory.GetMessageClassesForFiles()

GetDebugString

API: GetDebugString

取代項目

沒有取代項目。它僅在不再發佈的 Python C++ 中。純 Python 或 UPB 中不支援它。

Python setdefault Map 欄位的行為變更

setdefault 類似於 ScalarMapdict,但必須同時設定索引鍵和值。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。

徹底檢查未知欄位處理 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,這些 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 屬性

取代項目: -[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: +[GPBEnumDescriptor allocDescriptorForName:valueNames:values:count:enumVerifier:extraTextFormatInfo:]

取代項目: 使用程式庫的目前版本重新產生。

API: -[GPBExtensionDescriptor initWithExtensionDescription:]

取代項目: 使用程式庫的目前版本重新產生。

Poison Pill 警告

我們更新了 Poison Pill,針對新的滾動升級原則下可運作,但在下一個主要版本升級中會中斷的舊版 gencode + 新版執行階段組合發出警告。例如,Python 4.x.x gencode 應可針對 5.x.x 執行階段運作,但會警告即將針對 6.x.x 執行階段中斷。

C# 和 Ruby 中 UTF-8 強制執行的變更

我們納入了修正程式,以使 UTF-8 強制執行在各種語言之間保持一致。字串欄位中具有錯誤的非 UTF-8 資料的使用者可能會更早看到浮現的 UTF-8 強制執行錯誤。

JSON 剖析中的 Ruby 和 PHP 錯誤

我們修正了 JSON 剖析中數字欄位中字串的不一致性,根據 JSON 規格

此修正程式未伴隨主要版本升級,但 Ruby 和 PHP 現在會針對數字欄位中的非數字字串 (例如 """12abc""abc") 引發錯誤。v29.x 包含針對這些錯誤案例的警告。

v22.0 版的編譯器變更

JSON 欄位名稱衝突

變更來源:PR #11349PR #10750

我們針對 JSON 映射,在處理欄位名稱衝突的方式上進行了一些細微的變更。在 proto3 中,我們已部分放寬限制,且僅在欄位名稱產生區分大小寫的 JSON 映射 (原始名稱的駝峰式大小寫) 時才會提供錯誤。我們現在也會檢查 json_name 選項,並針對區分大小寫的衝突提供錯誤。在 proto2 中,我們稍微收緊了限制,如果兩個 json_name 規格衝突,則會提供錯誤。如果隱含 JSON 映射 (駝峰式大小寫) 發生衝突,我們將在 proto2 中提供警告。

我們提供了暫時的訊息/列舉選項,以還原舊版行為。如果重新命名衝突欄位不是您可以立即採取的選項,請在特定訊息/列舉上設定 deprecated_legacy_json_field_conflicts 選項。此選項將在未來版本中移除,但可讓您有更多時間進行遷移。

v22.0 版的 C++ API 變更

4.22.0 對於 C++ 執行階段和 protoc 具有重大變更,如 8 月公告 中所述。

Autotools 停用

變更來源:PR #10132

在 v22.0 中,我們從 protobuf 編譯器和 C++ 執行階段中移除了所有 Autotools 支援。如果您使用 Autotools 建置這兩者中的任一者,則必須遷移至 CMakeBazel。我們有一些 專用指示,用於使用 CMake 設定 protobuf。

Abseil 依附元件

變更來源:PR #10416

在 v22.0 中,我們已採用對 Abseil 的明確依附元件。這讓我們能夠移除我們的大部分 stubs,這些 stubs 是從舊版內部程式碼分支出來的,後來成為 Abseil。有一些細微的行為變更,但大多數對於使用者而言應是透明的。一些值得注意的變更包括

  • string_view - 在我們的許多 API 中,absl::string_view 已取代 const std::string&。這最常被用於輸入引數,使用者應不會注意到任何變更。在少數情況下 (例如虛擬方法引數或傳回類型),使用者可能需要明確變更以使用新的簽章。

  • tables - 我們現在使用 Abseil 的 flat_hash_mapflat_hash_setbtree_mapbtree_set,而不是 STL set/map。這些效率更高,並允許 異質查閱。這對於使用者而言應大多是不可見的,但可能會導致一些與表格排序相關的細微行為變更。

  • logging - Abseil 的 logging 程式庫 與我們的舊版記錄程式碼非常相似,只是拼寫略有不同 (例如,ABSL_CHECK 而不是 GOOGLE_CHECK)。最大的差異在於它不支援例外狀況,並且現在在 FATAL 判斷提示失敗時將一律當機。(先前我們有一個 PROTOBUF_USE_EXCEPTIONS 旗標可切換至例外狀況。) 由於這些僅在遇到嚴重問題時才會發生,因此我們認為無條件當機是合適的回應。

    記錄變更的來源:PR #11623

  • 建置依附元件 - 新的建置依附元件始終可能導致下游使用者中斷。我們需要 Abseil LTS 20230125 或更新版本才能建置。

    • 對於 Bazel 建置,當從您的 WORKSPACE 執行 protobuf_deps 時,將會自動下載並建置釘選 LTS 版本的 Abseil。這應是透明的,但如果您依賴舊版 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 程式庫中所做假設的更廣泛工作的一部分,我們已將靜態判斷提示新增至 MapRepeatedFieldRepeatedPtrField 容器。這些可確保您僅將這些容器與預期的類型搭配使用,如我們的文件中所述。如果您遇到這些靜態判斷提示,則應遷移您的程式碼以使用 Abseil 或 STL 容器。std::vector 是重複欄位容器的良好替代品,而 std::unordered_mapabsl::flat_hash_map 則適用於 Map (前者提供類似的指標穩定性,而後者效率更高)。

已清除元素淘汰

變更來源:PR #11588PR #11639

圍繞「已清除欄位」的 RepeatedPtrField API 已被淘汰,並將在稍後的重大版本中完全移除。這最初是作為最佳化而新增的,用於在元素清除後重複使用它們,但最終效果不佳。如果您正在使用此 API,則應考慮遷移至 arena 以獲得更好的記憶體重複使用。

UnsafeArena 淘汰

變更來源:PR #10325

為了移除 arena 不安全的 API,我們隱藏了 RepeatedField::UnsafeArenaSwap。這是目前為止唯一移除的 API,但在之後的版本中,我們會繼續移除這些 API,並提供輔助工具來處理 arena 之間有效率的借用模式。在單一 arena(或堆疊/堆積)中,SwapUnsafeArenaSwap 一樣有效率。好處是,如果您不小心跨不同的 arena 呼叫它,它不會導致無效的記憶體操作。

Map Pair 升級

變更來源:PR #11625

在 v22.0 版本中,我們開始清理 Map API,使其與 Abseil 和 STL 更一致。值得注意的是,我們將 MapPair 類別替換為 std::pair 的別名。這對大多數使用者來說應該是透明的,但如果您直接使用該類別,您可能需要更新您的程式碼。

新的 JSON 剖析器 {:#json-parser}

變更來源:PR #10729

我們在這個版本中重寫了 C++ JSON 解析器。這應該大部分是隱藏的變更,但不可避免地,一些未記載的特性可能已經改變;請相應地進行測試。解析不符合 RFC-8219 JSON 規範的文件(例如那些缺少引號或使用非標準布林值的文件)已被棄用,並將在未來版本中移除。欄位的序列化順序現在保證與欄位編號順序一致,而之前則較不確定。

作為此遷移的一部分,util/internal 下的所有檔案(位於 util/internal)都已被刪除。這些檔案用於舊的解析器,並且從未打算對外部使用。

Arena::Init

變更來源:PR #10623

Arena 中的 Init 方法是沒有任何作用的程式碼,現在已被移除。如果您正在呼叫此方法,您可能原本是想直接使用一組 ArenaOptions 呼叫 Arena 建構子。您應該刪除此呼叫,或遷移到該建構子。

ErrorCollector 遷移

變更來源:PR #11555

作為我們 Abseil 遷移的一部分,我們正從 const std::string& 遷移到 absl::string_view。對於我們的三個錯誤收集器類別,如果不破壞現有程式碼,就無法完成此操作。對於 v22.0 版本,我們決定發布這兩個變體,並將方法從 AddErrorAddWarning 重新命名為 RecordErrorRecordWarning。舊的簽名已被標記為已棄用,並且效率會稍微降低(由於字串複製),但其他方面仍可正常運作。您應該將這些遷移到新版本,因為 Add* 方法將在之後的重大版本中移除。