NanoPB: Как обрабатывать типы oneof (union) в C++
Смотрите также: Версия на C: Как обрабатывать типы oneof/union в C
NanoPB — это оптимизированная по размеру кода реализация Protocol Buffers для встраиваемых систем. В этом посте показано, как обрабатывать типы oneof (union) в C++ с помощью NanoPB.
Определение Proto
Сначала создайте файл .proto с полями oneof:
syntax = "proto3";
package example;
message OneofMessage {
oneof value {
uint32 int_value = 1;
float float_value = 2;
string string_value = 3;
}
}Генерация кода NanoPB
Сгенерируйте код NanoPB с помощью файла .options для указания размеров строковых буферов:
Создайте oneof.options:
example.OneofMessage.string_value max_size:32Затем сгенерируйте:
protoc --nanopb_out=. oneof.protoЭто создаст oneof.pb.h и oneof.pb.c.
Пример на C++
Вот полный пример на C++ для обработки полей oneof:
#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 (можно установить только одно за раз)
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("Кодирование не удалось: %s\n", PB_GET_ERROR(&ostream));
return 1;
}
message_length = ostream.bytes_written;
printf("Закодировано %zu байт (int_value)\n", message_length);
// Выводим шестнадцатеричный дамп
printf("Закодированные данные: ");
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("Декодирование не удалось: %s\n", PB_GET_ERROR(&istream));
return 1;
}
// Выводим декодированное значение
printf("Декодированное значение:\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(" (нет)\n");
break;
}
return 0;
}Команда компиляции
Скомпилируйте пример с помощью nanopb. NanoPB обычно используется путём прямого включения исходных файлов в ваш проект:
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
Для проверки кодирования можно использовать библиотеку protobuf для Python:
import oneof_pb2
# Читаем бинарные данные
with open('encoded.bin', 'rb') as f:
data = f.read()
# Декодируем
msg = oneof_pb2.OneofMessage()
msg.ParseFromString(data)
print("Декодированные Python значения:")
print(f" Какой 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}")Сначала скомпилируйте определения protobuf для Python:
protoc --python_out=. oneof.protoЗатем измените пример на C++, чтобы сохранять закодированные данные в файл:
// После кодирования добавьте это:
FILE *f = fopen("encoded.bin', "wb");
fwrite(buffer, 1, message_length, f);
fclose(f);Пример со всеми типами oneof
Вот пример, демонстрирующий все типы 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("Кодирование не удалось\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("Декодирование не удалось\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("Кодирование не удалось\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("Декодирование не удалось\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("Кодирование не удалось\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("Декодирование не удалось\n");
return;
}
printf("string_value: %s\n", decoded.value.string_value);
}
int main() {
printf("Тестирование всех типов oneof:\n");
encode_decode_int_value();
encode_decode_float_value();
encode_decode_string_value();
return 0;
}Ключевые моменты
- Определение oneof: Используйте ключевое слово
oneofв proto для определения типов union - Сгенерированное which_field: NanoPB генерирует перечисление
which_fieldnameдля отслеживания установленного поля - Кодирование: Установите
which_*в соответствующий тег, затем установите значение поля - Декодирование: Проверьте
which_*, чтобы определить, какое поле было установлено - Взаимоисключаемость: Только одно поле в oneof может быть установлено за раз
- Семантика union: Аналогично union в C, но с типобезопасностью
- Эффективность памяти: Выделяется память только для одного поля
Когда использовать oneof
- Когда должно присутствовать только одно из нескольких возможных значений
- Когда нужно сэкономить память, не выделяя место для всех полей
- Когда нужны типобезопасные union
- При представлении вариантных типов или помеченных union
- При реализации полиморфных сообщений
Ожидаемый вывод
Encoded 2 bytes (int_value)
Encoded data: 08 2a
Decoded value:
int_value: 42Ожидаемый вывод (все типы)
Testing all oneof types:
int_value: 42
float_value: 3.141590
string_value: HelloДругие посты о NanoPB
- Базовые скалярные типы в C++
- Базовые скалярные типы в C
- Строковые типы в C++
- Строковые типы в C
- Типы bytes в C++
- Типы bytes в C
- Опциональные поля в C++
- Опциональные поля в C
- Повторяющиеся поля/массивы в C++
- Повторяющиеся поля/массивы в C
- Перечисления в C++
- Перечисления в C
- Вложенные сообщения в C++
- Вложенные сообщения в C
- Типы oneof/union в C
- Пользовательские конвертеры массивов в C++
- Пользовательские конвертеры массивов в C