NanoPB: C'de enum'lar nasıl işlenir

Ayrıca bakınız: C++ sürümü: C++‘da enum’lar 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 enum tiplerinin nasıl işleneceğini gösterir.

Proto tanımı

Önce enum’lar içeren bir .proto dosyası oluşturun:

enums.proto
syntax = "proto3";

package example;

enum Status {
  UNKNOWN = 0;
  OK = 1;
  ERROR = 2;
  BUSY = 3;
}

message EnumMessage {
  Status status = 1;
}

NanoPB kodu oluşturma

NanoPB kodunu oluşturun:

generate_nanopb_enums.sh
protoc --nanopb_out=. enums.proto

Bu, enums.pb.h ve enums.pb.c dosyalarını oluşturur.

C örneği

Enum’ları işleyen tam bir C örneği:

enums_example.c
#include <stdio.h>
#include <stdint.h>
#include "enums.pb.h"
#include "pb_encode.h"
#include "pb_decode.h"

// Enum'u stringe dönüştüren yardımcı fonksiyon
const char* status_to_string(example_Status status) {
    switch (status) {
        case example_Status_UNKNOWN: return "UNKNOWN";
        case example_Status_OK: return "OK";
        case example_Status_ERROR: return "ERROR";
        case example_Status_BUSY: return "BUSY";
        default: return "INVALID";
    }
}

int main() {
    // Kodlanmış mesaj için arabellek
    uint8_t buffer[64];
    size_t message_length;
    
    // --- KODLAMA ---
    example_EnumMessage message = example_EnumMessage_init_zero;
    
    // Enum değerini ayarla
    message.status = example_Status_OK;
    
    // Kodlama için akış oluştur
    pb_ostream_t ostream = pb_ostream_from_buffer(buffer, sizeof(buffer));
    
    // Mesajı kodla
    if (!pb_encode(&ostream, example_EnumMessage_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);
    
    // Kodlanmış verinin 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");
    
    // --- KOD ÇÖZME ---
    example_EnumMessage decoded = example_EnumMessage_init_zero;
    
    // Kod çözme için akış oluştur
    pb_istream_t istream = pb_istream_from_buffer(buffer, message_length);
    
    // Mesajın kodunu çöz
    if (!pb_decode(&istream, example_EnumMessage_fields, &decoded)) {
        printf("Decoding failed: %s\n", PB_GET_ERROR(&istream));
        return 1;
    }
    
    // Kodu çözülen değeri yazdır
    printf("Decoded value:\n");
    printf("  status: %s (enum value: %d)\n", 
           status_to_string(decoded.status), (int)decoded.status);
    
    return 0;
}

Derleme komutu

Örneği nanopb ile derleyin. NanoPB genellikle kaynak dosyaları doğrudan projenize dahil edilerek kullanılır:

compile_enums_example.sh
gcc -o enums_example enums_example.c enums.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.

Python test betiği

Kodlamayı doğrulamak için Python’un protobuf kütüphanesini kullanabilirsiniz:

test_enums.py
import enums_pb2

# İkili veriyi oku
with open('encoded.bin', 'rb') as f:
    data = f.read()

# Kod çöz
msg = enums_pb2.EnumMessage()
msg.ParseFromString(data)

print("Python decoded values:")
print(f"  status: {enums_pb2.Status.Name(msg.status)}")

Önce Python protobuf tanımlarını derleyin:

compile_python_enums.sh
protoc --python_out=. enums.proto

Ardından C örneğini, kodlanmış veriyi bir dosyaya kaydedecek şekilde değiştirin:

save_encoded_enums.c
// Kodlamadan sonra bunu ekleyin:
FILE *f = fopen("encoded.bin", "wb");
fwrite(buffer, 1, message_length, f);
fclose(f);

Switch ifadesinde enum örneği

Enum’ların switch ifadelerinde nasıl kullanılacağını gösteren bir örnek:

enums_switch_example.c
#include <stdio.h>
#include <stdint.h>
#include "enums.pb.h"
#include "pb_encode.h"
#include "pb_decode.h"

void handle_status(example_Status status) {
    printf("Handling status: ");
    
    switch (status) {
        case example_Status_UNKNOWN:
            printf("UNKNOWN - Default state\n");
            break;
        case example_Status_OK:
            printf("OK - Operation successful\n");
            break;
        case example_Status_ERROR:
            printf("ERROR - Operation failed\n");
            break;
        case example_Status_BUSY:
            printf("BUSY - System busy\n");
            break;
        default:
            printf("INVALID - Unknown enum value\n");
            break;
    }
}

int main() {
    uint8_t buffer[64];
    size_t message_length;
    
    // Tüm enum değerlerini test et
    example_Status test_values[] = {
        example_Status_UNKNOWN,
        example_Status_OK,
        example_Status_ERROR,
        example_Status_BUSY
    };
    
    for (size_t i = 0; i < 4; i++) {
        example_EnumMessage message = example_EnumMessage_init_zero;
        message.status = test_values[i];
        
        pb_ostream_t ostream = pb_ostream_from_buffer(buffer, sizeof(buffer));
        
        if (!pb_encode(&ostream, example_EnumMessage_fields, &message)) {
            printf("Encoding failed: %s\n", PB_GET_ERROR(&ostream));
            return 1;
        }
        
        message_length = ostream.bytes_written;
        
        example_EnumMessage decoded = example_EnumMessage_init_zero;
        pb_istream_t istream = pb_istream_from_buffer(buffer, message_length);
        
        if (!pb_decode(&istream, example_EnumMessage_fields, &decoded)) {
            printf("Decoding failed: %s\n", PB_GET_ERROR(&istream));
            return 1;
        }
        
        handle_status(decoded.status);
    }
    
    return 0;
}

Önemli noktalar

  • Enum tanımı: .proto dosyasında açık değerlerle enum’lar tanımlayın
  • Oluşturulan tipler: NanoPB, package_EnumName adlandırmasıyla enum tipleri oluşturur
  • Kodlama: Enum değerini doğrudan alana atayın
  • Kod çözme: Enum değerleri otomatik olarak çözülür
  • Varsayılan değerler: İlk enum değeri (genellikle 0) varsayılandır
  • Switch ifadeleri: Enum’lar switch ifadelerinde doğal olarak çalışır
  • Tip güvenliği: Enum’lar ham tamsayılara göre tip güvenliği sağlar
  • Bilinmeyen değerler: Geçersiz enum değerleri sayısal değerleri olarak çözülebilir

Enum’lar ne zaman kullanılır

  • Sabit bir olası değer kümesine sahip olduğunuzda
  • Durum veya mod alanları için tip güvenliği istediğinizde
  • Kendi kendini belgeleyen kod istediğinizde
  • Farklı çalışma modlarını ayırt etmeniz gerektiğinde
  • Mesajlara anlamsal anlam kodlamak istediğinizde

Beklenen çıktı

enums_expected_output.txt
Encoded 2 bytes
Encoded data: 08 01 
Decoded value:
  status: OK (enum value: 1)

Beklenen çıktı (switch örneği)

enums_switch_expected_output.txt
Handling status: UNKNOWN - Default state
Handling status: OK - Operation successful
Handling status: ERROR - Operation failed
Handling status: BUSY - System busy

C++‘dan farkları

C sürümü, C++ sürümüyle neredeyse aynıdır:

  • Enum’lar hem C hem de C++‘da aynı şekilde çalışır
  • C’de printf format belirteçleri için açık cast kullanın
  • Enum işleme için C++‘a özgü özellikler gerekmez
  • Oluşturulan kod her iki dil için de aynıdır

Diğer NanoPB yazıları


Check out similar posts by category: Gömülü C/C++ Protocol Buffers