String View API
使用 std::string 的 C++ 字串欄位 API 顯著限制了 protobuf 內部實現及其演進。例如,mutable_string_field() 返回 std::string*,這迫使我們使用 std::string 來儲存欄位。這使得它與競技場(arena)的互動變得複雜,我們必須維護競技場捐贈狀態以跟蹤字串負載分配是來自競技場還是堆。
從長遠來看,我們希望將所有執行時和生成的 API 遷移為接受 string_view 作為輸入並從訪問器返回它們。本文件描述了截至 30.x 版本遷移的狀態。
字串欄位訪問器
作為 2023 年版本的一部分,string_type 功能釋出了 VIEW 選項,以允許逐步遷移到生成的 string_view API。使用此功能將影響 string 和 bytes 欄位的 C++ 生成程式碼。
與 ctype 的互動
在 2023 年版本中,您仍然可以在欄位級別指定 ctype,同時可以在檔案或欄位級別指定 string_type。不允許在同一欄位上同時指定兩者。如果 string_type 在檔案級別設定,欄位上指定的 ctype 將優先。
除了 VIEW 選項,string_type 的所有可能值都有一個對應的 ctype 值,其拼寫相同並提供相同的行為。例如,兩個列舉都有一個 CORD 值。
在 2024 年及以後的版本中,將不再可能指定 ctype。
生成的單一欄位
對於 2023 年版本中的以下任一欄位定義
bytes foo = 1 [features.(pb.cpp).string_type=VIEW];
string foo = 1 [features.(pb.cpp).string_type=VIEW];
編譯器將生成以下訪問器方法
::absl::string_view foo() const: 返回欄位的當前值。如果欄位未設定,則返回預設值。void clear_foo(): 清除欄位的值。呼叫此方法後,foo()將返回預設值。bool has_foo(): 如果欄位已設定,則返回true。void set_foo(::absl::string_view value): 設定欄位的值。呼叫此方法後,has_foo()將返回true,foo()將返回value的副本。void set_foo(const string& value): 設定欄位的值。呼叫此方法後,has_foo()將返回true,foo()將返回value的副本。void set_foo(string&& value): 設定欄位的值,從傳入的字串移動。呼叫此方法後,has_foo()將返回true,foo()將返回value。void set_foo(const char* value): 使用 C 風格的以 null 結尾的字串設定欄位的值。呼叫此方法後,has_foo()將返回true,foo()將返回value的副本。
生成的重複欄位
對於以下任一欄位定義
repeated string foo = 1 [features.(pb.cpp).string_type=VIEW];
repeated bytes foo = 1 [features.(pb.cpp).string_type=VIEW];
編譯器將生成以下訪問器方法
int foo_size() const: 返回欄位中當前元素的數量。::absl::string_view foo(int index) const: 返回給定基於零索引處的元素。使用超出[0, foo_size()-1]範圍的索引呼叫此方法會導致未定義行為。void set_foo(int index, ::absl::string_view value): 設定給定基於零索引處元素的值。void set_foo(int index, const string& value): 設定給定基於零索引處元素的值。void set_foo(int index, string&& value): 設定給定基於零索引處元素的值,從傳入的字串移動。void set_foo(int index, const char* value): 使用 C 風格的以 null 結尾的字串設定給定基於零索引處元素的值。void add_foo(::absl::string_view value): 將一個新元素附加到欄位的末尾,並賦予給定值。void add_foo(const string& value): 將一個新元素附加到欄位的末尾,並賦予給定值。void add_foo(string&& value): 將一個新元素附加到欄位的末尾,從傳入的字串移動。void add_foo(const char* value): 使用 C 風格的以 null 結尾的字串將一個新元素附加到欄位的末尾。void clear_foo(): 從欄位中移除所有元素。呼叫此方法後,foo_size()將返回零。const RepeatedPtrField<string>& foo() const: 返回儲存欄位元素的底層RepeatedPtrField。此容器類提供類似 STL 的迭代器和其他方法。RepeatedPtrField<string>* mutable_foo(): 返回指向儲存欄位元素的底層可變RepeatedPtrField的指標。此容器類提供類似 STL 的迭代器和其他方法。
生成的 Oneof 欄位
對於以下任一 oneof 欄位定義
oneof example_name {
string foo = 1 [features.(pb.cpp).string_type=VIEW];
...
}
oneof example_name {
bytes foo = 1 [features.(pb.cpp).string_type=VIEW];
...
}
編譯器將生成以下訪問器方法
bool has_foo() const: 如果 oneof 情況是kFoo,則返回true。::absl::string_view foo() const: 如果 oneof 情況是kFoo,則返回欄位的當前值。否則,返回預設值。void set_foo(::absl::string_view value):- 如果同一 oneof 中的任何其他 oneof 欄位已設定,則呼叫
clear_example_name()。 - 設定此欄位的值並將 oneof 情況設定為
kFoo。 has_foo()將返回true,foo()將返回value的副本,example_name_case()將返回kFoo。
- 如果同一 oneof 中的任何其他 oneof 欄位已設定,則呼叫
void set_foo(const string& value): 與第一個set_foo()類似,但從const string引用複製。void set_foo(string&& value): 與第一個set_foo()類似,但從傳入的字串移動。void set_foo(const char* value): 與第一個set_foo()類似,但從 C 風格的以 null 結尾的字串複製。void clear_foo():- 如果 oneof 情況不是
kFoo,則不會更改任何內容。 - 如果 oneof 情況是
kFoo,則釋放欄位並清除 oneof 情況。has_foo()將返回false,foo()將返回預設值,並且example_name_case()將返回EXAMPLE_NAME_NOT_SET。
- 如果 oneof 情況不是
列舉名稱助手
從 2024 年版本開始,引入了一個新功能 enum_name_uses_string_view,並預設為 true。除非停用,對於像這樣的列舉
enum Foo {
VALUE_A = 0;
VALUE_B = 5;
VALUE_C = 1234;
}
除了 Foo 列舉之外,協議緩衝區編譯器還將生成以下新函式,作為標準 生成程式碼 的補充
::absl::string_view Foo_Name(int value): 返回給定數值的名稱。如果不存在此類值,則返回空字串。如果有多個值具有此數字,則返回第一個定義的值。在上面的示例中,Foo_Name(5)將返回VALUE_B。
這可以透過新增如下功能覆蓋來恢復為舊行為
enum Foo {
option features.(pb.cpp).enum_name_uses_string_view = false;
VALUE_A = 0;
VALUE_B = 5;
VALUE_C = 1234;
}
在這種情況下,名稱助手將切換回 const string& Foo_Name(int value)。