String View API

string_view のさまざまな移行について説明します

std::string を使用する C++ 文字列フィールド API は、protobuf の内部実装とその進化を大きく制約します。たとえば、mutable_string_field()std::string* を返すため、フィールドの格納に std::string を使用せざるを得ません。これにより、アリーナでの相互作用が複雑になり、文字列ペイロードの割り当てがアリーナからかヒープからかを追跡するためにアリーナ寄贈状態を維持する必要があります。

長期的には、すべてのランタイムおよび生成された 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 値があります。たとえば、どちらの enum も 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 スタイルのヌル終端文字列を使用してフィールドの値を設定します。これを呼び出すと、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 ベースのインデックスにある要素を返します。このメソッドを [0, foo_size()-1] の範囲外のインデックスで呼び出すと、未定義の動作になります。
  • void set_foo(int index, ::absl::string_view value): 指定された 0 ベースのインデックスにある要素の値を設定します。
  • void set_foo(int index, const string& value): 指定された 0 ベースのインデックスにある要素の値を設定します。
  • void set_foo(int index, string&& value): 指定された 0 ベースのインデックスにある要素の値を設定し、渡された文字列から移動します。
  • void set_foo(int index, const char* value): C スタイルのヌル終端文字列を使用して、指定された 0 ベースのインデックスにある要素の値を設定します。
  • 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 スタイルのヌル終端文字列を使用して、新しい要素をフィールドの末尾に追加します。
  • 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 を返します。
  • 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 スタイルのヌル終端文字列からコピーします。
  • void clear_foo():
    • oneof ケースが kFoo でない場合、何も変更されません。
    • oneof ケースが kFoo の場合、フィールドを解放し、oneof ケースをクリアします。has_foo()false を返し、foo() はデフォルト値を返し、example_name_case()EXAMPLE_NAME_NOT_SET を返します。

Enum 名ヘルパー

エディション 2024 以降では、新しい機能 enum_name_uses_string_view が導入され、デフォルトで true になります。無効にしない限り、次のような enum の場合

enum Foo {
  VALUE_A = 0;
  VALUE_B = 5;
  VALUE_C = 1234;
}

protocol buffer コンパイラは、Foo enum に加えて、標準の生成されたコードに加えて、次の新しい関数を生成します

  • ::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) に戻ります。