[C/C++]C++でMessagePackを試す。

バイナリデータの扱いが難しかった。
C++のMessagePackライブラリは自作クラスのシリアル化が可能ですので、あるクラスのシリアル化を考えます。

InterfaceClassの定義
    int cmd;     // 数値
    string guid; // 文字列
    string time; // 文字列
    char[] data; // バイト配列

変数dataにはバイナリ(1バイト型の配列)を保持させたいのですが、ちょっとよくわからなくて、C#のように簡単にシリアライズすることはなりませんでした。もうちょっと調べてみないとよくわかりませんー。

配列を作成しデータを作成。
以前、python版のMessagePackを使用した時のように、配列を作成した上で、配列内に様々なデータ型を挿入することにしました。

InterfarceClassのシリアライズ

    // 対象データ
    int cmd = 0x10;
    string guid = "f65a9f53-15dc-48f4-b0f2-eac3f96df93a";
    string time = "20130226192100";
    
    printf("pack   cmd[%d],guid[%s],time[%s]\n", cmd, guid.c_str(), time.c_str());
    
    vector<char> rData;
    ReadAllBytes(IMAGEFILE_A, rData);
    
    
    msgpack::sbuffer buffer;
    msgpack::packer<msgpack::sbuffer> pk(&buffer);
    
    pk.pack_array(4);           // データが4つ
    pk.pack(cmd);
    pk.pack(guid);
    pk.pack(time);
    pk.pack_raw(rData.size());  // バイナリを入れる処理
    pk.pack_raw_body(&rData[0], rData.size());

    WriteAllBytes(MSGPACKFILE, buffer.data(), buffer.size());

pk.pack_array(4)の処理がないと配列型として認識されません。これは重要で、クラスをシリアル化した結果と同じになります。ちなみに、配列はデータ型と認識されるので、最終的には1つのデータになります。

デシリアライズの処理。
シリアル化したデータから元のデータを取り出すのも手動でやってみます。MessagePackの実装部分のソースを調べないとわからない部分がたくさんあった。

InterfarceClassのデシリアライズ

    vector<char> packData;
    
    // MessagePackデータ読み取り
    ReadAllBytes(MSGPACKFILE, packData);
    
    msgpack::unpacked msg;
    msgpack::unpack(&msg, &packData[0], packData.size());
    
    msgpack::object obj = msg.get();
    
    // msgapck::objectの配列を取り出す
    msgpack::object_array obj_array = obj.via.array;
    
    // デシリアライズ対象データ
    int cmd;
    string guid, time;
    
    // それぞれの型に変換して取り出す
    (obj_array.ptr[0]).convert(&cmd);
    (obj_array.ptr[1]).convert(&guid);
    (obj_array.ptr[2]).convert(&time);
   
    printf("unpack cmd[%d],guid[%s],time[%s]\n", cmd, guid.c_str(), time.c_str());
    
    // バイナリの取り出し
    msgpack::object_raw obj_raw = (obj_array.ptr[3]).via.raw;
    
    // バイナリの書き出し
    WriteAllBytes(IMAGEFILE_B, obj_raw.ptr, obj_raw.size);

正直、やりかた合っているかわからないですが、C#で同様の処理を作成し、C++版でシリアル化したデータが無事デシリアライズできたので、たぶん大丈夫だと思います。
ちょっと残念なのは、例えばコマンドによって専用のpack,unpackの処理をコーディングしないといけないところでしょうか。ちゃんとやり方があるんだろうなぁ。。。

C#での例。
以前作成したObject2MessagePack()の拡張メソッドを使うと以下のようにかけます。

    public class MyInterface
    {
        public int _0CMD { get; set; }
        public string _1GUID { get; set; }
        public string _2TIME { get; set; }
        public byte[] _3DATA { get; set; }
    }
    static void Main(string[] args)
    {
        var m = new MyInterface();
           
        m._0CMD = 0x10;
        m._1GUID = "f65a9f53-15dc-48f4-b0f2-eac3f96df93a";
        m._2TIME = "20130226192100";
        m._3DATA = new byte[] { 1, 2, 3 };
     
        System.IO.File.WriteAllBytes("csharp_MsgPack.bin", m.Object2MessagePack());
    }

C++で同じデータ作成します。

void pack2()
{
    // 対象データ
    int cmd = 0x10;
    string guid = "f65a9f53-15dc-48f4-b0f2-eac3f96df93a";
    string time = "20130226192100";
    
    printf("pack   cmd[%d],guid[%s],time[%s]\n", cmd, guid.c_str(), time.c_str());
    
    char data[] = {1,2,3};
    
    msgpack::sbuffer buffer;
    msgpack::packer<msgpack::sbuffer> pk(&buffer);
    
    pk.pack_array(4);           // データが4つ
    pk.pack(cmd);
    pk.pack(guid);
    pk.pack(time);
    pk.pack_raw(sizeof(data));  // バイナリを入れる処理
    pk.pack_raw_body(&data[0], sizeof(data));
    
    printf("size =%d\n", sizeof(data));
    
    WriteAllBytes("cpp_MsgPack.bin", buffer.data(), buffer.size());
}

シリアライズしたデータをダンプしてみます。
Img20130228132404

テストソース
InterfaceClassに合うようにMessagePackデータを作成し、pack,unpackを試してみました。
バイト配列には画像データを読み込み、処理終了後、元画像ファイルとデシリアライズ後出力した画像ファイルのmd5ハッシュ値を表示させ同じデータであるかを確認します。

テストソース(msgpack_main.cpp)

#include <msgpack.hpp>
#include <vector>
#include <string>
#include <iostream>
#include <fstream>

using namespace std;

// ファイル読み込み
int ReadAllBytes(const string& path,vector<char>& buf)
{
	ifstream fin(path.c_str(), ios::in | ios::binary );
	
	if(!fin)
	{
		return 1;
	}
	
	char c;
	for(;;)
	{
		fin.read(&c,sizeof(char));
		
		if(!fin.eof()) buf.push_back(c);
		else break;
	}
	fin.close();
	
	
	return 0;
}

// ファイル書き込み
int WriteAllBytes(const string& path,const  char* buf, int size)
{
	ofstream ofs(path.c_str(), ios::out | ios::binary | ios::trunc );
	
	if(!ofs)
	{
		return 1;
	}
	
    ofs.write(buf,size);
	ofs.close();
	
	
	return 0;
}

// md5の表示
void DispMd5(const string& fileName)
{
    FILE *pipe;
    
    char buf[1024]={0};
    string cmd = "md5sum " + fileName;
    
    pipe = popen(cmd.c_str(), "r");
    if( pipe )
    {
        fread(buf, sizeof(char), sizeof(buf), pipe);
        pclose(pipe);
    }
    
    printf("MD5> %s",buf);
}



// MessagePackデータ
const char* MSGPACKFILE = "MsgPack.bin";
const char* IMAGEFILE_A = "icon.png";
const char* IMAGEFILE_B = "_icon.png";

// シリアライズ
void pack()
{
    // 対象データ
    int cmd = 0x10;
    string guid = "f65a9f53-15dc-48f4-b0f2-eac3f96df93a";
    string time = "20130226192100";
    
    printf("pack   cmd[%d],guid[%s],time[%s]\n", cmd, guid.c_str(), time.c_str());
    
    vector<char> rData;
    ReadAllBytes(IMAGEFILE_A, rData);
    
    
    msgpack::sbuffer buffer;
    msgpack::packer<msgpack::sbuffer> pk(&buffer);
    
    pk.pack_array(4);           // データが4つ
    pk.pack(cmd);
    pk.pack(guid);
    pk.pack(time);
    pk.pack_raw(rData.size());  // バイナリを入れる処理
    pk.pack_raw_body(&rData[0], rData.size());
    
    WriteAllBytes(MSGPACKFILE, buffer.data(), buffer.size());
}

// デシリアライズ
void unpack()
{
    vector<char> packData;
    
    // MessagePackデータ読み取り
    ReadAllBytes(MSGPACKFILE, packData);
    
    msgpack::unpacked msg;
    msgpack::unpack(&msg, &packData[0], packData.size());
    
    msgpack::object obj = msg.get();
    
    // msgapck::objectの配列を取り出す
    msgpack::object_array obj_array = obj.via.array;
    
    // デシリアライズ対象データ
    int cmd;
    string guid, time;
    
    // それぞれの型に変換して取り出す
    (obj_array.ptr[0]).convert(&cmd);
    (obj_array.ptr[1]).convert(&guid);
    (obj_array.ptr[2]).convert(&time);
   
    printf("unpack cmd[%d],guid[%s],time[%s]\n", cmd, guid.c_str(), time.c_str());
    
    // バイナリの取り出し
    msgpack::object_raw obj_raw = (obj_array.ptr[3]).via.raw;
    
    // バイナリの書き出し
    WriteAllBytes(IMAGEFILE_B, obj_raw.ptr, obj_raw.size);
}



// interfaces
    //  int cmd
    //  string guid
    //  string time
    //  char[] data
int main()
{
    pack();
    unpack();
    
    printf("\n");
    
    DispMd5(IMAGEFILE_A);
    DispMd5(IMAGEFILE_B);
    
    return 0;
}

実行結果

$ ./msgpack_main
pack   cmd[16],guid[f65a9f53-15dc-48f4-b0f2-eac3f96df93a],time[20130226192100]
unpack cmd[16],guid[f65a9f53-15dc-48f4-b0f2-eac3f96df93a],time[20130226192100]

MD5> 5b2eb4f3de00f5d7477faa850514f641  icon.png
MD5> 5b2eb4f3de00f5d7477faa850514f641  _icon.png

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA


This blog is kept spam free by WP-SpamFree.