A mere hateblo

単なるはてブロです。

Google C++ Testing FrameworkをVisual Studio Express 2012 for Windows Desktopでビルドした

ちょっと必要になったのでGoogle C++ Testing Framework(以下gtest)をVisual Studio Express 2012 for Windows Desktop(以下msvc11)でビルドしました。といっても、やったのはソリューションファイルとプロジェクトファイルを用意しただけ。差分はgistにあげました。

https://gist.github.com/iwadon/4722737 *1

gtest-1.6.0を展開したディレクトリからpatchするとmsvc11ディレクトリが出来上がるので、その中のソリューションファイルを使ってビルドします。

*1:当初はgistを貼り付けてましたが、縦にやたら長くなってしまったのでリンクのみにしました。

C#でMessagePackを使う(その後)

先日の記事(C#でMessagePackを使う - A mere hateblo)の続き。

その後アレコレ調べたり試しましたが自分の理解や経験ではなんともならず、MessagePack互換のクラスを自作しました。実のところ、相手側のC++の方も配布されているソースコードをそのままコンパイル出来なかった*1ので、こちらもすでに書き下ろしてます。徐々にunpackするとか、一旦オブジェクトに入れてから好きな値だけ拾うとか、ゼロコピーとかはできませんが、とりあえずC++でもC#でもシリアライズ・デシリアライズ共に意図通りに動いている感じです(今のところは)。

*1:ソースコードが悪いのではなくて、そのままは使えないような特殊な環境ということで。

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が発生してしまいます。
というところで今日は時間切れです。

ケアレスミス

どうしても直せない不具合があった。ifの条件が真ではないことが明らかなのに真とみなされてしまう。かなり頭を抱えたが、よくよくソースを見たらなんてことはない(以下は適当な再現コード)。

bool ret = true;
if (!ret); // ← ここに謎のセミコロンがある
{
}

なんだよこのセミコロンw if文の意味ねー…orz 結局数時間悩んだ問題はセミコロンを一つ削除することで対処できた。

ちなみにこれ、別ラインの同じソースでも同じ原因で同じように悩んで同じように修正してるはずなんだよね。その場でこちらのソースも修正すれば良かった。過去の俺もっと頑張れよ(無茶振り)。

ODSファイル内のシートのデータが大きくなりすぎた時の対処方法

仕事のデータを作成するのにLibreOffice Calcを利用しています。具体的には数値や文字列などCalcで入力してODSファイルとして保存、ODSファイルの内容をRubyから読み取ってバイナリファイルやC言語のヘッダファイルを作成しています。普段はとくに問題ありませんが、稀にODSファイルに保存したシートのデータが馬鹿でかくなるせいでRubyからデータを読み取る際にえらく時間がかかることがあります。で、問題は解決していませんが、それなりな対処方法がわかったのでココにメモしておきます。

以前ODSファイルに含まれるcontent.xmlの内容を調べたところ、問題発生時にはなぜか1行に含まれるセルの数が実際のデータよりもはるかに多い状態になり、それが全行数で起きているようでした。そのため、延々と空のセルを読むという無駄な処理ばかりしていました。一番大きなシートはだいたい8000行x15列くらいのもので、Ruby 1.9.3-p125とnokogiriを使ってだいたい50秒弱で処理できる規模のものですが、問題が起きると数倍から数十倍時間がかかるようになります。もうこうなると仕事になりません。

問題が発生するキッカケがわからないため、バックアップ目的でODSファイルをこまめにSubversionリポジトリへcommitし、問題が起きたらrevertするという対処を続けてきました。これはこれで問題には対処できますが、それまで入力したものとかが無駄になりがちでした。しかしとうとう、現状は問題なくとも、少しでもセルの内容を変更してODSファイルへ保存するだけで上記問題が発生するようになってしまいました。

今日になってさらに調べたところ、この問題はシート単位で起こることがわかりました。そこでシートを新規作成し、問題のシートの内容を必要なところだけ新規シートへ移し、問題のシートを削除する、という対処をとったところ、上記問題を解消できました。ただし、問題のあるシートを対処したところ別の問題のなかったシートに問題が発生することがありましたので、結局すべてのシートで問題が起きなくなるまで対処を続けました。全10シートのうち3シート対処しましたが、シート数が少ないからいいものを、シート数が多くなると頭抱えそうです。

問題の再現方法がわかればLibreOffice(OpenOfficeも多分同様)へ報告したいところですが、突然起きるのでなんともできないのが歯がゆいです。同じような問題を抱えている人もググった限りでは見つかりませんでしたし、Calc上で編集する分にはなんら問題のないことなので、余計に報告しづらいというか、なんというか。