Visual Studio 2017 で protobuf-c を試してみる
移転しました。
前回の続き。
もろもろの準備がやっとできたので、 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言語でここまでできるのはかなり魅力的だなーと思いました。
ソースコード
一応、ソースコードを載せとく。