Ayrıca bakınız: C++ sürümü: C++‘da özel dizi dönüştürücüler nasıl işlenir
NanoPB, gömülü sistemler için kod boyutu açısından optimize edilmiş bir Protocol Buffers uygulamasıdır. Bu yazı, NanoPB ile C’de özel dizi dönüştürücülerin nasıl işleneceğini gösterir.
Proto tanımı
Önce tekrarlanan alanlar içeren bir .proto dosyası oluşturun:
syntax = "proto3";
package example;
message CustomArrayMessage {
repeated uint32 values = 1;
}NanoPB kodu oluşturma
Maksimum sayıyı belirtmek için bir .options dosyası ile NanoPB kodunu oluşturun:
custom_array.options oluşturun:
example.CustomArrayMessage.values max_count:20Ardından oluşturun:
protoc --nanopb_out=. custom_array.protoBu, custom_array.pb.h ve custom_array.pb.c dosyalarını oluşturur.
Özel dizi dönüştürücülü C örneği
Ofset ve boyutla özel bir dizi dönüştürücü uygulayan tam bir C örneği:
#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"
// Özel dizi dönüştürücü bağlamı
typedef struct {
const uint32_t* data;
size_t size;
size_t offset;
size_t count;
} array_context_t;
// Ofset ve boyutlu uint32 dizisi için kodlama geri çağrısı
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() {
// Kodlanmış mesaj için arabellek
uint8_t buffer[256];
size_t message_length;
// 10 elemanlı dizi oluştur
uint32_t values[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// --- KODLAMA ---
example_CustomArrayMessage message = example_CustomArrayMessage_init_zero;
// Bağlamı ayarla: 3-7. elemanları kodla (offset=2, count=5)
array_context_t ctx = {
.data = values,
.size = 10,
.offset = 2,
.count = 5
};
// Geri çağrıyı ayarla
message.values.funcs.encode = uint32_array_encode_callback;
message.values.arg = &ctx;
// Kodlama için akış oluştur
pb_ostream_t ostream = pb_ostream_from_buffer(buffer, sizeof(buffer));
// Mesajı kodla
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);
// Hex dökümünü yazdır
printf("Encoded data: ");
for (size_t i = 0; i < message_length; i++) {
printf("%02x ", buffer[i]);
}
printf("\n");
// Kodlanan değerleri yazdır
printf("Encoded values: ");
for (size_t i = 2; i < 2 + 5 && i < 10; i++) {
printf("%u ", values[i]);
}
printf("\n");
return 0;
}Derleme komutu
Örneği nanopb ile derleyin. NanoPB genellikle kaynak dosyaları doğrudan projenize dahil edilerek kullanılır:
gcc -o custom_array_example custom_array_example.c custom_array.pb.c pb_common.c pb_encode.c pb_decode.c -I.Not: NanoPB kaynak dosyaları (pb_common.c, pb_encode.c, pb_decode.c) projenizle birlikte doğrudan derlenmelidir. Bunları NanoPB GitHub deposundan edinebilirsiniz.
Önemli noktalar
- Bağlam yapısı: Ofset, sayı ve veri işaretçisini geçirmek için bir struct kullanın
- Ofset mantığı: Belirtilen ofsetten kodlamaya başlayın
- Sayı sınırı: Ofsetten sonra en fazla count elemanı kodlayın
- Geri çağrı deseni: Özel kodlama fonksiyonuyla pb_callback_t kullanın
- Esneklik: Kopyalamadan bir dizinin herhangi bir dilimini kodlayabilir
- Bellek verimliliği: Geçici diziler oluşturmaya gerek yok
- KKS-Firmware deseni: KKS-Firmware’den ArrayConverterWithOffsetAndSize’a dayanır
Özel dizi dönüştürücüler ne zaman kullanılır
- Büyük bir dizinin alt kümesini kodlamanız gerektiğinde
- Veri kopyalamaktan kaçınmak istediğinizde
- Sayfalama veya parçalama uygularken
- Dizi düzeni protobuf gereksinimleriyle eşleşmediğinde
- Diziler için özel kodlama mantığına ihtiyaç duyduğunuzda
Beklenen çıktı
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 Yalnızca 3, 4, 5, 6, 7 numaralı elemanların kodlandığına dikkat edin (ofset 2’den başlayan 5 eleman).
Gelişmiş: Genel şablon benzeri uygulama
Daha genel bir C uygulaması (şablonları taklit ederek):
#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;
}Gerçek dünya kullanımı
Bu desen KKS-Firmware’de şunlar için kullanılır:
- Daha büyük bir kanal dizisinden yalnızca aktif kanalları kodlama
- Bant genişliğinden tasarruf etmek için kısmi sensör verileri gönderme
- Farklı güncellemeler uygulama
- Seyrek dizileri verimli şekilde işleme
C++‘dan farkları
C sürümü C++‘dan birkaç yönden farklıdır:
- Şablon yok: Bunun yerine struct’lar ve fonksiyon işaretçileri kullanın
- Manuel bağlam yönetimi: Bağlamı açıkça geçirmeniz gerekir
- std::array yok: Boyut takibiyle düz C dizileri kullanın
- std::algorithm yok: Yinelemeyi manuel olarak uygulayın
- Aynı geri çağrı deseni: İkisi de pb_callback_t kullanır
Diğer NanoPB yazıları
- C++‘da temel skaler tipler
- C’de temel skaler tipler
- C++‘da string tipleri
- C’de string tipleri
- C++‘da bytes tipleri
- C’de bytes tipleri
- C++‘da opsiyonel alanlar
- C’de opsiyonel alanlar
- C++‘da tekrarlanan alanlar/diziler
- C’de tekrarlanan alanlar/diziler
- C++‘da enum’lar
- C’de enum’lar
- C++‘da iç içe mesajlar
- C’de iç içe mesajlar
- C++‘da Oneof/union tipleri
- C’de Oneof/union tipleri
- C++‘da özel dizi dönüştürücüler