前回の続き。

protobuf-c で今度こそ遊んでみる(proto2とproto3の違い編)
あらすじもともと軽い気持ちで Protocol Buffers のC言語版を遊んでみようと思っていたら、意図せず Docker を巻き込んだ一大イベントに発展した。やっとこさ準備ができたので、楽しく遊ぼうとしていた。が、しかし・・・p...
もろもろの準備がやっとできたので、 Visual Studio 2017 で protobuf-c を試してみる。
前準備
あらかじめ下記内容で amessage.proto を用意しておく(前回生成済み)
syntax = "proto3";
message AMessage {
  int32 a=1; 
  int32 b=2;
}そして、protoc-c --c_out=. amessage.proto で、以下のファイルを生成しておく(これも前回実施済み)
- amessage.pb-c.c
- amessage.pb-c.h
ソリューションの作成
上記のファイル2つと、 protobuf-c.c および protobuf-c.h をソリューションフォルダ内につっこむ。
この際、便宜上、以下を変更した。
amessage.pb-c.h
7:  --- #include <protobuf-c/protobuf-c.h>
7:  +++ #include "protobuf-c.h"    // protobuf-c.hをローカルに置いたため & <>の括弧ではアクセスできないためprotobuf-c.c
316:  --- return (-(uint32_t)v) * 2 - 1;
316:  +++ return ((uint32_t)(-v)) * 2 - 1;    // Visual Studio コンパイル時にエラーとなったため(符号なし型にマイナスをつけることができないみたい)
381:  --- return (-(uint64_t)v) * 2 - 1;
381:  +++ return ((uint64_t)(-v)) * 2 - 1;    // Visual Studio コンパイル時にエラーとなったため(符号なし型にマイナスをつけることができないみたい)
2413:  --- return -(v >> 1) - 1;
2413:  +++ return -1 * (v >> 1) - 1;    // Visual Studio コンパイル時にエラーとなったため(符号なし型にマイナスをつけることができないみたい)
2457:  --- return -(v >> 1) - 1;
2457:  +++ return -1 * (v >> 1) - 1;    // Visual Studio コンパイル時にエラーとなったため(符号なし型にマイナスをつけることができないみたい)
3147:  --- tmp.length_prefix_len = pref_len;
3147:  +++ tmp.length_prefix_len = (uint8_t)pref_len;    // 警告が出たので一応注意
Visual Studio の環境では、これらのファイルをコンパイル対象とするために、明示的にソリューション(というかプロジェクト)構成配下に登録する必要がある。
Simple complete example チュートリアル実行
基本的に、Examples · protobuf-c/protobuf-c Wiki · GitHub を参照して、 protobuf-c の Simple complete example チュートリアルを実行してみる。
しかし以下を変更してやってみる。
- Windows の環境用にカスタマイズ
- ファイルにシリアライズ → ファイルからデシリアライズしてみる
- protobuf ver3.XX 用にカスタマイズ
結果、以下のようなファイルを用意した。
// AMessageSerialize.h
#pragma once
#include <windows.h>
BOOL AMessageSerialize(int argc, const char * argv[]);// AMessageSerialize.c
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include "amessage.pb-c.h"
#include "AMessageSerialize.h"
static void MyWriteFile(void const * buf, size_t len, const char* filename);
BOOL AMessageSerialize(int argc, const char * argv[])
{
    AMessage msg = AMESSAGE__INIT; // AMessage
    void *buf;                     // Buffer to store serialized data
    unsigned len;                  // Length of serialized data
    if (argc != 2 && argc != 3)
    {   // Allow one or two integers
        fprintf(stderr, "usage: amessage a [b]\n");
        return FALSE;
    }
    msg.a = atoi(argv[1]);
    msg.b = atoi(argv[2]);
    len = amessage__get_packed_size(&msg);
    buf = malloc(len);
    amessage__pack(&msg, buf);
    fprintf(stderr, "Writing %d serialized bytes\n", len); // See the length of message
    MyWriteFile(buf, len, "test.txt"); // Write to stdout to allow direct command line piping
    free(buf); // Free the allocated serialized buffer
    return TRUE;
}
static void MyWriteFile(void const * buf, size_t len, const char* filename)
{
    FILE* fp;
    fopen_s(&fp, filename, "wb");
    if (fp == NULL) {
        fprintf(stderr, "failed to write file.");
        goto END;
    }
    fwrite(buf, len, 1, fp);
END:
    fclose(fp);
    return;
}// AMessageDeserialize.h
#pragma once
#include <windows.h>
BOOL AMessageDeserialize();// AMessageDeserialize.c
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include "amessage.pb-c.h"
#include "AMessageDeserialize.h"
#define MAX_MSG_SIZE 1024
static size_t
read_buffer(unsigned max_length, uint8_t *out, const char* filename)
{
    size_t cur_len = 0;
    size_t nread;
    FILE* fp;
    fopen_s(&fp, filename, "rb");
    if (fp == NULL) {
        fprintf(stderr, "failed to read file.");
        goto END;
    }
    while ((nread = fread(out + cur_len, 1, max_length - cur_len, fp)) != 0)
    {
        cur_len += nread;
        if (cur_len == max_length)
        {
            fprintf(stderr, "max message length exceeded\n");
            exit(1);
        }
    }
END:
    fclose(fp);
    return cur_len;
}
BOOL AMessageDeserialize()
{
    AMessage *msg;
    // Read packed message from standard-input.
    uint8_t buf[MAX_MSG_SIZE];
    size_t msg_len = read_buffer(MAX_MSG_SIZE, buf, "test.txt");
    // Unpack the message using protobuf-c.
    msg = amessage__unpack(NULL, msg_len, buf);
    if (msg == NULL)
    {
        fprintf(stderr, "error unpacking incoming message\n");
        return FALSE;
    }
    // display the message's fields.
    printf("Received: a=%d  b=%d \n", msg->a, msg->b);  // required field
    // Free the unpacked message
    amessage__free_unpacked(msg, NULL);
    return TRUE;
}// main.c
#pragma once
#include <stdio.h>
#include "AMessageSerialize.h"
#include "AMessageDeserialize.h"
int main(int argc, const char * argv[])
{
    AMessageSerialize(argc, argv);
    AMessageDeserialize();
    system("pause");
    return 0;
}実行結果
引数を 10 2 で実行してみると、以下になった。
Writing 4 serialized bytes
Received: a=10  b=2
続行するには何かキーを押してください . . .んーたぶんできてるっぽい。
とりあえず目的達成。シリアライズとデシリアライズの方法も直感的だし、C言語でここまでできるのはかなり魅力的だなーと思いました。
ソースコード
一応、ソースコードを載せとく。
 
  
  
  
  
コメント