前回の続き。
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言語でここまでできるのはかなり魅力的だなーと思いました。
ソースコード
一応、ソースコードを載せとく。
コメント