関連記事: C 版: C で文字列型を扱う方法
NanoPB は組み込みシステム向けにコードサイズを最適化した Protocol Buffers 実装です。この記事では、NanoPB で C++ の文字列型を扱う方法を説明します。
Proto 定義
まず、文字列フィールドを持つ .proto ファイルを作成します。
strings.proto
syntax = "proto3";
package example;
message StringMessage {
string name = 1;
string description = 2;
}NanoPB コードの生成
文字列バッファサイズを指定する .options ファイルを使って NanoPB コードを生成します。
strings.options を作成します。
strings.options
example.StringMessage.name max_size:64
example.StringMessage.description max_size:256次に生成します。
generate_nanopb_strings.sh
protoc --nanopb_out=. strings.protoこれにより strings.pb.h と strings.pb.c が生成されます。
固定長バッファを使った C++ の例
固定長バッファを使った完全な C++ の例を示します。
strings_example.cpp
#include <stdio.h>
#include <string.h>
#include "strings.pb.h"
#include "pb_encode.h"
#include "pb_decode.h"
int main() {
// エンコードされたメッセージ用バッファ
uint8_t buffer[256];
size_t message_length;
// --- エンコード ---
example_StringMessage message = example_StringMessage_init_zero;
// 文字列値を設定(バッファサイズに収まる必要あり)
const char* name = "NanoPB";
const char* description = "Protocol Buffers for embedded systems";
strncpy(message.name, name, sizeof(message.name) - 1);
strncpy(message.description, description, sizeof(message.description) - 1);
// エンコード用ストリームを作成
pb_ostream_t ostream = pb_ostream_from_buffer(buffer, sizeof(buffer));
// メッセージをエンコード
if (!pb_encode(&ostream, example_StringMessage_fields, &message)) {
printf("Encoding failed: %s\n", PB_GET_ERROR(&ostream));
return 1;
}
message_length = ostream.bytes_written;
printf("Encoded %zu bytes\n", message_length);
// エンコードデータの16進ダンプを出力
printf("Encoded data: ");
for (size_t i = 0; i < message_length; i++) {
printf("%02x ", buffer[i]);
}
printf("\n");
// --- デコード ---
example_StringMessage decoded = example_StringMessage_init_zero;
// デコード用ストリームを作成
pb_istream_t istream = pb_istream_from_buffer(buffer, message_length);
// メッセージをデコード
if (!pb_decode(&istream, example_StringMessage_fields, &decoded)) {
printf("Decoding failed: %s\n", PB_GET_ERROR(&istream));
return 1;
}
// デコードされた値を出力
printf("Decoded values:\n");
printf(" name: %s\n", decoded.name);
printf(" description: %s\n", decoded.description);
return 0;
}コンパイルコマンド
nanopb を使って例をコンパイルします。NanoPB は通常、ソースファイルをプロジェクトに直接含めて使用します。
compile_strings_example.sh
g++ -o strings_example strings_example.cpp strings.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_strings.py
import strings_pb2
# バイナリデータを読み込む
with open('encoded.bin', 'rb') as f:
data = f.read()
# デコード
msg = strings_pb2.StringMessage()
msg.ParseFromString(data)
print("Python decoded values:")
print(f" name: {msg.name}")
print(f" description: {msg.description}")まず、Python の protobuf 定義をコンパイルします。
compile_python_strings.sh
protoc --python_out=. strings.proto次に、エンコードされたデータをファイルに保存するように C++ の例を変更します。
save_encoded_strings.cpp
// エンコード後にこれを追加:
FILE *f = fopen("encoded.bin", "wb");
fwrite(buffer, 1, message_length, f);
fclose(f);代替案:コールバックベースの文字列
動的な文字列処理にはコールバックを使用できます。strings_callback.options を作成します。
strings_callback.options
# 動的文字列にコールバックを使用
msg.StringMessage.name callback
msg.StringMessage.description callback次に再生成してこの方法を使用します。
strings_callback_example.cpp
#include <stdio.h>
#include <string>
#include "strings.pb.h"
#include "pb_encode.h"
#include "pb_decode.h"
// 文字列用エンコーダコールバック
bool string_encode_callback(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) {
const std::string* str = (const std::string*)*arg;
if (!pb_encode_tag_for_field(stream, field))
return false;
return pb_encode_string(stream, (const pb_byte_t*)str->c_str(), str->size());
}
// 文字列用デコーダコールバック
bool string_decode_callback(pb_istream_t *stream, const pb_field_t *field, void **arg) {
std::string* str = (std::string*)*arg;
// バッファを確保
size_t size = stream->bytes_left;
char* buf = new char[size];
if (!pb_read(stream, (pb_byte_t*)buf, size)) {
delete[] buf;
return false;
}
str->assign(buf, size);
delete[] buf;
return true;
}
int main() {
uint8_t buffer[256];
size_t message_length;
std::string name = "NanoPB";
std::string description = "Protocol Buffers for embedded systems";
// --- エンコード ---
example_StringMessage message = example_StringMessage_init_zero;
message.name.funcs.encode = string_encode_callback;
message.name.arg = &name;
message.description.funcs.encode = string_encode_callback;
message.description.arg = &description;
pb_ostream_t ostream = pb_ostream_from_buffer(buffer, sizeof(buffer));
if (!pb_encode(&ostream, example_StringMessage_fields, &message)) {
printf("Encoding failed: %s\n", PB_GET_ERROR(&ostream));
return 1;
}
message_length = ostream.bytes_written;
printf("Encoded %zu bytes\n", message_length);
// --- デコード ---
example_StringMessage decoded = example_StringMessage_init_zero;
std::string decoded_name, decoded_description;
decoded.name.funcs.decode = string_decode_callback;
decoded.name.arg = &decoded_name;
decoded.description.funcs.decode = string_decode_callback;
decoded.description.arg = &decoded_description;
pb_istream_t istream = pb_istream_from_buffer(buffer, message_length);
if (!pb_decode(&istream, example_StringMessage_fields, &decoded)) {
printf("Decoding failed: %s\n", PB_GET_ERROR(&istream));
return 1;
}
printf("Decoded values:\n");
printf(" name: %s\n", decoded_name.c_str());
printf(" description: %s\n", decoded_description.c_str());
return 0;
}ポイント
- 固定長バッファ: シンプルで静的な割り当てには、.options ファイルで
max_sizeを使用します - コールバックベース: 動的な文字列処理には、.options で
callbackを使用します - 固定長:
strncpyで文字列をコピーし、NULL 終端を確保します - コールバックベース: 動的割り当て用にエンコード/デコードコールバックを実装します
- オーバーフローを防ぐため、常にバッファサイズを確認してください
- コールバック方式では(メモリ制限内で)任意のサイズの文字列を扱えます
どちらの方法を使うべきか
- 固定長バッファ: 最大文字列サイズが分かっていて、シンプルなコードにしたい場合
- コールバックベース: 文字列サイズが可変の場合や、動的メモリ割り当てが必要な場合
期待される出力
strings_expected_output.txt
Encoded 38 bytes
Encoded data: 0a 06 4e 61 6e 6f 50 42 12 1e 50 72 6f 74 6f 63 6f 6c 20 42 75 66 66 65 72 73 20 66 6f 72 20 65 6d 62 65 64 64 65 64 20 73 79 73 74 65 6d 73
Decoded values:
name: NanoPB
description: Protocol Buffers for embedded systemsその他の NanoPB 記事
同じカテゴリの類似記事をチェック:
組み込み C/C++ Protocol Buffers
この記事が役に立った場合は、TechOverflow での新しい記事の調査と公開を支援するため、「Buy Me a Coffee」でのご支援、または PayPal 経由でのご寄付をご検討ください