NanoPB: Jak pracovat s oneof (union) typy v C++
Viz také: Verze pro C: Jak pracovat s oneof/union typy v C
NanoPB je implementace Protocol Buffers optimalizovaná na velikost kódu pro vestavěné systémy. Tento příspěvek ukazuje, jak pracovat s oneof (union) typy v C++ s NanoPB.
Definice Proto
Nejprve vytvořte soubor .proto s poli oneof:
syntax = "proto3";
package example;
message OneofMessage {
oneof value {
uint32 int_value = 1;
float float_value = 2;
string string_value = 3;
}
}Generování NanoPB kódu
Vygenerujte kód NanoPB se souborem .options pro určení velikostí bufferu pro řetězce:
Vytvořte oneof.options:
example.OneofMessage.string_value max_size:32Poté vygenerujte:
protoc --nanopb_out=. oneof.protoTím se vygenerují soubory oneof.pb.h a oneof.pb.c.
Příklad v C++
Zde je kompletní příklad v C++ zpracovávající pole oneof:
#include <stdio.h>
#include <string.h>
#include "oneof.pb.h"
#include "pb_encode.h"
#include "pb_decode.h"
int main() {
// Buffer pro kódovanou zprávu
uint8_t buffer[128];
size_t message_length;
// --- KÓDOVÁNÍ (int_value) ---
example_OneofMessage message = example_OneofMessage_init_zero;
// Nastavení pole oneof (najednou lze nastavit pouze jedno)
message.which_value = example_OneofMessage_int_value_tag;
message.value.int_value = 42;
// Vytvoření streamu pro kódování
pb_ostream_t ostream = pb_ostream_from_buffer(buffer, sizeof(buffer));
// Kódování zprávy
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);
// Výpis hex dumpu
printf("Encoded data: ");
for (size_t i = 0; i < message_length; i++) {
printf("%02x ", buffer[i]);
}
printf("\n");
// --- DEKÓDOVÁNÍ ---
example_OneofMessage decoded = example_OneofMessage_init_zero;
// Vytvoření streamu pro dekódování
pb_istream_t istream = pb_istream_from_buffer(buffer, message_length);
// Dekódování zprávy
if (!pb_decode(&istream, example_OneofMessage_fields, &decoded)) {
printf("Decoding failed: %s\n", PB_GET_ERROR(&istream));
return 1;
}
// Výpis dekódované hodnoty
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;
}Příkaz pro kompilaci
Zkompilujte příklad s nanopb. NanoPB se obvykle používá tak, že zdrojové soubory se přímo vloží do vašeho projektu:
g++ -o oneof_example oneof_example.cpp oneof.pb.c pb_common.c pb_encode.c pb_decode.c -I.Poznámka: Zdrojové soubory NanoPB (pb_common.c, pb_encode.c, pb_decode.c) je třeba kompilovat přímo s vaším projektem. Můžete je získat z NanoPB GitHub repozitáře.
Testovací skript v Pythonu
Pro ověření kódování můžete použít knihovnu protobuf v Pythonu:
import oneof_pb2
# Načtení binárních dat
with open('encoded.bin', 'rb') as f:
data = f.read()
# Dekódování
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}")Nejprve zkompilujte definice protobuf pro Python:
protoc --python_out=. oneof.protoPoté upravte příklad v C++ tak, aby ukládal kódovaná data do souboru:
// Po kódování přidejte toto:
FILE *f = fopen("encoded.bin', "wb");
fwrite(buffer, 1, message_length, f);
fclose(f);Příklad se všemi typy oneof
Zde je příklad ukazující všechny typy oneof:
#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;
}Klíčové body
- Definice oneof: Použijte klíčové slovo
oneofv proto pro definici union typů - Generované which_field: NanoPB generuje výčet
which_fieldnamepro sledování, které pole je nastaveno - Kódování: Nastavte
which_*na příslušný tag, poté nastavte hodnotu pole - Dekódování: Zkontrolujte
which_*pro určení, které pole bylo nastaveno - Vzájemně se vylučující: Najednou lze nastavit pouze jedno pole v oneof
- Sémantika union: Podobné jako C union, ale s bezpečností typů
- Efektivita paměti: Alokuje se pouze paměť pro jedno pole
Kdy použít oneof
- Pokud by měla být přítomna pouze jedna z několika možných hodnot
- Pokud chcete ušetřit paměť tím, že nealokujete prostor pro všechna pole
- Pokud potřebujete bezpečné union typy
- Při reprezentaci variantních typů nebo tagovaných union
- Při implementaci polymorfních zpráv
Očekávaný výstup
Encoded 2 bytes (int_value)
Encoded data: 08 2a
Decoded value:
int_value: 42Očekávaný výstup (všechny typy)
Testing all oneof types:
int_value: 42
float_value: 3.141590
string_value: HelloDalší příspěvky o NanoPB
- Základní skalární typy v C++
- Základní skalární typy v C
- Řetězcové typy v C++
- Řetězcové typy v C
- Bytové typy v C++
- Bytové typy v C
- Volitelná pole v C++
- Volitelná pole v C
- Opakovaná pole/pole v C++
- Opakovaná pole/pole v C
- Výčty v C++
- Výčty v C
- Vnořené zprávy v C++
- Vnořené zprávy v C
- Oneof/union typy v C
- Vlastní konvertory polí v C++
- Vlastní konvertory polí v C