関連記事: C 版: C で oneof/union 型を扱う方法
NanoPB は組み込みシステム向けにコードサイズを最適化した Protocol Buffers 実装です。この記事では、NanoPB で C++ の oneof(union)型を扱う方法を説明します。
Proto 定義
まず、oneof フィールドを持つ .proto ファイルを作成します。
oneof.proto
syntax = "proto3";
package example;
message OneofMessage {
oneof value {
uint32 int_value = 1;
float float_value = 2;
string string_value = 3;
}
}NanoPB コードの生成
文字列バッファサイズを指定する .options ファイルを使って NanoPB コードを生成します。
oneof.options を作成します。
oneof.options
example.OneofMessage.string_value max_size:32次に生成します。
generate_nanopb_oneof.sh
protoc --nanopb_out=. oneof.protoこれにより oneof.pb.h と oneof.pb.c が生成されます。
C++ の例
oneof フィールドを扱う完全な C++ の例を示します。
oneof_example.cpp
#include <stdio.h>
#include <string.h>
#include "oneof.pb.h"
#include "pb_encode.h"
#include "pb_decode.h"
int main() {
// エンコードされたメッセージ用バッファ
uint8_t buffer[128];
size_t message_length;
// --- エンコード (int_value) ---
example_OneofMessage message = example_OneofMessage_init_zero;
// oneof フィールドを設定(同時に1つだけ設定可能)
message.which_value = example_OneofMessage_int_value_tag;
message.value.int_value = 42;
// エンコード用ストリームを作成
pb_ostream_t ostream = pb_ostream_from_buffer(buffer, sizeof(buffer));
// メッセージをエンコード
if (!pb_encode(&ostream, example_OneofMessage_fields, &message)) {
printf("Encoding failed: %s\n", PB_GET_ERROR(&ostream));
return 1;
}
message_length = ostream.bytes_written;
printf("Encoded %zu bytes (int_value)\n", message_length);
// 16進ダンプを出力
printf("Encoded data: ");
for (size_t i = 0; i < message_length; i++) {
printf("%02x ", buffer[i]);
}
printf("\n");
// --- デコード ---
example_OneofMessage decoded = example_OneofMessage_init_zero;
// デコード用ストリームを作成
pb_istream_t istream = pb_istream_from_buffer(buffer, message_length);
// メッセージをデコード
if (!pb_decode(&istream, example_OneofMessage_fields, &decoded)) {
printf("Decoding failed: %s\n", PB_GET_ERROR(&istream));
return 1;
}
// デコードされた値を出力
printf("Decoded value:\n");
switch (decoded.which_value) {
case example_OneofMessage_int_value_tag:
printf(" int_value: %u\n", decoded.value.int_value);
break;
case example_OneofMessage_float_value_tag:
printf(" float_value: %f\n", decoded.value.float_value);
break;
case example_OneofMessage_string_value_tag:
printf(" string_value: %s\n", decoded.value.string_value);
break;
default:
printf(" (none)\n");
break;
}
return 0;
}コンパイルコマンド
nanopb を使って例をコンパイルします。NanoPB は通常、ソースファイルをプロジェクトに直接含めて使用します。
compile_oneof_example.sh
g++ -o oneof_example oneof_example.cpp oneof.pb.c pb_common.c pb_encode.c pb_decode.c -I.注意: NanoPB のソースファイル(pb_common.c、pb_encode.c、pb_decode.c)は、プロジェクトと一緒に直接コンパイルする必要があります。これらは NanoPB GitHub リポジトリ から入手できます。
Python テストスクリプト
エンコードを検証するために、Python の protobuf ライブラリを使用できます。
test_oneof.py
import oneof_pb2
# バイナリデータを読み込む
with open('encoded.bin', 'rb') as f:
data = f.read()
# デコード
msg = oneof_pb2.OneofMessage()
msg.ParseFromString(data)
print("Python decoded values:")
print(f" Which oneof: {msg.WhichOneof('value')}")
if msg.HasField('int_value'):
print(f" int_value: {msg.int_value}")
if msg.HasField('float_value'):
print(f" float_value: {msg.float_value}")
if msg.HasField('string_value'):
print(f" string_value: {msg.string_value}")まず、Python の protobuf 定義をコンパイルします。
compile_python_oneof.sh
protoc --python_out=. oneof.proto次に、エンコードされたデータをファイルに保存するように C++ の例を変更します。
save_encoded_oneof.cpp
// エンコード後にこれを追加:
FILE *f = fopen("encoded.bin', "wb");
fwrite(buffer, 1, message_length, f);
fclose(f);すべての oneof 型を使う例
すべての oneof 型を示す例を以下に示します。
oneof_all_example.cpp
#include <stdio.h>
#include <string.h>
#include "oneof.pb.h"
#include "pb_encode.h"
#include "pb_decode.h"
void encode_decode_int_value() {
uint8_t buffer[128];
size_t message_length;
example_OneofMessage message = example_OneofMessage_init_zero;
message.which_value = example_OneofMessage_int_value_tag;
message.value.int_value = 42;
pb_ostream_t ostream = pb_ostream_from_buffer(buffer, sizeof(buffer));
if (!pb_encode(&ostream, example_OneofMessage_fields, &message)) {
printf("Encoding failed\n");
return;
}
message_length = ostream.bytes_written;
example_OneofMessage decoded = example_OneofMessage_init_zero;
pb_istream_t istream = pb_istream_from_buffer(buffer, message_length);
if (!pb_decode(&istream, example_OneofMessage_fields, &decoded)) {
printf("Decoding failed\n");
return;
}
printf("int_value: %u\n", decoded.value.int_value);
}
void encode_decode_float_value() {
uint8_t buffer[128];
size_t message_length;
example_OneofMessage message = example_OneofMessage_init_zero;
message.which_value = example_OneofMessage_float_value_tag;
message.value.float_value = 3.14159f;
pb_ostream_t ostream = pb_ostream_from_buffer(buffer, sizeof(buffer));
if (!pb_encode(&ostream, example_OneofMessage_fields, &message)) {
printf("Encoding failed\n");
return;
}
message_length = ostream.bytes_written;
example_OneofMessage decoded = example_OneofMessage_init_zero;
pb_istream_t istream = pb_istream_from_buffer(buffer, message_length);
if (!pb_decode(&istream, example_OneofMessage_fields, &decoded)) {
printf("Decoding failed\n");
return;
}
printf("float_value: %f\n", decoded.value.float_value);
}
void encode_decode_string_value() {
uint8_t buffer[128];
size_t message_length;
example_OneofMessage message = example_OneofMessage_init_zero;
message.which_value = example_OneofMessage_string_value_tag;
strncpy(message.value.string_value, "Hello", sizeof(message.value.string_value) - 1);
pb_ostream_t ostream = pb_ostream_from_buffer(buffer, sizeof(buffer));
if (!pb_encode(&ostream, example_OneofMessage_fields, &message)) {
printf("Encoding failed\n");
return;
}
message_length = ostream.bytes_written;
example_OneofMessage decoded = example_OneofMessage_init_zero;
pb_istream_t istream = pb_istream_from_buffer(buffer, message_length);
if (!pb_decode(&istream, example_OneofMessage_fields, &decoded)) {
printf("Decoding failed\n");
return;
}
printf("string_value: %s\n", decoded.value.string_value);
}
int main() {
printf("Testing all oneof types:\n");
encode_decode_int_value();
encode_decode_float_value();
encode_decode_string_value();
return 0;
}ポイント
- Oneof 定義: proto で
oneofキーワードを使って union 型を定義します - 生成される which_field: NanoPB はどのフィールドが設定されているかを追跡する
which_fieldname列挙型を生成します - エンコード:
which_*を適切なタグに設定し、フィールドの値を設定します - デコード:
which_*を確認して、どのフィールドが設定されたかを判断します - 相互排他: oneof 内では同時に1つのフィールドのみ設定できます
- Union セマンティクス: C の union に似ていますが、型安全性があります
- メモリ効率: 1つのフィールド分のメモリのみが割り当てられます
oneof を使う場面
- 複数の可能な値のうち1つだけが存在するべき場合
- すべてのフィールドにスペースを割り当てずにメモリを節約したい場合
- 型安全な union が必要な場合
- バリアント型やタグ付き union を表現する場合
- ポリモーフィックなメッセージを実装する場合
期待される出力
oneof_expected_output.txt
Encoded 2 bytes (int_value)
Encoded data: 08 2a
Decoded value:
int_value: 42期待される出力(すべての型)
oneof_all_expected_output.txt
Testing all oneof types:
int_value: 42
float_value: 3.141590
string_value: Helloその他の NanoPB 記事
同じカテゴリの類似記事をチェック:
組み込み C/C++ Protocol Buffers
この記事が役に立った場合は、TechOverflow での新しい記事の調査と公開を支援するため、「Buy Me a Coffee」でのご支援、または PayPal 経由でのご寄付をご検討ください