C++ 生成コードガイド

Protocol Buffer コンパイラが、任意のプロトコル定義に対してどのような C++ コードを生成するかを正確に説明します。

proto2 と proto3 の生成コードの違いが強調されています。これらの違いは、このドキュメントで説明されている生成コードにあり、両方のバージョンで同じである基本メッセージクラス/インターフェースにはありません。このドキュメントを読む前に、proto2 言語ガイドおよび/またはproto3 言語ガイドを読むことをお勧めします。

コンパイラの起動

Protocol Buffer コンパイラは、`--cpp_out=` コマンドラインフラグを付けて呼び出されたときに C++ 出力を生成します。`--cpp_out=` オプションのパラメーターは、コンパイラが C++ 出力を書き込むディレクトリです。コンパイラは、各 `.proto` ファイル入力に対してヘッダーファイルと実装ファイルを作成します。出力ファイルの名前は、`.proto` ファイルの名前から次の2つの変更を行って計算されます

  • 拡張子 (`.proto`) は、それぞれヘッダーファイルまたは実装ファイルの場合、`.pb.h` または `.pb.cc` に置き換えられます。
  • proto パス (`--proto_path=` または `-I` コマンドラインフラグで指定) は、出力パス (`--cpp_out=` フラグで指定) に置き換えられます。

例えば、次のようにコンパイラを呼び出したとします

protoc --proto_path=src --cpp_out=build/gen src/foo.proto src/bar/baz.proto

コンパイラは `src/foo.proto` および `src/bar/baz.proto` のファイルを読み込み、4つの出力ファイル: `build/gen/foo.pb.h`、`build/gen/foo.pb.cc`、`build/gen/bar/baz.pb.h`、`build/gen/bar/baz.pb.cc` を生成します。コンパイラは必要に応じてディレクトリ `build/gen/bar` を自動的に作成しますが、`build` または `build/gen` は作成しません。これらは既に存在している必要があります。

パッケージ

`.proto` ファイルに `package` 宣言が含まれている場合、ファイルの内容全体は対応する C++ 名前空間に配置されます。例えば、次の `package` 宣言の場合

package foo.bar;

ファイル内のすべての宣言は `foo::bar` 名前空間に存在します。

メッセージ

簡単なメッセージ宣言の場合

message Foo {}

Protocol Buffer コンパイラは、`google::protobuf::Message` から公開継承する `Foo` という名前のクラスを生成します。このクラスは具象クラスであり、純粋仮想メソッドは未実装のまま残りません。`Message` で仮想であっても純粋仮想ではないメソッドは、最適化モードに応じて `Foo` によってオーバーライドされる場合とされない場合があります。デフォルトでは、`Foo` は最大限の速度のためにすべてのメソッドの特殊なバージョンを実装します。しかし、`.proto` ファイルに次の行が含まれている場合

option optimize_for = CODE_SIZE;

その場合、`Foo` は機能に必要な最小限のメソッドのみをオーバーライドし、残りはリフレクションベースの実装に依存します。これにより、生成されるコードのサイズが大幅に削減されますが、パフォーマンスも低下します。代わりに、`.proto` ファイルに次の内容が含まれている場合

option optimize_for = LITE_RUNTIME;

その場合、`Foo` はすべてのメソッドの高速な実装を含みますが、`Message` のメソッドのサブセットのみを含む`google::protobuf::MessageLite` インターフェースを実装します。具体的には、ディスクリプターやリフレクションをサポートしません。ただし、このモードでは、生成されたコードは `libprotobuf.so` (`libprotobuf.lib`) の代わりに `libprotobuf-lite.so` (`libprotobuf-lite.lib` on Windows) にリンクするだけで済みます。「lite」ライブラリはフルライブラリよりもはるかに小さく、携帯電話などのリソースに制約のあるシステムに適しています。

独自の `Foo` サブクラスを作成するべきではありません。このクラスをサブクラス化して仮想メソッドをオーバーライドしても、多くの生成されたメソッド呼び出しはパフォーマンス向上のために非仮想化されるため、そのオーバーライドは無視される可能性があります。

`Message` インターフェースは、メッセージ全体をチェック、操作、読み取り、書き込みできるメソッドを定義しており、バイナリ文字列からの解析やバイナリ文字列へのシリアライズも含まれます。

  • `bool ParseFromString(::absl::string_view data)`: 指定されたシリアライズされたバイナリ文字列 (ワイヤーフォーマットとも呼ばれます) からメッセージを解析します。
  • `bool SerializeToString(string* output) const`: 指定されたメッセージをバイナリ文字列にシリアライズします。
  • `string DebugString()`: proto の `text_format` 表現を示す文字列を返します (デバッグ目的でのみ使用してください)。

これらのメソッドに加えて、`Foo` クラスは以下のメソッドを定義します。

  • `Foo()`: デフォルトコンストラクタ。
  • `~Foo()`: デフォルトデストラクタ。
  • `Foo(const Foo& other)`: コピーコンストラクタ。
  • `Foo(Foo&& other)`: ムーブコンストラクタ。
  • `Foo& operator=(const Foo& other)`: 代入演算子。
  • `Foo& operator=(Foo&& other)`: ムーブ代入演算子。
  • `void Swap(Foo* other)`: 別のメッセージと内容を交換します。
  • `const UnknownFieldSet& unknown_fields() const`: このメッセージの解析中に検出された不明なフィールドのセットを返します。`.proto` ファイルで `option optimize_for = LITE_RUNTIME` が指定されている場合、戻り値の型は `std::string&` に変更されます。
  • `UnknownFieldSet* mutable_unknown_fields()`: このメッセージの解析中に検出された不明なフィールドの変更可能なセットへのポインターを返します。`.proto` ファイルで `option optimize_for = LITE_RUNTIME` が指定されている場合、戻り値の型は `std::string*` に変更されます。

注: コピーコンストラクタと代入演算子は、メッセージデータのディープコピーを実行します。これにより、各メッセージオブジェクトが独自のデータのコピーを所有および管理し、二重解放や解放後使用エラーなどの問題を防止します。この動作は、`std::vector` のようなデータを所有するオブジェクトの標準的な C++ の慣行と一致します。異なるコピーセマンティクスを持つ言語 (JavaScript や TypeScript など、シャローコピーがより一般的である場合がある) から来た開発者にとっては、コピーされたメッセージへの変更が元のメッセージに影響を与えず、その逆も同様であることに注意することが重要です。

このクラスは以下の静的メソッドも定義します。

  • `static const Descriptor* descriptor()`: 型のディスクリプターを返します。これには、型に関する情報 (持つフィールドとその型を含む) が含まれます。これは、リフレクションを使用してフィールドをプログラムで検査するために使用できます。
  • `static const Foo& default_instance()`: 新しく構築された `Foo` のインスタンスと同一の `Foo` の const シングルトンインスタンスを返します (したがって、すべての単一フィールドは設定されておらず、すべての繰り返しフィールドは空です)。メッセージのデフォルトインスタンスは、`New()` メソッドを呼び出すことでファクトリとして使用できることに注意してください。

生成されるファイル名

予約キーワードには、生成された出力でアンダースコアが追加されます。

例えば、以下の proto3 定義構文の場合

message MyMessage {
  string false = 1;
  string myFalse = 2;
}

次の部分的な出力を生成します。

  void clear_false_() ;
  const std::string& false_() const;
  void set_false_(Arg_&& arg, Args_... args);
  std::string* mutable_false_();
  PROTOBUF_NODISCARD std::string* release_false_();
  void set_allocated_false_(std::string* ptr);

  void clear_myfalse() ;
  const std::string& myfalse() const;
  void set_myfalse(Arg_&& arg, Args_... args);
  std::string* mutable_myfalse();
  PROTOBUF_NODISCARD std::string* release_myfalse();
  void set_allocated_myfalse(std::string* ptr);

ネストされた型

メッセージは別のメッセージ内に宣言できます。例えば

message Foo {
  message Bar {}
}

この場合、コンパイラは `Foo` と `Foo_Bar` の2つのクラスを生成します。さらに、コンパイラは `Foo` 内に次のような typedef を生成します。

typedef Foo_Bar Bar;

これは、ネストされた型のクラスをネストされたクラス `Foo::Bar` のように使用できることを意味します。ただし、C++ ではネストされた型の前方宣言が許可されていないことに注意してください。別のファイルで `Bar` を前方宣言してその宣言を使用したい場合は、`Foo_Bar` として識別する必要があります。

フィールド

前のセクションで説明したメソッドに加えて、Protocol Buffer コンパイラは、`.proto` ファイルのメッセージ内に定義された各フィールドに対して一連のアクセサーメソッドを生成します。これらのメソッドは小文字/スネークケースであり、`has_foo()` や `clear_foo()` などがあります。

アクセサーメソッドと同様に、コンパイラは各フィールドにそのフィールド番号を含む整数定数を生成します。定数名は文字 `k` の後にフィールド名をキャメルケースに変換したもの、そして `FieldNumber` が続きます。例えば、`optional int32 foo_bar = 5;` というフィールドが与えられた場合、コンパイラは定数 `static const int kFooBarFieldNumber = 5;` を生成します。

`const` 参照を返すフィールドアクセサーの場合、その参照は、メッセージに対して次に変更アクセスが行われたときに無効になる可能性があります。これには、任意のフィールドの非 `const` アクセサーの呼び出し、`Message` から継承された非 `const` メソッドの呼び出し、またはその他の方法 (例えば、メッセージを `Swap()` の引数として使用するなど) でメッセージを変更することが含まれます。それに伴い、返された参照のアドレスは、その間にメッセージへの変更アクセスが行われなかった場合にのみ、アクセサーの異なる呼び出し間で同じであることが保証されます。

ポインターを返すフィールドアクセサーの場合、そのポインターは、メッセージに対して次に変更または非変更アクセスが行われたときに無効になる可能性があります。これには、constness にかかわらず、任意のフィールドのアクセサーの呼び出し、`Message` から継承されたメソッドの呼び出し、またはその他の方法 (例えば、コピーコンストラクタを使用してメッセージをコピーするなど) でメッセージにアクセスすることが含まれます。それに伴い、返されたポインターの値は、アクセサーの2つの異なる呼び出し間で同じであることが保証されることはありません。

オプションの数値フィールド (proto2 および proto3)

これらのフィールド定義のいずれかの場合

optional int32 foo = 1;
required int32 foo = 1;

コンパイラは次のアクセサーメソッドを生成します。

  • `bool has_foo() const`: フィールドが設定されている場合 `true` を返します。
  • `int32 foo() const`: フィールドの現在の値を返します。フィールドが設定されていない場合、デフォルト値を返します。
  • `void set_foo(int32 value)`: フィールドの値を設定します。これを呼び出した後、`has_foo()` は `true` を返し、`foo()` は `value` を返します。
  • `void clear_foo()`: フィールドの値をクリアします。これを呼び出した後、`has_foo()` は `false` を返し、`foo()` はデフォルト値を返します。

その他の数値フィールド型 (`bool` を含む) の場合、`int32` はスカラー値型の表に従って対応する C++ 型に置き換えられます。

暗黙的な存在の数値フィールド (proto3)

以下のフィールド定義の場合

int32 foo = 1;  // no field label specified, defaults to implicit presence.

コンパイラは次のアクセサーメソッドを生成します。

  • `int32 foo() const`: フィールドの現在の値を返します。フィールドが設定されていない場合、0 を返します。
  • `void set_foo(int32 value)`: フィールドの値を設定します。これを呼び出した後、`foo()` は `value` を返します。
  • `void clear_foo()`: フィールドの値をクリアします。これを呼び出した後、`foo()` は 0 を返します。

その他の数値フィールド型 (`bool` を含む) の場合、`int32` はスカラー値型の表に従って対応する C++ 型に置き換えられます。

オプションの文字列/バイトフィールド (proto2 および proto3)

注: 2023年版以降、`features.(pb.cpp).string_type` が `VIEW` に設定されている場合、string_view API が代わりに生成されます。

これらのフィールド定義のいずれかの場合

optional string foo = 1;
required string foo = 1;
optional bytes foo = 1;
required bytes foo = 1;

コンパイラは次のアクセサーメソッドを生成します。

  • `bool has_foo() const`: フィールドが設定されている場合 `true` を返します。
  • `const string& foo() const`: フィールドの現在の値を返します。フィールドが設定されていない場合、デフォルト値を返します。
  • `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` のコピーを返します。
  • `void set_foo(const char* value, int size)`: ヌル終端バイトを探すのではなく、明示的にサイズが指定された文字列を使用してフィールドの値を設定します。これを呼び出した後、`has_foo()` は `true` を返し、`foo()` は `value` のコピーを返します。
  • `string* mutable_foo()`: フィールドの値を格納する変更可能な `string` オブジェクトへのポインターを返します。呼び出し前にフィールドが設定されていなかった場合、返される文字列は空になります (デフォルト値ではありません)。これを呼び出した後、`has_foo()` は `true` を返し、`foo()` は指定された文字列に書き込まれた任意の値を返します。
  • `void clear_foo()`: フィールドの値をクリアします。これを呼び出した後、`has_foo()` は `false` を返し、`foo()` はデフォルト値を返します。
  • `void set_allocated_foo(string* value)`: `string` オブジェクトをフィールドに設定し、以前のフィールド値が存在する場合は解放します。`string` ポインターが `NULL` でない場合、メッセージは割り当てられた `string` オブジェクトの所有権を取得し、`has_foo()` は `true` を返します。メッセージは割り当てられた `string` オブジェクトをいつでも自由に削除できるため、オブジェクトへの参照は無効になる可能性があります。それ以外の場合、`value` が `NULL` の場合、動作は `clear_foo()` の呼び出しと同じです。
  • `string* release_foo()`: フィールドの所有権を解放し、`string` オブジェクトのポインターを返します。これを呼び出した後、呼び出し元は割り当てられた `string` オブジェクトの所有権を取得し、`has_foo()` は `false` を返し、`foo()` はデフォルト値を返します。

暗黙的な存在の文字列/バイトフィールド (proto3)

注: 2023年版以降、`features.(pb.cpp).string_type` が `VIEW` に設定されている場合、string_view API が代わりに生成されます。

これらのフィールド定義のいずれかの場合

string foo = 1;  // no field label specified, defaults to implicit presence.
bytes foo = 1;

コンパイラは次のアクセサーメソッドを生成します。

  • `const string& foo() const`: フィールドの現在の値を返します。フィールドが設定されていない場合、空の文字列/空のバイトを返します。
  • `void set_foo(::absl::string_view value)`: フィールドの値を設定します。これを呼び出した後、`foo()` は `value` のコピーを返します。
  • `void set_foo(const string& value)`: フィールドの値を設定します。これを呼び出した後、`foo()` は `value` のコピーを返します。
  • `void set_foo(string&& value)`: 渡された文字列から移動して、フィールドの値を設定します。これを呼び出した後、`foo()` は `value` のコピーを返します。
  • `void set_foo(const char* value)`: Cスタイルのヌル終端文字列を使用してフィールドの値を設定します。これを呼び出した後、`foo()` は `value` のコピーを返します。
  • `void set_foo(const char* value, int size)`: ヌル終端バイトを探すのではなく、明示的にサイズが指定された文字列を使用してフィールドの値を設定します。これを呼び出した後、`foo()` は `value` のコピーを返します。
  • `string* mutable_foo()`: フィールドの値を格納する変更可能な `string` オブジェクトへのポインターを返します。呼び出し前にフィールドが設定されていなかった場合、返される文字列は空になります。これを呼び出した後、`foo()` は指定された文字列に書き込まれた任意の値を返します。
  • `void clear_foo()`: フィールドの値をクリアします。これを呼び出した後、`foo()` は空の文字列/空のバイトを返します。
  • `void set_allocated_foo(string* value)`: `string` オブジェクトをフィールドに設定し、以前のフィールド値が存在する場合は解放します。`string` ポインターが `NULL` でない場合、メッセージは割り当てられた `string` オブジェクトの所有権を取得します。メッセージは割り当てられた `string` オブジェクトをいつでも自由に削除できるため、オブジェクトへの参照は無効になる可能性があります。それ以外の場合、`value` が `NULL` の場合、動作は `clear_foo()` の呼び出しと同じです。
  • `string* release_foo()`: フィールドの所有権を解放し、`string` オブジェクトのポインターを返します。これを呼び出した後、呼び出し元は割り当てられた `string` オブジェクトの所有権を取得し、`foo()` は空の文字列/空のバイトを返します。

Cord サポート付きの単数バイトフィールド

v23.0 で、単一の `bytes` フィールド (`oneof` フィールドを含む) に対する `absl::Cord` のサポートが追加されました。単一の `string`、繰り返し `string`、および繰り返し `bytes` フィールドは `Cord` の使用をサポートしていません。

単一の `bytes` フィールドに `absl::Cord` を使用してデータを格納するには、次の構文を使用します。

optional bytes foo = 25 [ctype=CORD];
bytes bar = 26 [ctype=CORD];

`cord` の使用は `repeated bytes` フィールドでは利用できません。Protoc はこれらのフィールドの `[ctype=CORD]` 設定を無視します。

コンパイラは次のアクセサーメソッドを生成します。

  • `const ::absl::Cord& foo() const`: フィールドの現在の値を返します。フィールドが設定されていない場合、空の `Cord` (proto3) またはデフォルト値 (proto2) を返します。
  • `void set_foo(const ::absl::Cord& value)`: フィールドの値を設定します。これを呼び出した後、`foo()` は `value` を返します。
  • `void set_foo(::absl::string_view value)`: フィールドの値を設定します。これを呼び出した後、`foo()` は `value` を `absl::Cord` として返します。
  • `void clear_foo()`: フィールドの値をクリアします。これを呼び出した後、`foo()` は空の `Cord` (proto3) またはデフォルト値 (proto2) を返します。
  • `bool has_foo()`: フィールドが設定されている場合 `true` を返します。

オプションのEnumフィールド (proto2 および proto3)

次の Enum 型の場合

enum Bar {
  BAR_UNSPECIFIED = 0;
  BAR_VALUE = 1;
  BAR_OTHER_VALUE = 2;
}

これらのフィールド定義のいずれかの場合

optional Bar bar = 1;
required Bar bar = 1;

コンパイラは次のアクセサーメソッドを生成します。

  • `bool has_bar() const`: フィールドが設定されている場合 `true` を返します。
  • `Bar bar() const`: フィールドの現在の値を返します。フィールドが設定されていない場合、デフォルト値を返します。
  • `void set_bar(Bar value)`: フィールドの値を設定します。これを呼び出した後、`has_bar()` は `true` を返し、`bar()` は `value` を返します。デバッグモード (すなわち NDEBUG が定義されていない場合) では、`value` が `Bar` で定義された値のいずれとも一致しない場合、このメソッドはプロセスを異常終了させます。
  • `void clear_bar()`: フィールドの値をクリアします。これを呼び出した後、`has_bar()` は `false` を返し、`bar()` はデフォルト値を返します。

暗黙的な存在のEnumフィールド (proto3)

次の Enum 型の場合

enum Bar {
  BAR_UNSPECIFIED = 0;
  BAR_VALUE = 1;
  BAR_OTHER_VALUE = 2;
}

このフィールド定義の場合

Bar bar = 1;  // no field label specified, defaults to implicit presence.

コンパイラは次のアクセサーメソッドを生成します。

  • `Bar bar() const`: フィールドの現在の値を返します。フィールドが設定されていない場合、デフォルト値 (0) を返します。
  • `void set_bar(Bar value)`: フィールドの値を設定します。これを呼び出した後、`bar()` は `value` を返します。
  • `void clear_bar()`: フィールドの値をクリアします。これを呼び出した後、`bar()` はデフォルト値を返します。

オプションの埋め込みメッセージフィールド (proto2 および proto3)

次のメッセージ型の場合

message Bar {}

これらのフィールド定義のいずれかの場合

//proto2
optional Bar bar = 1;
required Bar bar = 1;

//proto3
Bar bar = 1;

コンパイラは次のアクセサーメソッドを生成します。

  • `bool has_bar() const`: フィールドが設定されている場合 `true` を返します。
  • `const Bar& bar() const`: フィールドの現在の値を返します。フィールドが設定されていない場合、どのフィールドも設定されていない `Bar` (おそらく `Bar::default_instance()`) を返します。
  • `Bar* mutable_bar()`: フィールドの値を格納する変更可能な `Bar` オブジェクトへのポインターを返します。呼び出し前にフィールドが設定されていなかった場合、返される `Bar` はどのフィールドも設定されていません (すなわち、新しく割り当てられた `Bar` と同一になります)。これを呼び出した後、`has_bar()` は `true` を返し、`bar()` は同じ `Bar` のインスタンスへの参照を返します。
  • `void clear_bar()`: フィールドの値をクリアします。これを呼び出した後、`has_bar()` は `false` を返し、`bar()` はデフォルト値を返します。
  • `void set_allocated_bar(Bar* bar)`: `Bar` オブジェクトをフィールドに設定し、以前のフィールド値が存在する場合は解放します。`Bar` ポインターが `NULL` でない場合、メッセージは割り当てられた `Bar` オブジェクトの所有権を取得し、`has_bar()` は `true` を返します。それ以外の場合、`Bar` が `NULL` の場合、動作は `clear_bar()` の呼び出しと同じです。
  • `Bar* release_bar()`: フィールドの所有権を解放し、`Bar` オブジェクトのポインターを返します。これを呼び出した後、呼び出し元は割り当てられた `Bar` オブジェクトの所有権を取得し、`has_bar()` は `false` を返し、`bar()` はデフォルト値を返します。

繰り返し数値フィールド

このフィールド定義の場合

repeated int32 foo = 1;

コンパイラは次のアクセサーメソッドを生成します。

  • `int foo_size() const`: フィールドに現在存在する要素の数を返します。空のセットをチェックするには、このメソッドの代わりに基になる `RepeatedField` の `empty()` メソッドを使用することを検討してください。
  • `int32 foo(int index) const`: 指定された0ベースのインデックスにある要素を返します。このメソッドを [0, foo_size()) の範囲外のインデックスで呼び出すと、未定義の動作が発生します。
  • `void set_foo(int index, int32 value)`: 指定された0ベースのインデックスにある要素の値を設定します。
  • `void add_foo(int32 value)`: 指定された値を持つ新しい要素をフィールドの末尾に追加します。
  • `void clear_foo()`: フィールドからすべての要素を削除します。これを呼び出した後、`foo_size()` はゼロを返します。
  • `const RepeatedField<int32>& foo() const`: フィールドの要素を格納する基になる `RepeatedField` を返します。このコンテナクラスは、STLのようなイテレータやその他のメソッドを提供します。
  • `RepeatedField<int32>* mutable_foo()`: フィールドの要素を格納する基になる変更可能な `RepeatedField` へのポインターを返します。このコンテナクラスは、STLのようなイテレータやその他のメソッドを提供します。

その他の数値フィールド型 (`bool` を含む) の場合、`int32` はスカラー値型の表に従って対応する C++ 型に置き換えられます。

繰り返し文字列フィールド

注: 2023年版以降、`features.(pb.cpp).string_type` が `VIEW` に設定されている場合、string_view API が代わりに生成されます。

これらのフィールド定義のいずれかの場合

repeated string foo = 1;
repeated bytes foo = 1;

コンパイラは次のアクセサーメソッドを生成します。

  • `int foo_size() const`: フィールドに現在存在する要素の数を返します。空のセットをチェックするには、このメソッドの代わりに基になる `RepeatedField` の `empty()` メソッドを使用することを検討してください。
  • `const string& 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 set_foo(int index, const char* value, int size)`: ヌル終端バイトを探すのではなく、明示的にサイズが指定されたCスタイルの文字列を使用して、指定された0ベースのインデックスにある要素の値を設定します。
  • `string* mutable_foo(int index)`: 指定された0ベースのインデックスにある要素の値を格納する変更可能な `string` オブジェクトへのポインターを返します。このメソッドを [0, foo_size()) の範囲外のインデックスで呼び出すと、未定義の動作が発生します。
  • `void add_foo(::absl::string_view value)`: 指定された0ベースのインデックスにある要素の末尾に新しい要素を追加します。
  • `void add_foo(const string& value)`: 指定された値を持つ新しい要素をフィールドの末尾に追加します。
  • `void add_foo(string&& value)`: 渡された文字列から移動して、新しい要素をフィールドの末尾に追加します。
  • `void add_foo(const char* value)`: Cスタイルのヌル終端文字列を使用して、新しい要素をフィールドの末尾に追加します。
  • `void add_foo(const char* value, int size)`: ヌル終端バイトを探すのではなく、明示的にサイズが指定された文字列を使用して、新しい要素をフィールドの末尾に追加します。
  • `string* add_foo()`: 新しい空の文字列要素をフィールドの末尾に追加し、それへのポインターを返します。
  • `void clear_foo()`: フィールドからすべての要素を削除します。これを呼び出した後、`foo_size()` はゼロを返します。
  • `const RepeatedPtrField<string>& foo() const`: フィールドの要素を格納する基になる `RepeatedPtrField` を返します。このコンテナクラスは、STLのようなイテレータやその他のメソッドを提供します。
  • `RepeatedPtrField<string>* mutable_foo()`: フィールドの要素を格納する基になる変更可能な `RepeatedPtrField` へのポインターを返します。このコンテナクラスは、STLのようなイテレータやその他のメソッドを提供します。

繰り返しEnumフィールド

次の Enum 型の場合

enum Bar {
  BAR_UNSPECIFIED = 0;
  BAR_VALUE = 1;
  BAR_OTHER_VALUE = 2;
}

このフィールド定義の場合

repeated Bar bar = 1;

コンパイラは次のアクセサーメソッドを生成します。

  • `int bar_size() const`: フィールドに現在存在する要素の数を返します。空のセットをチェックするには、このメソッドの代わりに基になる `RepeatedField` の `empty()` メソッドを使用することを検討してください。
  • `Bar bar(int index) const`: 指定された0ベースのインデックスにある要素を返します。このメソッドを [0, bar_size()) の範囲外のインデックスで呼び出すと、未定義の動作が発生します。
  • `void set_bar(int index, Bar value)`: 指定された0ベースのインデックスにある要素の値を設定します。デバッグモード (すなわち NDEBUG が定義されていない場合) では、`value` が `Bar` で定義された値のいずれとも一致しない場合、このメソッドはプロセスを異常終了させます。
  • `void add_bar(Bar value)`: 指定された値を持つ新しい要素をフィールドの末尾に追加します。デバッグモード (すなわち NDEBUG が定義されていない場合) では、`value` が `Bar` で定義された値のいずれとも一致しない場合、このメソッドはプロセスを異常終了させます。
  • `void clear_bar()`: フィールドからすべての要素を削除します。これを呼び出した後、`bar_size()` はゼロを返します。
  • `const RepeatedField<int>& bar() const`: フィールドの要素を格納する基になる `RepeatedField` を返します。このコンテナクラスは、STLのようなイテレータやその他のメソッドを提供します。
  • `RepeatedField<int>* mutable_bar()`: フィールドの要素を格納する基になる変更可能な `RepeatedField` へのポインターを返します。このコンテナクラスは、STLのようなイテレータやその他のメソッドを提供します。

繰り返し埋め込みメッセージフィールド

次のメッセージ型の場合

message Bar {}

このフィールド定義の場合

repeated Bar bar = 1;

コンパイラは次のアクセサーメソッドを生成します。

  • `int bar_size() const`: フィールドに現在存在する要素の数を返します。空のセットをチェックするには、このメソッドの代わりに基になる `RepeatedField` の `empty()` メソッドを使用することを検討してください。
  • `const Bar& bar(int index) const`: 指定された0ベースのインデックスにある要素を返します。このメソッドを [0, bar_size()) の範囲外のインデックスで呼び出すと、未定義の動作が発生します。
  • `Bar* mutable_bar(int index)`: 指定された0ベースのインデックスにある要素の値を格納する変更可能な `Bar` オブジェクトへのポインターを返します。このメソッドを [0, bar_size()) の範囲外のインデックスで呼び出すと、未定義の動作が発生します。
  • `Bar* add_bar()`: 新しい要素をフィールドの末尾に追加し、それへのポインターを返します。返される `Bar` は変更可能であり、どのフィールドも設定されていません (すなわち、新しく割り当てられた `Bar` と同一になります)。
  • `void clear_bar()`: フィールドからすべての要素を削除します。これを呼び出した後、`bar_size()` はゼロを返します。
  • `const RepeatedPtrField<Bar>& bar() const`: フィールドの要素を格納する基になる `RepeatedPtrField` を返します。このコンテナクラスは、STLのようなイテレータやその他のメソッドを提供します。
  • `RepeatedPtrField<Bar>* mutable_bar()`: フィールドの要素を格納する基になる変更可能な `RepeatedPtrField` へのポインターを返します。このコンテナクラスは、STLのようなイテレータやその他のメソッドを提供します。

Oneof 数値フィールド

このoneofフィールド定義の場合

oneof example_name {
    int32 foo = 1;
    ...
}

コンパイラは次のアクセサーメソッドを生成します。

  • `bool has_foo() const`: oneof ケースが `kFoo` の場合 `true` を返します。
  • `int32 foo() const`: oneof ケースが `kFoo` の場合、フィールドの現在の値を返します。それ以外の場合、デフォルト値を返します。
  • void set_foo(int32 value):
    • 同じ oneof 内の他の oneof フィールドが設定されている場合、`clear_example_name()` を呼び出します。
    • このフィールドの値を設定し、oneof ケースを `kFoo` に設定します。
    • `has_foo()` は true を返し、`foo()` は `value` を返し、`example_name_case()` は `kFoo` を返します。
  • void clear_foo():
    • oneof ケースが `kFoo` でない場合、何も変更されません。
    • oneof ケースが `kFoo` の場合、フィールドの値と oneof ケースをクリアします。`has_foo()` は `false` を返し、`foo()` はデフォルト値を返し、`example_name_case()` は `EXAMPLE_NAME_NOT_SET` を返します。

その他の数値フィールド型 (`bool` を含む) の場合、`int32` はスカラー値型の表に従って対応する C++ 型に置き換えられます。

Oneof 文字列フィールド

注: 2023年版以降、代わりにstring_view API が生成される場合があります

これらのoneofフィールド定義のいずれかの場合

oneof example_name {
    string foo = 1;
    ...
}
oneof example_name {
    bytes foo = 1;
    ...
}

コンパイラは次のアクセサーメソッドを生成します。

  • `bool has_foo() const`: oneof ケースが `kFoo` の場合 `true` を返します。
  • `const string& 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 set_foo(const char* value, int size)`: 最初の `set_foo()` と同様ですが、ヌル終端バイトを探すのではなく、明示的にサイズが指定された文字列からコピーします。
  • string* mutable_foo():
    • 同じ oneof 内の他の oneof フィールドが設定されている場合、`clear_example_name()` を呼び出します。
    • oneof ケースを `kFoo` に設定し、フィールドの値を格納する変更可能な文字列オブジェクトへのポインターを返します。呼び出し前に oneof ケースが `kFoo` でなかった場合、返される文字列は空になります (デフォルト値ではありません)。
    • `has_foo()` は `true` を返し、`foo()` は指定された文字列に書き込まれた任意の値を返し、`example_name_case()` は `kFoo` を返します。
  • void clear_foo():
    • oneof ケースが `kFoo` でない場合、何も変更されません。
    • oneof ケースが `kFoo` の場合、フィールドを解放し、oneof ケースをクリアします。`has_foo()` は `false` を返し、`foo()` はデフォルト値を返し、`example_name_case()` は `EXAMPLE_NAME_NOT_SET` を返します。
  • void set_allocated_foo(string* value):
    • `clear_example_name()` を呼び出します。
    • 文字列ポインターが `NULL` でない場合: 文字列オブジェクトをフィールドに設定し、oneof ケースを `kFoo` に設定します。メッセージは割り当てられた文字列オブジェクトの所有権を取得し、`has_foo()` は `true` を返し、`example_name_case()` は `kFoo` を返します。
    • 文字列ポインターが `NULL` の場合、`has_foo()` は `false` を返し、`example_name_case()` は `EXAMPLE_NAME_NOT_SET` を返します。
  • string* release_foo():
    • oneof ケースが `kFoo` でない場合 `NULL` を返します。
    • oneof ケースをクリアし、フィールドの所有権を解放し、文字列オブジェクトのポインターを返します。これを呼び出した後、呼び出し元は割り当てられた文字列オブジェクトの所有権を取得し、`has_foo()` は false を返し、`foo()` はデフォルト値を返し、`example_name_case()` は `EXAMPLE_NAME_NOT_SET` を返します。

Oneof Enum フィールド

次の Enum 型の場合

enum Bar {
  BAR_UNSPECIFIED = 0;
  BAR_VALUE = 1;
  BAR_OTHER_VALUE = 2;
}

このoneofフィールド定義の場合

oneof example_name {
    Bar bar = 1;
    ...
}

コンパイラは次のアクセサーメソッドを生成します。

  • `bool has_bar() const`: oneof ケースが `kBar` の場合 `true` を返します。
  • `Bar bar() const`: oneof ケースが `kBar` の場合、フィールドの現在の値を返します。それ以外の場合、デフォルト値を返します。
  • void set_bar(Bar value):
    • 同じ oneof 内の他の oneof フィールドが設定されている場合、`clear_example_name()` を呼び出します。
    • このフィールドの値を設定し、oneof ケースを `kBar` に設定します。
    • `has_bar()` は `true` を返し、`bar()` は `value` を返し、`example_name_case()` は `kBar` を返します。
    • デバッグモード (すなわち NDEBUG が定義されていない場合) では、`value` が `Bar` で定義された値のいずれとも一致しない場合、このメソッドはプロセスを異常終了させます。
  • void clear_bar():
    • oneof ケースが `kBar` でない場合、何も変更されません。
    • oneof ケースが `kBar` の場合、フィールドの値と oneof ケースをクリアします。`has_bar()` は `false` を返し、`bar()` はデフォルト値を返し、`example_name_case()` は `EXAMPLE_NAME_NOT_SET` を返します。

Oneof 埋め込みメッセージフィールド

次のメッセージ型の場合

message Bar {}

このoneofフィールド定義の場合

oneof example_name {
    Bar bar = 1;
    ...
}

コンパイラは次のアクセサーメソッドを生成します。

  • `bool has_bar() const`: oneof ケースが `kBar` の場合 true を返します。
  • `const Bar& bar() const`: oneof ケースが `kBar` の場合、フィールドの現在の値を返します。それ以外の場合、どのフィールドも設定されていない Bar (おそらく `Bar::default_instance()`) を返します。
  • Bar* mutable_bar():
    • 同じ oneof 内の他の oneof フィールドが設定されている場合、`clear_example_name()` を呼び出します。
    • oneof ケースを `kBar` に設定し、フィールドの値を格納する変更可能な Bar オブジェクトへのポインターを返します。呼び出し前に oneof ケースが `kBar` でなかった場合、返される Bar はどのフィールドも設定されていません (すなわち、新しく割り当てられた Bar と同一になります)。
    • これを呼び出した後、`has_bar()` は `true` を返し、`bar()` は同じ `Bar` のインスタンスへの参照を返し、`example_name_case()` は `kBar` を返します。
  • void clear_bar():
    • oneof ケースが `kBar` でない場合、何も変更されません。
    • oneof ケースが `kBar` の場合、フィールドを解放し、oneof ケースをクリアします。`has_bar()` は `false` を返し、`bar()` はデフォルト値を返し、`example_name_case()` は `EXAMPLE_NAME_NOT_SET` を返します。
  • void set_allocated_bar(Bar* bar):
    • `clear_example_name()` を呼び出します。
    • `Bar` ポインターが `NULL` でない場合: `Bar` オブジェクトをフィールドに設定し、oneof ケースを `kBar` に設定します。メッセージは割り当てられた `Bar` オブジェクトの所有権を取得し、has_bar() は true を返し、`example_name_case()` は `kBar` を返します。
    • ポインターが `NULL` の場合、`has_bar()` は `false` を返し、`example_name_case()` は `EXAMPLE_NAME_NOT_SET` を返します。(この動作は `clear_example_name()` の呼び出しと同じです)
  • Bar* release_bar():
    • oneof ケースが `kBar` でない場合 `NULL` を返します。
    • oneof ケースが `kBar` の場合、oneof ケースをクリアし、フィールドの所有権を解放し、`Bar` オブジェクトのポインターを返します。これを呼び出した後、呼び出し元は割り当てられた `Bar` オブジェクトの所有権を取得し、`has_bar()` は `false` を返し、`bar()` はデフォルト値を返し、`example_name_case()` は `EXAMPLE_NAME_NOT_SET` を返します。

マップフィールド

このマップフィールド定義の場合

map<int32, int32> weight = 1;

コンパイラは次のアクセサーメソッドを生成します。

  • `const google::protobuf::Map<int32, int32>& weight();`: 不変の `Map` を返します。
  • `google::protobuf::Map<int32, int32>* mutable_weight();`: 変更可能な `Map` を返します。

`google::protobuf::Map` は、Protocol Buffers でマップフィールドを格納するために使用される特殊なコンテナ型です。以下のインターフェースからわかるように、`std::map` および `std::unordered_map` メソッドの一般的に使用されるサブセットを使用します。

template<typename Key, typename T> {
class Map {
  // Member types
  typedef Key key_type;
  typedef T mapped_type;
  typedef MapPair< Key, T > value_type;

  // Iterators
  iterator begin();
  const_iterator begin() const;
  const_iterator cbegin() const;
  iterator end();
  const_iterator end() const;
  const_iterator cend() const;
  // Capacity
  int size() const;
  bool empty() const;

  // Element access
  T& operator[](const Key& key);
  const T& at(const Key& key) const;
  T& at(const Key& key);

  // Lookup
  bool contains(const Key& key) const;
  int count(const Key& key) const;
  const_iterator find(const Key& key) const;
  iterator find(const Key& key);

  // Modifiers
  pair<iterator, bool> insert(const value_type& value);
  template<class InputIt>
  void insert(InputIt first, InputIt last);
  size_type erase(const Key& Key);
  iterator erase(const_iterator pos);
  iterator erase(const_iterator first, const_iterator last);
  void clear();

  // Copy
  Map(const Map& other);
  Map& operator=(const Map& other);
}

データを追加する最も簡単な方法は、通常のマップ構文を使用することです。例えば

std::unique_ptr<ProtoName> my_enclosing_proto(new ProtoName);
(*my_enclosing_proto->mutable_weight())[my_key] = my_value;

`pair<iterator, bool> insert(const value_type& value)` は、`value_type` インスタンスのディープコピーを暗黙的に引き起こします。`google::protobuf::Map` に新しい値を挿入する最も効率的な方法は次のとおりです。

T& operator[](const Key& key): map[new_key] = new_mapped;

標準マップでの `google::protobuf::Map` の使用

`google::protobuf::Map` は、`std::map` および `std::unordered_map` と同じイテレータ API をサポートしています。`google::protobuf::Map` を直接使用したくない場合は、次の方法で `google::protobuf::Map` を標準マップに変換できます。

std::map<int32, int32> standard_map(message.weight().begin(),
                                    message.weight().end());

これにより、マップ全体のディープコピーが作成されることに注意してください。

標準マップから `google::protobuf::Map` を構築することもできます。

google::protobuf::Map<int32, int32> weight(standard_map.begin(), standard_map.end());

不明な値の解析

ワイヤー形式では、.proto マップは各キー/値ペアに対するマップエントリメッセージと等価であり、マップ自体はマップエントリの繰り返しフィールドです。通常のメッセージ型と同様に、解析されたマップエントリメッセージに不明なフィールドが含まれる可能性があります。例えば、`map<int32, string>` として定義されたマップに `int64` 型のフィールドが含まれる場合などです。

マップエントリメッセージのワイヤー形式に不明なフィールドがある場合、それらは破棄されます。

マップエントリメッセージのワイヤー形式に不明な enum 値がある場合、proto2 と proto3 では異なる方法で処理されます。proto2 では、マップエントリメッセージ全体が、それを囲むメッセージの不明なフィールドセットに配置されます。proto3 では、既知の enum 値であるかのようにマップフィールドに配置されます。

Any

次のような`Any` フィールドがある場合

import "google/protobuf/any.proto";

message ErrorStatus {
  string message = 1;
  google.protobuf.Any details = 2;
}

生成されたコードでは、`details` フィールドのゲッターは `google::protobuf::Any` のインスタンスを返します。これにより、`Any` の値をパックおよびアンパックするための以下の特別なメソッドが提供されます。

class Any {
 public:
  // Packs the given message into this Any using the default type URL
  // prefix “type.googleapis.com”. Returns false if serializing the message failed.
  bool PackFrom(const google::protobuf::Message& message);

  // Packs the given message into this Any using the given type URL
  // prefix. Returns false if serializing the message failed.
  bool PackFrom(const google::protobuf::Message& message,
                ::absl::string_view type_url_prefix);

  // Unpacks this Any to a Message. Returns false if this Any
  // represents a different protobuf type or parsing fails.
  bool UnpackTo(google::protobuf::Message* message) const;

  // Returns true if this Any represents the given protobuf type.
  template<typename T> bool Is() const;
}

Oneof

次のような oneof 定義がある場合

oneof example_name {
    int32 foo_int = 4;
    string foo_string = 9;
    ...
}

コンパイラは以下の C++ enum 型を生成します。

enum ExampleNameCase {
  kFooInt = 4,
  kFooString = 9,
  EXAMPLE_NAME_NOT_SET = 0
}

さらに、以下のメソッドを生成します。

  • `ExampleNameCase example_name_case() const`: どのフィールドが設定されているかを示す enum を返します。いずれも設定されていない場合 `EXAMPLE_NAME_NOT_SET` を返します。
  • `void clear_example_name()`: oneof フィールドセットがポインター (Message または String) を使用している場合、オブジェクトを解放し、oneof ケースを `EXAMPLE_NAME_NOT_SET` に設定します。

列挙型

注: 2024年版以降、特定の機能設定で `string_view` API が生成される場合があります。このトピックの詳細については、Enumeration Name Helper を参照してください。

次のような enum 定義がある場合

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

Protocol Buffer コンパイラは、同じ値のセットを持つ `Foo` という名前の C++ enum 型を生成します。さらに、コンパイラは以下の関数を生成します。

  • `const EnumDescriptor* Foo_descriptor()`: 型のディスクリプターを返します。これには、この enum 型が定義する値に関する情報が含まれます。
  • `bool Foo_IsValid(int value)`: 指定された数値が `Foo` で定義された値のいずれかと一致する場合 `true` を返します。上記の例では、入力が 0、5、または 1234 であった場合 `true` を返します。
  • `const string& Foo_Name(int value)`: 指定された数値の名前を返します。そのような値が存在しない場合、空の文字列を返します。複数の値がこの数値を持つ場合、最初に定義されたものが返されます。上記の例では、`Foo_Name(5)` は `"VALUE_B"` を返します。
  • `bool Foo_Parse(::absl::string_view name, Foo* value)`: `name` がこの enum の有効な値名である場合、その値を `value` に割り当て、true を返します。それ以外の場合、false を返します。上記の例では、`Foo_Parse("VALUE_C", &some_foo)` は true を返し、`some_foo` を 1234 に設定します。
  • `const Foo Foo_MIN`: enum の最小の有効な値 (例では VALUE_A)。
  • `const Foo Foo_MAX`: enum の最大の有効な値 (例では VALUE_C)。
  • `const int Foo_ARRAYSIZE`: 常に `Foo_MAX + 1` として定義されます。

整数を proto2 enum にキャストする際は注意してください。 整数を proto2 enum 値にキャストする場合、その整数は当該 enum の有効な値のいずれかである*必要*があり、そうでない場合、結果は未定義となる可能性があります。疑わしい場合は、生成された `Foo_IsValid()` 関数を使用してキャストが有効かどうかをテストしてください。proto2 メッセージの enum 型フィールドに無効な値を設定すると、アサーションの失敗を引き起こす可能性があります。proto2 メッセージを解析する際に無効な enum 値が読み込まれた場合、それは不明なフィールドとして扱われます。これらのセマンティクスは proto3 で変更されています。int32 に収まる限り、任意の整数を proto3 enum 値にキャストするのは安全です。無効な enum 値も proto3 メッセージの解析時に保持され、enum フィールドアクセサーによって返されます。

switch ステートメントで proto3 enum を使用する際は注意してください。 proto3 enum は、指定されたシンボルの範囲外の値を持つオープン enum 型です。認識されない enum 値は、proto3 メッセージの解析時に保持され、enum フィールドアクセサーによって返されます。デフォルトケースのない proto3 enum の switch ステートメントは、すべての既知のフィールドがリストされていても、すべてのケースを捕捉することはできません。これにより、データ破損やランタイムクラッシュを含む予期しない動作につながる可能性があります。不明な enum 値を処理するために、常にデフォルトケースを追加するか、switch の外で明示的に `Foo_IsValid(int)` を呼び出してください。

enum はメッセージ型の中に定義できます。この場合、Protocol Buffer コンパイラは、enum 型自体がメッセージのクラスの中にネストして宣言されたかのように見えるコードを生成します。`Foo_descriptor()` および `Foo_IsValid()` 関数は静的メソッドとして宣言されます。実際には、enum 型自体とその値はグローバルスコープでマングルされた名前で宣言され、typedef と一連の定数定義によってクラスのスコープにインポートされます。これは宣言の順序に関する問題を回避するためだけに実行されます。マングルされたトップレベルの名前に依存しないでください。enum が実際にメッセージクラスにネストされていると見なしてください。

拡張機能 (proto2 のみ)

拡張範囲を持つメッセージの場合

message Foo {
  extensions 100 to 199;
}

Protocol Buffer コンパイラは、`Foo` 用にいくつかの追加メソッド (`HasExtension()`、`ExtensionSize()`、`ClearExtension()`、`GetExtension()`、`SetExtension()`、`MutableExtension()`、`AddExtension()`、`SetAllocatedExtension()`、`ReleaseExtension()`) を生成します。これらの各メソッドは、最初のパラメータとして拡張識別子 (以下で説明) を受け取り、それが拡張フィールドを識別します。残りのパラメータと戻り値は、拡張識別子と同じ型の通常の (拡張機能ではない) フィールドに対して生成される対応するアクセサーメソッドとまったく同じです。( `GetExtension()` は、特別なプレフィックスのないアクセサーに対応します。)

拡張定義の場合

extend Foo {
  optional int32 bar = 123;
  repeated int32 repeated_bar = 124;
  optional Bar message_bar = 125;
}

単一の拡張フィールド `bar` の場合、Protocol Buffer コンパイラは `bar` と呼ばれる「拡張識別子」を生成します。これは、`Foo` の拡張アクセサーと共に使用して、次のようにこの拡張にアクセスできます。

Foo foo;
assert(!foo.HasExtension(bar));
foo.SetExtension(bar, 1);
assert(foo.HasExtension(bar));
assert(foo.GetExtension(bar) == 1);
foo.ClearExtension(bar);
assert(!foo.HasExtension(bar));

メッセージ拡張フィールド `message_bar` の場合、フィールドが設定されていないと、`foo.GetExtension(message_bar)` は、どのフィールドも設定されていない `Bar` (おそらく `Bar::default_instance()`) を返します。

同様に、繰り返し拡張フィールド `repeated_bar` の場合、コンパイラは `repeated_bar` と呼ばれる拡張識別子を生成します。これも `Foo` の拡張アクセサーと共に使用できます。

Foo foo;
for (int i = 0; i < kSize; ++i) {
  foo.AddExtension(repeated_bar, i)
}
assert(foo.ExtensionSize(repeated_bar) == kSize)
for (int i = 0; i < kSize; ++i) {
  assert(foo.GetExtension(repeated_bar, i) == i)
}

(拡張識別子の正確な実装は複雑であり、テンプレートの魔法のような使用を含みますが、使用するために拡張識別子がどのように機能するかを心配する必要はありません。)

拡張は別の型の内部にネストして宣言できます。例えば、一般的なパターンは次のようになります。

message Baz {
  extend Foo {
    optional Baz foo_ext = 124;
  }
}

この場合、拡張識別子 `foo_ext` は `Baz` の内部にネストして宣言されます。これは次のように使用できます。

Foo foo;
Baz* baz = foo.MutableExtension(Baz::foo_ext);
FillInMyBaz(baz);

アリーナアロケーション

アリーナアロケーションは C++ 専用の機能であり、Protocol Buffers を使用する際のメモリ使用量を最適化し、パフォーマンスを向上させるのに役立ちます。`.proto` でアリーナアロケーションを有効にすると、C++ 生成コードにアリーナを扱うための追加コードが追加されます。アリーナアロケーション API の詳細については、アリーナアロケーションガイド を参照してください。

サービス

`.proto` ファイルに次の行が含まれている場合

option cc_generic_services = true;

その場合、Protocol Buffer コンパイラは、このセクションで説明されているように、ファイル内で見つかったサービス定義に基づいてコードを生成します。ただし、生成されたコードは特定の RPC システムに結び付けられていないため、1つのシステムに特化したコードよりも多くの間接参照レベルを必要とするため、望ましくない場合があります。このコードを生成したくない場合は、次の行をファイルに追加します。

option cc_generic_services = false;

上記のいずれの行も指定されていない場合、汎用サービスは非推奨であるため、オプションはデフォルトで `false` になります。(注: 2.4.0 以前は、オプションはデフォルトで `true` でした)

`.proto` 言語サービス定義に基づく RPC システムは、システムに適したコードを生成するためのプラグインを提供する必要があります。これらのプラグインは、抽象サービスが無効になっていることを要求する可能性が高く、それによって同じ名前の独自のクラスを生成できます。

このセクションの残りの部分では、抽象サービスが有効になっている場合に Protocol Buffer コンパイラが何を生成するかを説明します。

インターフェース

サービス定義の場合

service Foo {
  rpc Bar(FooRequest) returns(FooResponse);
}

Protocol Buffer コンパイラは、このサービスを表すクラス `Foo` を生成します。`Foo` は、サービス定義で定義された各メソッドに対して仮想メソッドを持ちます。この場合、メソッド `Bar` は次のように定義されます。

virtual void Bar(RpcController* controller, const FooRequest* request,
                 FooResponse* response, Closure* done);

パラメータは `Service::CallMethod()` のパラメータと等価ですが、`method` 引数は暗黙的であり、`request` と `response` はそれらの正確な型を指定します。

これらの生成されたメソッドは仮想ですが、純粋仮想ではありません。デフォルトの実装は、メソッドが未実装であることを示すエラーメッセージと共に単に `controller->SetFailed()` を呼び出し、その後 `done` コールバックを呼び出します。独自のサービスを実装する場合、この生成されたサービスをサブクラス化し、そのメソッドを適切に実装する必要があります。

`Foo` は `Service` インターフェースをサブクラス化します。Protocol Buffer コンパイラは、`Service` のメソッドの実装を次のように自動的に生成します。

  • `GetDescriptor`: サービスの`ServiceDescriptor` を返します。
  • `CallMethod`: 提供されたメソッドディスクリプターに基づいてどのメソッドが呼び出されているかを判断し、それを直接呼び出し、リクエストおよびレスポンスメッセージオブジェクトを正しい型にダウンキャストします。
  • `GetRequestPrototype` および `GetResponsePrototype`: 指定されたメソッドの正しい型のリクエストまたはレスポンスのデフォルトインスタンスを返します。

以下の静的メソッドも生成されます。

  • `static ServiceDescriptor descriptor()`: 型のディスクリプターを返します。これには、このサービスが持つメソッドとその入力型および出力型に関する情報が含まれます。

スタブ

Protocol Buffer コンパイラは、すべてのサービスインターフェースの「スタブ」実装も生成します。これは、サービスを実装するサーバーにリクエストを送信したいクライアントによって使用されます。上記の `Foo` サービスの場合、スタブ実装 `Foo_Stub` が定義されます。ネストされたメッセージ型と同様に、typedef が使用されるため、`Foo_Stub` は `Foo::Stub` としても参照できます。

`Foo_Stub` は `Foo` のサブクラスであり、以下のメソッドも実装します。

  • `Foo_Stub(RpcChannel* channel)`: 指定されたチャネルでリクエストを送信する新しいスタブを構築します。
  • `Foo_Stub(RpcChannel* channel, ChannelOwnership ownership)`: 指定されたチャネルでリクエストを送信し、そのチャネルを所有する可能性のある新しいスタブを構築します。`ownership` が `Service::STUB_OWNS_CHANNEL` である場合、スタブオブジェクトが削除されるときにチャネルも削除されます。
  • `RpcChannel* channel()`: このスタブのチャネルを、コンストラクタに渡されたとおりに返します。

スタブは、さらにサービスの各メソッドをチャネルのラッパーとして実装します。いずれかのメソッドを呼び出すと、単に `channel->CallMethod()` が呼び出されます。

Protocol Buffer ライブラリには RPC 実装は含まれていません。しかし、生成されたサービスグラスを任意の RPC 実装に接続するために必要なすべてのツールが含まれています。必要なのは`RpcChannel` および`RpcController` の実装を提供するだけです。`service.h` のドキュメントで詳細を確認してください。

プラグイン挿入ポイント

コードジェネレータープラグインで C++ コードジェネレーターの出力を拡張したい場合、指定された挿入ポイント名を使用して以下の型のコードを挿入できます。各挿入ポイントは、特に断りのない限り、`.pb.cc` ファイルと `.pb.h` ファイルの両方に現れます。

  • `includes`: インクルードディレクティブ。
  • `namespace_scope`: ファイルのパッケージ/名前空間に属するが、特定のクラス内ではない宣言。他のすべての名前空間スコープコードの後に現れます。
  • `global_scope`: ファイルの名前空間の外のトップレベルに属する宣言。ファイルの最後に現れます。
  • `class_scope:TYPENAME`: メッセージクラスに属するメンバー宣言。`TYPENAME` は完全な proto 名です (例: `package.MessageType`)。クラス内の他のすべての公開宣言の後に現れます。この挿入ポイントは `.pb.h` ファイルにのみ現れます。

標準コードジェネレーターによって宣言されたプライベートクラスメンバーに依存するコードは生成しないでください。これらの実装詳細は、将来の Protocol Buffers のバージョンで変更される可能性があります。