Entries

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

「ビルド後のイベント」でコードカバレッジを計測するvsprops

タグ: VS2005 C++

大分昔に作ったVisual Studio 2005 Team Edition用プロパティシートを久しぶりに使った記念メモ。

<?xml version="1.0" encoding="shift_jis"?>
<VisualStudioPropertySheet
	ProjectType="Visual C++"
	Version="8.00"
	Name="Code Coverage"
	DeleteExtensionsOnClean="*.pdb;*.coverage"
	>
	<Tool
		Name="VCLinkerTool"
		GenerateDebugInformation="true"
		Profile="true"
	/>
	<Tool
		Name="VCNMakeTool"
		BuildCommandLine=""
		ReBuildCommandLine=""
		CleanCommandLine=""
		Output=""
		PreprocessorDefinitions=""
		IncludeSearchPath=""
		ForcedIncludes=""
		AssemblySearchPath=""
		ForcedUsingAssemblies=""
		CompileAsManaged=""
	/>
	<Tool
		Name="VCPostBuildEventTool"
		Description="コードカバレッジ情報を収集しています..."
		CommandLine="set UNIQUECOVERAGEPATH=$(OutDir)\$(ConfigurationName) %DATE:/=.% %TIME::=.%.coverage&#x0D;&#x0A;&quot;$(VSInstallDir)\Team Tools\Performance Tools\vsinstr&quot; /coverage $(Excludes) &quot;$(TargetPath)&quot;&#x0D;&#x0A;&quot;$(VSInstallDir)\Team Tools\Performance Tools\vsperfcmd&quot; /start:coverage /output:&quot;$(CoveragePath)&quot;&#x0D;&#x0A;&quot;$(TargetPath)&quot;&#x0D;&#x0A;&quot;$(VSInstallDir)\Team Tools\Performance Tools\vsperfcmd&quot; /shutdown&#x0D;&#x0A;cp &quot;$(CoveragePath)&quot; &quot;%UNIQUECOVERAGEPATH%&quot;&#x0D;&#x0A;&quot;%UNIQUECOVERAGEPATH%&quot;&#x0D;&#x0A;"
	/>
	<UserMacro
		Name="Excludes"
		Value="/exclude:std::* /exclude:stdext::* /exclude:boost::* /exclude:ATL::* /exclude:CppUnit::*"
	/>
	<UserMacro
		Name="CoveragePath"
		Value="$(OutDir)\$(ConfigurationName).coverage"
	/>
</VisualStudioPropertySheet>
  1. ↑を「CodeCoverage.vsprops」というファイル名で保存しておく
  2. プロジェクトを開く
  3. 構成マネージャでDebug構成をコピーしてCoverage構成を作る
  4. プロパティマネージャでCoverage構成に「CodeCoverage.vsprops」を加える
  5. Coverage構成をビルドする
  6. あとは「ビルド後のイベント」が勝手に成果物を起動してコードカバレッジを計測し、結果のレポートファイルを開いてくれる

コードカバレッジ

このプロパティシートはもともと「ATLやBoostを使ったライブラリをテストするCppUnitベースのユニットテスト」のコードカバレッジを計測するために作ったものなので、バイナリのインストルメント時に

/exclude:std::* /exclude:stdext::* /exclude:boost::* /exclude:ATL::* /exclude:CppUnit::*

として余計なものまで計測に含まれないようにしている。

この結果をHudson辺りで集約できれば素敵なのだが……

プロパティシート作成時に参考にしたページ:

C++において基本的かつ重要と思う機能をつらつらと

タグ: C++

C言語に関する知識は最低限あるものと考えて……

C++スタイルキャスト

Cスタイルキャストは「暗黙の型変換の規則を無視する」機能。
C++スタイルキャストは「特定の規則に従った型変換であることをコンパイラに保証してもらう」機能。
Cスタイルキャストはバグを隠すが、C++スタイルキャストは不自然なキャストを明らかにする

言うまでもないが、キャストしなくて済む(=警告も出ない)ならそれに越したことはない。

デストラクタ

C++の肝となる機能。
C++を使いこなそうと思ったら、「delete等の解放処理を明記しなくとも、デストラクタのみで正しくリソースの解放を行えるようにする」ことを目指そう。
C言語ではmallocとfreeが1:1になっているコードが見やすいが、C++ではnewとdeleteが1:0になっているコードが見やすい

スマートポインタ

「newとdeleteを1:0にする」ためのクラス。
代表的なのは

  • std::auto_ptr
  • boost::shared_ptr ( std::tr1::shared_ptr )
  • boost::scoped_ptr

……「代表的」というか、「私がよく使う」というか。

const参照型

主な用途は「値渡しの代替」と「(一時変数を束縛する性質を利用した)関数戻り値の受け皿」。
参照については別エントリがあるのでそちらを参照。

constメンバ関数

「関数内でデータメンバに変更を加えていないことをコンパイラに保証してもらう」機能。

データメンバに変更を加えないメンバ関数は、なるべくconstメンバ関数にすべきである。メンバ関数のconst化を怠っているクラスは「非const汚染」を引き起こす。
constメンバ関数の中ではデータメンバの非constメンバ関数を呼ぶこともできない。そのため「constを持つクラス」をデータメンバにすると、そのデータメンバのメンバ関数を呼ぶメンバ関数は全て非constにするか、データメンバをconst_castするか、データメンバをmutableにしなければならなくなる。

例外

C++標準のnewやSTLなどはメモリの確保に失敗したら例外を返す。
そのため例外を知らないとメモリ回りの異常系すら実装できない(newはNULLを返すようもできるが)。
例外を使う上では例外安全性(exception safety)が問題になるが、日頃から「デストラクタのみで正しくリソースの解放を行える」ようにしていれば、基本例外安全(basic exception safety)くらいは容易に保証できるだろう。
逆にデストラクタをうまく使えないと、finallyのないC++では非常に泥臭いコードを書かなければならなくなる。

コンストラクタ

「無効なオブジェクトが作られるのを(可能ならコンパイル段階で)阻止する」機能。

コピーコンストラクタ、代入演算子オーバーロード

主な用途は「両者のアクセス指定をprivateにしてオブジェクトのコピーを不可にする」こと。また、ポインタ型のデータメンバを「浅いコピー」したり「深いコピー」したりすること。
コピーコンストラクタや代入演算子オーバーロードは、できれば書かなくて済むように(デフォルトのコピーコンストラクタと代入演算子で破綻なくコピーできるように)することが望ましい。

仮想関数テーブル

C++の規格で規定されているわけではないが、仮想関数を持つクラスは「仮想関数テーブル」という関数ポインタの配列のようなものへのポインタをインスタンスに持たせて、どの関数を呼べば良いかが分かるようにしていると考えてまず間違いない。
これの存在を認識することで、仮想関数を持つクラスのサイズは仮想関数を持たないクラスよりポインタ1個分以上大きくなるとか、デバッグ実行時にどのクラスのインスタンスであるか、そもそもオブジェクトが壊れていないかとかいったことが分かるようになる。

テンプレート

C++の目玉機能。
「型が違うだけで処理の流れ自体は同じクラスや関数」をコピペ(保守性が低くなる上に修正漏れが残りやすい)やマクロ(デバッグしにくい)より扱いやすい形で記すことができる。
この機能に習熟し、ある程度の域まで達すると、テンプレートメタプログラミングという世界が開ける。

timeSetEventとクリティカルセクション

タグ: WindowsCE C++
#include <windows.h>
#include <stdio.h>

CRITICAL_SECTION cs;

void CALLBACK TimerProc(UINT /*uTimerID*/, UINT /*uMsg*/, DWORD dwUser, DWORD /*dw1*/, DWORD /*dw2*/)
{
    ::wprintf(L" %d will lock...\n", dwUser);
    ::EnterCriticalSection(&cs);
    ::wprintf(L" %d locked\n", dwUser);
    ::wprintf(L" %d unlock\n", dwUser);
    ::LeaveCriticalSection(&cs);
}

#ifdef WINCE
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
#else
int main()
#endif // WINCE
{
    ::InitializeCriticalSection(&cs);
    for(int i = 0; i < 3; i++)
    {
        ::wprintf(L"%d will lock...\n", i);
        ::EnterCriticalSection(&cs);
        ::wprintf(L"%d locked\n", i);
        ::Sleep(300);
        ::timeSetEvent(200, 1, TimerProc, i, TIME_ONESHOT);
        ::wprintf(L"%d unlock\n", i);
        ::LeaveCriticalSection(&cs);
    }
    ::Sleep(10000);
    ::DeleteCriticalSection(&cs);
    return 0;
}

このコード、Windows XP SP2では問題なく動作するが、

0 will lock...
0 locked
0 unlock
1 will lock...
1 locked
 0 will lock...
1 unlock
 0 locked
 0 unlock
2 will lock...
2 locked
 1 will lock...
2 unlock
 1 locked
 1 unlock
 2 will lock...
 2 locked
 2 unlock

Windows CE 5.0で動かすとデッドロックする。

0 will lock...
0 locked
0 unlock
1 will lock...
1 locked
 0 will lock...

固まり方を見るに、どうやらCE 5.0では「TimerProc呼出し」と「timeSetEvent呼出し」は排他関係にあるらしい。

waveOut*** 系の罠:Code & Note

「コールバック関数内からこういった関数を呼ぶな」、ということは、

「中で排他制御してるから、それを考慮してやれよ」ということなのだ。

むぅ……「『EnterCriticalSectionは使える』と書いてあるから遠慮無く使わせてもらおう」と短絡的に考えるのは浅はかである、と。
奥が深い。

でももうちょっと分かりやすく書いてほしいよMSDN(´・ω・`)

Appendix

タグ

Blog内検索

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。