My Daily Programming Life...

CppUnitでstd::exceptionのテストはできない?

現在ユニットテストにCppUnitを使っている。最近使い始めて、今日直面した問題。
あるメソッドがstd::exceptionを投げることを確認するテストをしたかった。
CPPUNIT_ASSERT_THROW( obj.foo() , std::exception );
が・・・
コンパイルエラー : error C2312(Visual Studio 2005)が出てしまう。
問題は、上記マクロがstd::expressionを用いて実装されていること。
std::expeptionのcatch部分が二つになってしまう。

http://www.nabble.com/CPPUNIT_ASSERT_THROW-double-handling-of-STL-std::exception-tc9497496.html
こんなのは見つけたけど、結局あまり良い解決策は見つけられなかった。

Visual Studioって・・・

たまたま見つけたこのやりとりががかなり笑えた。
http://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q1010463885?fr=rcmd_chie_detail

Binary Treeの実装

今日はアルゴリズム、データ構造の勉強。
BinaryTreeのInsert部分だけ作ってみた

#include <cstddef>

template<class T> class BinaryTree
{
template <class T> class Element{
public:
Element()
:parent(NULL),child_less(NULL),child_more(NULL)
{
};
~Element(){ 
delete child_less;
delete child_more;
};

Element* parent;
Element* child_less;
Element* child_more;
T  obj;
};
public:
BinaryTree(void)
:m_first(NULL)
{
}

~BinaryTree(void){
delete m_first;
};

int Insert(const T& obj){
Element<T> *el = new Element<T>();
el->obj = obj;

if( !m_first ){
m_first = el;
return 1;
}

return CheckAndInsertObject( m_first , el ); 
};

private:
Element<T>* m_first;

int CheckAndInsertObject( Element<T>* target_el, Element<T>* new_el ){
if( !target_el ){
return 0;
}

if( target_el->obj < new_el->obj ){
if( target_el->child_more ){
return CheckAndInsertObject( target_el->child_more , new_el );
}
else{
target_el->child_more = new_el;
new_el->parent = target_el;
return 1;
}

}
else if( target_el->obj > new_el->obj ){
if( target_el->child_less ){
return CheckAndInsertObject( target_el->child_less , new_el );
}
else{
target_el->child_less = new_el;
new_el->parent = target_el;
return 1;
}
}

delete new_el;
return 0;

};
};

XCodeで別ディレクトリのライブラリへリンク

普段はWindowsしか使わないけど、今日はMacでこの前作ったPersistentQueueをビルドするためにXCodeプロジェクトを作った。
構成はこう。
PersistentQueue本体と(static library)とPersistenteQueueTest(本体のテストプログラム)の二つがプロジェクトとしてある。
それぞれにDebugとRelease版がある。
PersistenteQueueはSQLiteのライブラリをリンクする(これもDebugとRelease)がある

ひとまず、PersistentQueueというXCodeプロジェクトを作った。
そこに本体のヘッダとソースを追加して、Debug版はPersistentQueued.a , Release版はPersistentQueue.aをビルドするように設定した。
出力先は$(MYPROJ_HOME)/lib と自分で設定した環境変数を利用し設定。
Debug版はSQLited.aを、ReleaseはSQLite.aをリンクするように設定。
これでここまではうまくいった。

問題はPersistentQueueTestのほう。もともとVisualStudioで作っていたもので、そこではPersistentQueueソリューションの中に、両プロジェクトが入る形で構成されていた。
んじゃ、同じように、このPersistentQueueというXCodeプロジェクトに両方入れられるんじゃないかと言うことで、やってみた。

XCodeプロジェクトにはターゲットという物がある。これがビルドプロセスの一つの単位になっているっぽい。PersistentQueueライブラリ用にひとつ、PersistentQueueTestプログラム用に一つのターゲットをつくればよさそう。ということで作った。問題ない。

さて、このPersistentQueueTestは、先ほど出力したPersistentQueue.a ( $(MYPROJ_HOME)/lib内 )をリンクしたい。もちろんデバッグ版とリリース版分けて。

どうやったらいいのだろうかと思って、プロジェクト設定やターゲット設定をいじってみたが、これがうまくいかない。
いちばんそれっぽい方法として、PersistentQueueターゲットの出力であるPersistentQueue.aがプロジェクト管理ウィンドウの「Products」フォルダに表示されていたので、それをドラッグして、リンクプロセスに入れてみた。一応自動的にデバッグ版とリリース版でリンクするライブラリ名が変わるようになった。

でもさらに問題。なぜかリンクプロセスが失敗する。PersistentQueuTestがそのライブラリを自分のビルドプロダクト出力フォルダから探そうとする。なぜだ・・・。ライブラリ検索パスとかしていしたし、そもそもさっきドラッグしてリンクプロセスに追加したライブラリはは$(MYPROJ_HOME)/libにあって、その情報を見てもそうかかれているのに・・・。

色々調べたら、ターゲット設定の「構成別のビルドプロダクトパス」みたいなやつの中を探すらしい。これがPersistentQueueとPersistentQueueTestでまったく異なるフォルダを示しているとうまくいかない。
でも、やりたいことはまさに、この異なるフォルダにそれぞれの生成物を出力したいのですが・・・。

どうすりゃいいんでしょうか。

まあ、まだ今日が初日だし、いろいろ試してみよう

キーボードを使いやすく

最近どうも右手、右腕が変。なんか気持ち悪い。なんか常に違和感がある。痛い訳じゃないしかゆいわけでもないんだけど。
おそらくマウスの使いすぎなんだと思う。右肩も痛い。プログラマーなんだから、もっとキーボードで操作出来るようになればいいんだと思って、勉強中。エディタももっときちんと使えるように練習しなくちゃ。

今回は、まずWindowsでCapsとCtrlの入れ替え。
http://uguisu.skr.jp/Windows/winCaps.html
を見てやった。
Controlは左下にあるんだけど、それがちょっと使いにくいと感じて替えた。それに慣れてるからまだ間違えるけど、あきらかにCapsよりCtrlの方が使う頻度が高いのに、打ちにくい場所にあってもしょうがないし。

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layout]
"Scancode Map"=hex:00,00,00,00,00,00,00,00,06,00,00,00,3a,00,1d,00,1d,00,3a,00,\
29,00,79,00,5b,e0,7b,00,38,e0,70,00,00,00,00,00

これをテキストに保存して.regという拡張子を付ければ、僕の設定になる。
Ctrl ←→ Caps Lock
変換(スペースの右) → 全角/半角
無変換 → Windows Key
かな → Alt
に変換しているはず。

あとはキーボードショートカット
http://qune.cside.com/archives/000515.html
http://homepage1.nifty.com/keyboard_maniac/SpecialKT.htm
のとか。

エクスプローラの拡張とか、もっと使いやすいファイラとか誰か知ってたら教えてください。

2009/3/20 追記
各ユーザー別にキーボード設定したい場合はHKEY_CURRENT_USER\Keyboard Layout にScancode mapを作ればよいらしい

PersistentQueue

仕事でQueueをファイル上作る必要があってPersistentQueueクラス作った。
PopやPush、Frontとかのインターフェースをくっつけて、
インプリメンテーションはSQLiteを用いて行った。

Pushはバイト列をもらって、blob型で入れるだけ。Popはその逆の順番で出せばいい。でも、その順番を保障するために、idフィールドを入れておいた。auto_incrementになるので、いつも一番小さい値のやつを出せばいい。idは64bitもあるから、まあ、まず使い切ることはない。

このQueueには少し特別な仕様が要求されていて、あるファイルサイズを超えない範囲で追加可能というものが必要になっている。
1Byte単位で正確に指定されたファイルサイズを超えない必要はなかったので、SQLiteのPragma max_page_countを利用してサイズを制限することにした。

擬似コードはこんな感じ。
SetMaxSize( int size ){
sql = "pragma page_size";
int pagesize = GetValue( sql ); //SQLiteのページサイズ取得
int pagecount = size / pagesize;
sql = "pramga max_page_count=" + pagecount;
ExecQuery(sql); // max_page_countを設定
}

ちなみに、pagecountに0が入ると、サイズは制限されないっぽかった。

SetMaxSizeに指定されるものよりすでにファイルサイズが大きくなってしまっている場合には、それ以下にはmax_page_countの値は下がらない。でもそれからはPushできなくなるので今回の使用にはちょう
どよかった。

ただ、このままだと、Popしたときにファイルサイズがもとにもどらない。もともとSQLiteの使うファイルはレコードをdeleteしてもファイルサイズは勝手には減らない。それをするためにVACUUMというSQLを発行することが出来て、こうするとファイルのデフラグが行われる。
・・・ただ、これがかなり遅い。それなりにパフォーマンスが要求されたので、毎回PopしたときにVACUUMする訳にはいかない。
というわけで、とりあえずPop 100回に1回の割合でVACUUMを行うことにした。
ここら辺は要求される仕様と相談ですね。今回はこれでよさそう。クラスにはその値を設定できるようにはした。

SQLiteにはincremental_vacuumというのがあって、今回はそっちを利用した。
"pragma auto_vacuum = 2"として、"pragma incremental_vacuum"を実行すればいい。
feedSubscribe to my feed