A mere hateblo

単なるはてブロです。

C#でMessagePackを使う

C++で書いたアプリとC#で書いたアプリの間でMessagePackを使ってデータをやりとりできないかな、とやってます。C++の方はなんとか使い方を覚えましたが、C#の方はC#自体ここ一ヶ月くらいしか経験がないせいもあって苦労してます。どこかに質問しようにも頭の中を整理しないと始まらないので、ここにメモします。

参考

C++でMessagePackを使う

まずは勝手知ったるC++から。ドキュメントやソースコードを読みながら使い方を覚えました。

#include <string>
#include <msgpack.hpp>

struct Foo
{
  std::string str;
  MSGPACK_DEFINE(str);
};

int main()
{
  Foo foo;
  foo.str = "Hello";

  msgpack::sbuffer sbuf;
  msgpack::pack(sbuf, foo);  // 91 a5 48 65 6c 6c 6f と変換される

  return 0;
}

ユーザ定義クラスはMSGPACK_DEFINEマクロで指定した変数が並んだ配列としてシリアライズされます。Rubyでいうところの["Hello"]と同等の状態ですね。MSGPACK_DEFINEマクロでpackとunpack両方の仕組みが自動的に定義されて便利です。

C#でMessagePackを使う(1)

最初はVisual Studio 2012のNuGet経由でインストールしたところ、これは二代目の実装だそうです。バージョンは0.1.0.2011042300。以下は説明のためのコードです。

using MsgPack;
// 中略
class Foo
{
  string str;
}
// 中略
Foo foo = new Foo { str = "Hello };
var packer = new CompiledPacker(true);
byte[] msg = packer.Pack<Foo>(foo); // 81 a3 73 74 72 a5 48 65 6c 6c 6f と変換される

ユーザ定義クラスのオブジェクトはmapとしてシリアライズされています。これはC++でのシリアライズ結果と異なるため、両者に互換性がないことになります。

C#でMessagePackを使う(2)

次に三代目の実装(MessagePack for CLI)を使います。バージョンは0.3-beta2。こちらはデフォルトで配列としてシリアライズされるそうなのでC++での結果と互換性がありそうです。

using System.IO;
using Msgpack.Serialization;
// 中略
// (Fooクラスは上記を参照)
MemoryStream stream = new MemoryStream;
var serializer = MessagePackSerializer.Create<Foo>(); // ここで例外 System.TypeLoadException が発生する
serializer.Pack(stream, foo);
stream.Position = 0;
byte[] msg = new byte[256];
stream.Read(buf, 0, (int)stream.Length);

一応上記のように実装はしてみましたが、例外System.TypeLoadExceptionが発生してしまいます。
というところで今日は時間切れです。