C++ 生成コードガイド
proto2、proto3、およびエディションによって生成されるコードの違いを強調しています。これらの違いは、このドキュメントで説明されている生成コードにあり、すべてのバージョンで同じである基本メッセージクラス/インターフェースにはありません。このドキュメントを読む前に、proto2 言語ガイド、proto3 言語ガイド、またはエディション 2023 言語ガイドを読む必要があります。
コンパイラの呼び出し
Protocol Buffer コンパイラは、--cpp_out=
コマンドラインフラグを指定して起動すると、C++ 出力を生成します。--cpp_out=
オプションのパラメータは、コンパイラが C++ 出力を書き込むディレクトリです。コンパイラは、各 .proto
ファイル入力に対して、ヘッダーファイルと実装ファイルを生成します。出力ファイルの名前は、.proto
ファイルの名前を取り、2つの変更を加えることによって計算されます
- 拡張子 (
.proto
) は、それぞれヘッダーファイルの場合は.pb.h
に、実装ファイルの場合は.pb.cc
に置き換えられます。 - プロトパス (
--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
のファイルを読み込み、build/gen/foo.pb.h
、build/gen/foo.pb.cc
、build/gen/bar/baz.pb.h
、build/gen/bar/baz.pb.cc
の4つの出力ファイルを生成します。コンパイラは必要に応じてディレクトリ 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
(Windows では libprotobuf.lib
) の代わりに libprotobuf-lite.so
(Windows では libprotobuf-lite.lib
) にリンクするだけで済みます。「lite」ライブラリは完全なライブラリよりもはるかに小さく、携帯電話などのリソース制約のあるシステムに適しています。
独自の Foo
サブクラスを作成してはなりません。このクラスをサブクラス化して仮想メソッドをオーバーライドすると、パフォーマンスを向上させるために多くの生成されたメソッド呼び出しが非仮想化されるため、オーバーライドが無視される可能性があります。
Message
インターフェースは、バイナリ文字列からの解析やバイナリ文字列へのシリアル化を含む、メッセージ全体をチェック、操作、読み書きできるメソッドを定義します。
bool ParseFromString(::absl::string_view data)
: 指定されたシリアル化されたバイナリ文字列 (ワイヤフォーマットとも呼ばれます) からメッセージを解析します。bool SerializeToString(string* output) const
: 指定されたメッセージをバイナリ文字列にシリアル化します。string DebugString()
: プロトの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
の定数シングルトンインスタンスを返します (したがって、すべての単一フィールドは設定されておらず、すべての繰り返しフィールドは空です)。メッセージのデフォルトインスタンスは、その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 {}
}
この場合、コンパイラは2つのクラス、Foo
と Foo_Bar
を生成します。さらに、コンパイラは Foo
の内部に次のような typedef を生成します
typedef Foo_Bar Bar;
これは、ネストされた型のクラスを、ネストされたクラス Foo::Bar
のように使用できることを意味します。ただし、C++ ではネストされた型を前方宣言できないことに注意してください。別のファイルで Bar
を前方宣言してその宣言を使用したい場合は、それを Foo_Bar
として識別する必要があります。
フィールド
前のセクションで説明したメソッドに加えて、プロトコルバッファコンパイラは、.proto
ファイルのメッセージ内に定義された各フィールドに対して、一連のアクセサメソッドを生成します。これらのメソッドは、has_foo()
や clear_foo()
のように、小文字/スネークケースです。
アクセサメソッドと同様に、コンパイラは各フィールドに対してそのフィールド番号を含む整数定数を生成します。定数名は文字 k
、フィールド名をキャメルケースに変換したもの、および FieldNumber
です。たとえば、フィールド optional int32 foo_bar = 5;
が与えられた場合、コンパイラは定数 static const int kFooBarFieldNumber = 5;
を生成します。
const
参照を返すフィールドアクセサの場合、その参照は、メッセージに対して次に変更アクセスが行われたときに無効になる可能性があります。これには、任意のフィールドの非 const
アクセサの呼び出し、Message
から継承された任意の非 const
メソッドの呼び出し、またはその他の方法(たとえば、メッセージを Swap()
の引数として使用するなど)によるメッセージの変更が含まれます。同様に、返される参照のアドレスは、その間にメッセージに対して変更アクセスが行われなかった場合にのみ、アクセサの異なる呼び出し間で同じであることが保証されます。
ポインタを返すフィールドアクセサの場合、そのポインタは、メッセージに対して次に変更または非変更アクセスが行われたときに無効になる可能性があります。これには、const かどうかにかかわらず、任意のフィールドのアクセサの呼び出し、Message
から継承された任意のメソッドの呼び出し、またはその他の方法(たとえば、コピーコンストラクタを使用してメッセージをコピーするなど)によるメッセージへのアクセスが含まれます。同様に、返されるポインタの値は、アクセサの2つの異なる呼び出し間で同じであることが決して保証されません。
明示的な存在を持つ数値フィールド
明示的な存在を持つ数値フィールドのフィールド定義の場合
int32 foo = 1;
コンパイラは以下のアクセサメソッドを生成します
bool has_foo() const
: フィールドが設定されている場合にtrue
を返します。int32_t foo() const
: フィールドの現在の値を返します。フィールドが設定されていない場合、デフォルト値を返します。void set_foo(::int32_t value)
: フィールドの値を設定します。これを呼び出すと、has_foo()
はtrue
を返し、foo()
はvalue
を返します。void clear_foo()
: フィールドの値をクリアします。これを呼び出すと、has_foo()
はfalse
を返し、foo()
はデフォルト値を返します。
その他の数値フィールド型 (bool
を含む) の場合、int32_t
は、スカラ値型のテーブルに従って対応する C++ 型に置き換えられます。
暗黙的な存在を持つ数値フィールド
暗黙的な存在を持つ数値フィールドのフィールド定義の場合
int32 foo = 1;
コンパイラは以下のアクセサメソッドを生成します
::int32_t foo() const
: フィールドの現在の値を返します。フィールドが設定されていない場合、0 を返します。void set_foo(::int32_t value)
: フィールドの値を設定します。これを呼び出すと、foo()
はvalue
を返します。void clear_foo()
: フィールドの値をクリアします。これを呼び出すと、foo()
は 0 を返します。
その他の数値フィールド型 (bool
を含む) の場合、int32_t
は、スカラ値型のテーブルに従って対応する C++ 型に置き換えられます。
明示的な存在を持つ文字列/バイトフィールド
注: Edition 2023 以降、features.(pb.cpp).string_type
が VIEW
に設定されている場合、string_view API が代わりに生成されます。
明示的な存在を持つこれらのフィールド定義の場合
string foo = 1;
bytes foo = 2;
コンパイラは以下のアクセサメソッドを生成します
bool has_foo() const
: フィールドが設定されている場合にtrue
を返します。const string& foo() const
: フィールドの現在の値を返します。フィールドが設定されていない場合、デフォルト値を返します。void set_foo(...)
: フィールドの値を設定します。これを呼び出すと、has_foo()
はtrue
を返し、foo()
はvalue
のコピーを返します。string* mutable_foo()
: フィールドの値を格納する変更可能なstring
オブジェクトへのポインタを返します。呼び出し前にフィールドが設定されていなかった場合、返される文字列は空になります (デフォルト値ではありません)。これを呼び出した後、has_foo()
はtrue
を返し、foo()
は指定された文字列に書き込まれた任意の値を返します。注: このメソッドは新しい
string_view
API では削除されます。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()
はデフォルト値を返します。
暗黙的な存在を持つ文字列/バイトフィールド
注: Edition 2023 以降、features.(pb.cpp).string_type
が VIEW
に設定されている場合、string_view API が代わりに生成されます。
暗黙的な存在を持つこれらのフィールド定義の場合
string foo = 1 [features.field_presence = IMPLICIT];
bytes foo = 1 [features.field_presence = IMPLICIT];
コンパイラは以下のアクセサメソッドを生成します
const string& foo() const
: フィールドの現在の値を返します。フィールドが設定されていない場合、空の文字列/空のバイトを返します。void set_foo(Arg_&& arg, Args_... args)
: フィールドの値を設定します。これを呼び出すと、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
、repeated string
、および repeated bytes
フィールドは Cord
の使用をサポートしていません。
単一の bytes
フィールドを absl::Cord
を使用してデータを格納するように設定するには、次の構文を使用します
// edition (default settings)
bytes foo = 25 [ctype=CORD];
bytes foo = 26 [ctype=CORD, features.field_presence = IMPLICIT];
cord
の使用は、repeated bytes
フィールドでは利用できません。Protoc は、これらのフィールドの [ctype=CORD]
設定を無視します。
コンパイラは以下のアクセサメソッドを生成します
const ::absl::Cord& foo() const
: フィールドの現在の値を返します。フィールドが設定されていない場合、空のCord
(proto3) またはデフォルト値 (proto2 および editions) を返します。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 および editions) を返します。bool has_foo()
: フィールドが設定されている場合にtrue
を返します。proto3 のoptional
フィールドおよび editions の明示的な存在フィールドにのみ適用されます。
明示的な存在を持つEnumフィールド
enum型が与えられた場合
enum Bar {
BAR_UNSPECIFIED = 0;
BAR_VALUE = 1;
BAR_OTHER_VALUE = 2;
}
明示的な存在を持つこのフィールド定義の場合
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フィールド
enum型が与えられた場合
enum Bar {
BAR_UNSPECIFIED = 0;
BAR_VALUE = 1;
BAR_OTHER_VALUE = 2;
}
暗黙的な存在を持つこのフィールド定義の場合
Bar bar = 1;
コンパイラは以下のアクセサメソッドを生成します
Bar bar() const
: フィールドの現在の値を返します。フィールドが設定されていない場合、デフォルト値 (0) を返します。void set_bar(Bar value)
: フィールドの値を設定します。これを呼び出すと、bar()
はvalue
を返します。void clear_bar()
: フィールドの値をクリアします。これを呼び出すと、bar()
はデフォルト値を返します。
明示的な存在を持つ埋め込みメッセージフィールド
メッセージ型を考えると:
message Bar {}
明示的な存在を持つこのフィールド定義の場合
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* value)
: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_t foo(int index) const
: 指定された0ベースのインデックスの要素を返します。このメソッドを [0, foo_size()) の範囲外のインデックスで呼び出すと未定義の動作になります。void set_foo(int index, int32_t value)
: 指定された0ベースのインデックスの要素の値を設定します。void add_foo(int32_t value)
: 指定された値を持つ新しい要素をフィールドの末尾に追加します。void clear_foo()
: フィールドからすべての要素を削除します。これを呼び出すと、foo_size()
はゼロを返します。const RepeatedField<int32_t>& foo() const
: フィールドの要素を格納する基になるRepeatedField
を返します。このコンテナクラスは、STLのようなイテレータやその他のメソッドを提供します。RepeatedField<int32_t>* mutable_foo()
: フィールドの要素を格納する基になる変更可能なRepeatedField
へのポインタを返します。このコンテナクラスは、STLのようなイテレータやその他のメソッドを提供します。
その他の数値フィールド型 (bool
を含む) の場合、int32_t
は、スカラ値型のテーブルに従って対応する C++ 型に置き換えられます。
繰り返し文字列フィールド
注: Edition 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)
: 渡された文字列から移動して、指定されたゼロベースのインデックスにある要素の値を設定します。void set_foo(int index, const char* value)
: Cスタイルのヌル終端文字列を使用して、指定されたゼロベースのインデックスにある要素の値を設定します。void set_foo(int index, const char* value, int size)
: ヌルターミネータバイトを探して決定するのではなく、明示的に指定されたサイズの C スタイル文字列を使用して、指定されたゼロベースのインデックスの要素の値を設定します。string* mutable_foo(int index)
: 指定された0ベースのインデックスの要素の値を格納する変更可能なstring
オブジェクトへのポインタを返します。このメソッドを [0, foo_size()) の範囲外のインデックスで呼び出すと未定義の動作になります。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 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, bar_size()) の範囲外のインデックスで呼び出すと、未定義の動作になります。Bar* mutable_bar(int index)
: 指定されたゼロベースのインデックスにある要素の値を格納する変更可能な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
を返します。
- 同じ oneof 内の他の oneof フィールドが設定されている場合、
void clear_foo()
:- oneof ケースが
kFoo
でない場合、何も変更されません。 - oneof ケースが
kFoo
の場合、フィールドの値と oneof ケースをクリアします。has_foo()
はfalse
を返し、foo()
はデフォルト値を返し、example_name_case()
はEXAMPLE_NAME_NOT_SET
を返します。
- oneof ケースが
その他の数値フィールド型 (bool
を含む) の場合、int32_t
は、スカラ値型のテーブルに従って対応する C++ 型に置き換えられます。
Oneof 文字列フィールド
注: Edition 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
を返します。
- 同じ 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スタイルのヌル終端文字列からコピーします。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
を返します。
- 同じ oneof 内の他の oneof フィールドが設定されている場合、
void clear_foo()
:- oneof ケースが
kFoo
でない場合、何も変更されません。 - oneof ケースが
kFoo
の場合、フィールドを解放し、oneof ケースをクリアします。has_foo()
はfalse
を返し、foo()
はデフォルト値を返し、example_name_case()
はEXAMPLE_NAME_NOT_SET
を返します。
- oneof ケースが
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 ケースが
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
に定義されている値のいずれにも一致せず、それがクローズドな列挙型である場合、このメソッドはプロセスをアボートします。
- 同じ oneof 内の他の oneof フィールドが設定されている場合、
void clear_bar()
:- oneof ケースが
kBar
でない場合、何も変更されません。 - oneof ケースが
kBar
の場合、フィールドの値と oneof ケースをクリアします。has_bar()
はfalse
を返し、bar()
はデフォルト値を返し、example_name_case()
はEXAMPLE_NAME_NOT_SET
を返します。
- oneof ケースが
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
を返します。
- 同じ oneof 内の他の oneof フィールドが設定されている場合、
void clear_bar()
:- oneof ケースが
kBar
でない場合、何も変更されません。 - oneof ケースが
kBar
に等しい場合、フィールドを解放し、oneof ケースをクリアします。has_bar()
はfalse
を返し、bar()
はデフォルト値を返し、example_name_case()
はEXAMPLE_NAME_NOT_SET
を返します。
- oneof ケースが
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
を返します。
- oneof ケースが
マップフィールド
このmapフィールド定義の場合:
map<int32, int32> weight = 1;
コンパイラは以下のアクセサメソッドを生成します
const google::protobuf::Map<int32, int32>& weight();
: 不変のMap
を返します。google::protobuf::Map<int32, int32>* mutable_weight();
: 可変のMap
を返します。
google::protobuf::Map
は、プロトコルバッファでマップフィールドを格納するために使用される特別なコンテナ型です。以下のインターフェースからわかるように、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、および editions で異なる方法で処理されます。proto2 では、マップエントリメッセージ全体が、包含するメッセージの不明なフィールドセットに入れられます。proto3 では、既知の enum 値であるかのようにマップフィールドに入れられます。editions の場合、デフォルトでは proto3 の動作を反映します。features.enum_type
が CLOSED
に設定されている場合、proto2 の動作を反映します。
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
に設定します。
列挙型
注: Edition 2024 以降、特定の機能設定で string_view
API が生成される場合があります。このトピックの詳細については、列挙型名ヘルパーを参照してください。
以下のような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 フィールドアクセサによって返されます。
proto3 および editions enum を switch ステートメントで使用する際は注意してください。 Proto3 および editions enum は、指定されたシンボルの範囲外の値を持つ可能性のあるオープン enum 型です。(editions enum は、enum_type
機能を使用してクローズド enum に設定できます。) オープン enum 型の認識されない enum 値は、メッセージの解析時に保持され、enum フィールドアクセサによって返されます。デフォルトケースのないオープン enum の switch ステートメントは、すべての既知のフィールドがリストされていても、すべてのケースを捕捉できません。これにより、データ破損や実行時クラッシュなどの予期しない動作につながる可能性があります。常にデフォルトケースを追加するか、switch の外部で明示的に Foo_IsValid(int)
を呼び出して、不明な enum 値を処理してください。
メッセージ型内に enum を定義できます。この場合、Protocol Buffer コンパイラは、enum 型自体がメッセージのクラス内にネストされて宣言されたかのように見えるコードを生成します。Foo_descriptor()
および Foo_IsValid()
関数は静的メソッドとして宣言されます。実際には、enum 型自体とその値は、グローバルスコープでマングルされた名前で宣言され、typedef
と一連の定数定義によってクラスのスコープにインポートされます。これは宣言順序の問題を回避するためにのみ行われます。マングルされたトップレベルの名前に依存しないでください。enum は本当にメッセージクラス内にネストされていると見なしてください。
拡張機能 (proto2 および edition のみ)
拡張範囲を持つメッセージが与えられた場合
message Foo {
extensions 100 to 199;
}
プロトコルバッファコンパイラは、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
の場合、プロトコルバッファコンパイラは 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 システムに結びついていないため、特定のシステムに合わせたコードよりも間接的なレベルが多くなり、望ましくない場合があります。このコードを生成したくない場合は、ファイルに次の行を追加してください。
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()
が呼び出されます。
プロトコルバッファライブラリには RPC 実装は含まれていません。しかし、生成されたサービス クラスを任意の RPC 実装に接続するために必要なツールはすべて含まれています。必要なのは、RpcChannel
と RpcController
の実装を提供するだけです。service.h
のドキュメントで詳細を確認してください。
プラグイン挿入ポイント
C++ コードジェネレータの出力を拡張したいコードジェネレータプラグインは、指定された挿入ポイント名を使用して以下の種類のコードを挿入できます。各挿入ポイントは、特に明記されていない限り、.pb.cc
ファイルと .pb.h
ファイルの両方に表示されます。
includes
: インクルードディレクティブ。namespace_scope
: ファイルのパッケージ/名前空間に属するが、特定のクラス内ではない宣言。他のすべての名前空間スコープコードの後に表示されます。global_scope
: ファイルの名前空間の外側のトップレベルに属する宣言。ファイルの最後に表示されます。class_scope:TYPENAME
: メッセージクラスに属するメンバ宣言。TYPENAME
は、package.MessageType
のような完全なプロト名です。クラス内の他のすべての公開宣言の後に表示されます。この挿入ポイントは.pb.h
ファイルにのみ表示されます。
標準のコードジェネレータによって宣言されたプライベートクラスメンバーに依存するコードは生成しないでください。これらの実装詳細は、Protocol Buffers の将来のバージョンで変更される可能性があります。