Viz také: Verze pro C++: Jak pracovat s vlastními konvertory polí 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 vlastními konvertory polí v C s NanoPB.
Definice Proto
Nejprve vytvořte soubor .proto s opakovanými poli:
syntax = "proto3";
package example;
message CustomArrayMessage {
repeated uint32 values = 1;
}Generování NanoPB kódu
Vygenerujte kód NanoPB se souborem .options pro určení maximálního počtu:
Vytvořte custom_array.options:
example.CustomArrayMessage.values max_count:20Poté vygenerujte:
protoc --nanopb_out=. custom_array.protoTím se vygenerují soubory custom_array.pb.h a custom_array.pb.c.
Příklad v C s vlastním konvertorem polí
Zde je kompletní příklad v C implementující vlastní konvertor polí s offsetem a velikostí:
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <limits.h>
#include "custom_array.pb.h"
#include "pb_encode.h"
#include "pb_decode.h"
// Kontext vlastního konvertoru polí
typedef struct {
const uint32_t* data;
size_t size;
size_t offset;
size_t count;
} array_context_t;
// Callback kodéru pro pole uint32 s offsetem a velikostí
bool uint32_array_encode_callback(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) {
const array_context_t* ctx = (const array_context_t*)*arg;
size_t start = ctx->offset;
size_t end = (ctx->offset + ctx->count < ctx->size) ?
ctx->offset + ctx->count : ctx->size;
for (size_t i = start; i < end; i++) {
if (!pb_encode_tag_for_field(stream, field))
return false;
if (!pb_encode_varint(stream, ctx->data[i]))
return false;
}
return true;
}
int main() {
// Buffer pro kódovanou zprávu
uint8_t buffer[256];
size_t message_length;
// Vytvoření pole s 10 prvky
uint32_t values[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// --- KÓDOVÁNÍ ---
example_CustomArrayMessage message = example_CustomArrayMessage_init_zero;
// Nastavení kontextu: kódovat prvky 3-7 (offset=2, count=5)
array_context_t ctx = {
.data = values,
.size = 10,
.offset = 2,
.count = 5
};
// Nastavení callbacku
message.values.funcs.encode = uint32_array_encode_callback;
message.values.arg = &ctx;
// 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_CustomArrayMessage_fields, &message)) {
printf("Encoding failed: %s\n", PB_GET_ERROR(&ostream));
return 1;
}
message_length = ostream.bytes_written;
printf("Encoded %zu bytes (elements 3-7)\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");
// Výpis toho, co bylo zakódováno
printf("Encoded values: ");
for (size_t i = 2; i < 2 + 5 && i < 10; i++) {
printf("%u ", values[i]);
}
printf("\n");
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:
gcc -o custom_array_example custom_array_example.c custom_array.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.
Klíčové body
- Kontextová struktura: Použijte strukturu pro předání offsetu, počtu a ukazatele na data
- Logika offsetu: Začněte kódování na zadaném offsetu
- Omezení počtu: Zakódujte maximálně count prvků po offsetu
- Vzor callbacku: Použijte pb_callback_t s vlastní funkcí kódování
- Flexibilita: Lze zakódovat libovolný řez pole bez kopírování
- Efektivita paměti: Není potřeba vytvářet dočasná pole
- Vzor KKS-Firmware: Založeno na ArrayConverterWithOffsetAndSize z KKS-Firmware
Kdy použít vlastní konvertory polí
- Pokud potřebujete zakódovat podmnožinu velkého pole
- Pokud chcete předejít kopírování dat
- Při implementaci stránkování nebo dělení na bloky
- Pokud rozložení pole neodpovídá požadavkům protobuf
- Pokud potřebujete vlastní logiku kódování pro pole
Očekávaný výstup
Encoded 10 bytes (elements 3-7)
Encoded data: 08 03 08 04 08 05 08 06 08 07
Encoded values: 3 4 5 6 7 Všimněte si, že jsou zakódovány pouze prvky 3, 4, 5, 6, 7 (5 prvků začínajících od offsetu 2).
Pokročilé: Generická implementace ve stylu šablon
Pro obecnější implementaci v C (simulace šablon):
#include <stddef.h>
#include <limits.h>
#define MAX_OFFSET_SIZE (SIZE_MAX / 2)
typedef struct {
const void* data;
size_t element_size;
size_t size;
size_t offset;
size_t count;
bool (*encode_element)(pb_ostream_t*, const pb_field_t*, const void*);
} generic_array_context_t;
bool generic_array_encode_callback(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) {
const generic_array_context_t* ctx = (const generic_array_context_t*)*arg;
size_t start = ctx->offset;
size_t end = (ctx->offset + ctx->count < ctx->size) ?
ctx->offset + ctx->count : ctx->size;
const uint8_t* data = (const uint8_t*)ctx->data;
for (size_t i = start; i < end; i++) {
const void* element = data + (i * ctx->element_size);
if (!ctx->encode_element(stream, field, element)) {
return false;
}
}
return true;
}Reálné použití
Tento vzor se používá v KKS-Firmware pro:
- Kódování pouze aktivních kanálů z většího pole kanálů
- Odesílání částečných dat ze senzorů pro úsporu šířky pásma
- Implementaci diferenciálních aktualizací
- Efektivní zpracování řídkých polí
Rozdíly oproti C++
Verze v C se od C++ liší v několika ohledech:
- Žádné šablony: Místo toho použijte struktury a ukazatele na funkce
- Ruční správa kontextu: Kontext musí být předán explicitně
- Žádné std::array: Použijte obyčejná C pole se sledováním velikosti
- Žádné std::algorithm: Iteraci implementujte ručně
- Stejný vzor callbacku: Oba používají pb_callback_t
Další 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++
- Oneof/union typy v C
- Vlastní konvertory polí v C++