コーヒー飲みながら仕事したい

仕事で使う技術的なことの備忘録とか

Visual Studio 2017 で protobuf-c を試してみる

前回の続き。

tassi-yuzukko.hatenablog.com

もろもろの準備がやっとできたので、 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 チュートリアルを実行してみる。

しかし以下を変更してやってみる。

結果、以下のようなファイルを用意した。

// 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言語でここまでできるのはかなり魅力的だなーと思いました。

ソースコード

一応、ソースコードを載せとく。