Archive for category C

Visual CのGUIアプリで「出力」ウィンドウへデバッグメッセージを出す

俺、PHPerの頃からの癖で、デバッグメッセージをどうしても出したくなる。
コマンドライン上では普通にprintfではき出せば良いんだけど、GUIとなるとそこに出すのもちょいと面倒だし、メッセージボックスなんてやった日には、メッセージボックスの嵐となる可能性さえ秘めているのは自明。
で、Visual Studioでデバッグすると出力ウィンドウがあります、と。
これを使いたい。
答えを書いちゃうとwindows.hにある、OutputDebugStringA( *str )と言う関数で実現可能です、と。
と言うわけで、dprintfを自作。

// debug.h
bool dprintf( const char *str, ...);
// debug.cpp
#include <windows.h>
#include <stdio.h>
#include <stdarg.h>
#define _DEBUG_OUT_BUFF_SIZE_ 128
bool dprintf( const char *str, ...)
{
char debugOutBuff[ _DEBUG_OUT_BUFF_SIZE_ ];
va_list ap;
va_start( ap, str );
if ( !vsprintf_s( debugOutBuff, _DEBUG_OUT_BUFF_SIZE_, str, ap ) ) {
OutputDebugStringA( "dprintf error." );
return false;
}
OutputDebugStringA( debugOutBuff );
return true;
}

dprintfで「出力」ウィンドウへ出力したところ

まぁ、クソみたいな関数だけど、一応メモ。
使い方はまぁ、printfと同じだと思ってもらえれば。
あと、OutputDebugStringではなく、何故OutputDebugStringAを使っているかというと、「出力」ウィンドウがShift_JISだったからと言う。。。
少なくとも俺の持ってるVisual Studio 2008 Professionalはそうなってた。
OutputDebugStringを指定しておくとプロジェクトがUnicodeを使うように指定されていると、コンパイル時にOutputDebugStringWと言うUnicode版に書き換わってしまって都合が悪いかなぁ、と。
VC2010の「出力」ウィンドウがUnicodeになってるんだったらifdef使うなり拡張はするかも。

そんだけ。

んが、「続・dprintf」に続く。

アマゾンのサーバでエラーが起こっているかもしれません。
一度ページを再読み込みしてみてください。


プログラミングWindows第5版〈上〉Win32 APIを扱う開発者のための決定版! (Microsoft Programming Series)

著者/訳者:チャールズ ペゾルド

出版社:アスキー( 2000-10 )

定価:

単行本 ( 790 ページ )

ISBN-10 : 4756136001

ISBN-13 : 9784756136008



アマゾンのサーバでエラーが起こっているかもしれません。
一度ページを再読み込みしてみてください。

Post to Twitter

, , , , ,

No Comments

メモリのアラインメントを確認する

そもそもの話。
なぜ「ゲームプログラマになる前に覚えておきたい技術(以下「セガ本」)」を読んでる途中で「C言語ポインタ完全制覇(以下「ポインタ本」)」を読むことになったか?
それは、セガ本にデータ型のサイズで割りきれるアドレスでデータを読み込まないと、x86系(いわゆるIntel、AMD系)以外のCPUではクラッシュし、x86でもデータを2回読むことになってパフォーマンスが落ちる、と書いてあったから。
で、パッと見て理解できないでいたら、時を同じくしてポインタ本が届き、目次にアラインメントの記述が。
と言うわけで、ポインタ本を読み進めてみた次第。

そこで思ったのが、この本、本当に為になる。
俺みたいなCの見習いの肩もみの家の便所掃除みたいな人間にはいろいろ発見が多すぎて驚く。
例えば

#include <stdio.h>
int main()
{
int i;
char s[] = "abcdefghij";
for ( i = 0; i < sizeof( s ) - 1; ++i ) {
printf( "%d:%c\n", i, i[s] );
}
}

なんてコード。
実行するとこう。

McLaren% gcc -o pointer_test pointer_test.c
McLaren% ./pointer_test
0:a
1:b
2:c
3:d
4:e
5:f
6:g
7:h
8:i
9:j

9行目のi[s]はs[i]の間違いと思いがちだけど、”これでも”問題ない。
出来るか出来ないかの話であって、常識的にこの書き方をして良いかは別の話(そしてもちろんダメ)。
この表記はコンパイル時に

printf( "%d:%c\n", i, *( i + s ) );

と変換しているだけ。
確かにポインタ+数値でそのポインタの型の分だけ先へ進む機能がある。
とまぁ、なかなか興味深い。

さてさて本題。
ようやくアラインメントが書いてあるページまでたどり着いた。
どうやら現代のコンパイラはかなりその辺りも配慮しているらしく、次に置かれるデータがそのデータ型で割り切れない場合、”詰め物”をしてくれるんだとか。
実際コードにして試してみまふ。

// alignment_check.c
#include <stdio.h>
typedef struct {
char ch;
double dn;
short sn;
int nu;
} MyStruct;
int main()
{
MyStruct hoge;
unsigned long gap1, gap2, gap3;
printf( "sizeof char\t%lu\n", sizeof( char ) );
printf( "sizeof double\t%lu\n", sizeof( double ) );
printf( "sizeof short\t%lu\n", sizeof( short ) );
printf( "sizeof int\t%lu\n\n", sizeof( int ) );
printf( "sizeof MyStruct\t%lu\n\n", sizeof( MyStruct ) );
printf( "\t&hoge's hexadecimal memory address.\n" );
printf( "\tname\t\ttype\t\taddress\t\tsize\n" );
printf( "\t&hoge\t\tMyStruct\t%p\t%4lu\n", &hoge, sizeof( hoge ) );
printf( "\t&hoge.ch\tchar\t\t%p\t%4lu\n", &hoge.ch, sizeof( hoge.ch ) );
gap1 = (unsigned long)&hoge.dn -
( (unsigned long)&hoge.ch + sizeof( hoge.ch ) );
printf( "gap1:%lu\n", gap1 );
printf( "\t&hoge.dn\tdouble\t\t%p\t%4lu\n", &hoge.dn, sizeof( hoge.dn ) );
gap2 = (unsigned long)&hoge.sn -
( (unsigned long)&hoge.dn + sizeof( hoge.dn ) );
printf( "gap2:%lu\n", gap2 );
printf( "\t&hoge.sn\tshort\t\t%p\t%4lu\n", &hoge.sn, sizeof( hoge.sn ) );
gap3 = (unsigned long)&hoge.nu -
( (unsigned long)&hoge.sn + sizeof( hoge.sn ) );
printf( "gap3:%lu\n", gap3 );
printf( "\t&hoge.nu\tint\t\t%p\t%4lu\n\n", &hoge.nu, sizeof( hoge.nu ) );
printf( "char + gap1 + double + gap2 + short + gap3 + int = %lu\n\n",
sizeof( char ) + gap1 +
sizeof( double ) + gap2 +
sizeof( short ) + gap3 +
sizeof( int ) );
printf( "&hoge's decimal memory address (with alighnment gap size).\n" );
printf( "\tname\t\ttype\t\taddress\t\tsize\n" );
printf( "\t&hoge\t\tMyStruct\t%ll\t%4lu\n", &hoge, sizeof( hoge ) );
printf( "\t&hoge.ch\tchar\t\t%lu\t%4lu\n", &hoge.ch, sizeof( hoge.ch ) );
printf( "gap1:%lu\n", gap1 );
printf( "\t&hoge.dn\tdouble\t\t%lu\t%4lu\n", &hoge.dn, sizeof( hoge.dn ) );
printf( "gap2:%lu\n", gap2 );
printf( "\t&hoge.sn\tshort\t\t%lu\t%4lu\n", &hoge.sn, sizeof( hoge.sn ) );
printf( "gap3:%lu\n", gap3 );
printf( "\t&hoge.nu\tint\t\t%lu\t%4lu\n", &hoge.nu, sizeof( hoge.nu ) );
return 0;
}

まぁ、こんだけ親切に情報盛ったから、アドレス表記だけで良いとは思ったけど、一応アドレスを10進数にしたやつも記載。
(つっても、64ビット環境においてunsigned long程度でアドレス表示して間に合わない気がするけど、ギャップを目視することが目的だから、あえてこのまま。
あれ?ラップトップのMacでgcc動かしたときって64ビット扱い?
・・・ま、今回は気にしないことに。
どうせテストだし。。。
まともに64ビットアドレス書いてたら、京単位らしいから・・・ね。)

で、結果。

McLaren% gcc -o alignment_check alignment_check.c
alignment_check.c: In function ‘main’:
alignment_check.c:48: warning: format ‘%lu’ expects type ‘long unsigned int’, but argument 2 has type ‘struct MyStruct *’
alignment_check.c:49: warning: format ‘%lu’ expects type ‘long unsigned int’, but argument 2 has type ‘char *’
alignment_check.c:52: warning: format ‘%lu’ expects type ‘long unsigned int’, but argument 2 has type ‘double *’
alignment_check.c:55: warning: format ‘%lu’ expects type ‘long unsigned int’, but argument 2 has type ‘short int *’
alignment_check.c:58: warning: format ‘%lu’ expects type ‘long unsigned int’, but argument 2 has type ‘int *’
McLaren% ./alignment_check
sizeof char     1
sizeof double   8
sizeof short    2
sizeof int      4
sizeof MyStruct 24
&hoge's hexadecimal memory address.
name            type            address         size
&hoge           MyStruct        0x7fff5fbfef10    24
&hoge.ch        char            0x7fff5fbfef10     1
gap1:7
&hoge.dn        double          0x7fff5fbfef18     8
gap2:0
&hoge.sn        short           0x7fff5fbfef20     2
gap3:2
&hoge.nu        int             0x7fff5fbfef24     4
char + gap1 + double + gap2 + short + gap3 + int = 24
&hoge's decimal memory address (with alighnment gap size).
name            type            address         size
&hoge           MyStruct        140734799802128   24
&hoge.ch        char            140734799802128    1
gap1:7
&hoge.dn        double          140734799802136    8
gap2:0
&hoge.sn        short           140734799802144    2
gap3:2
&hoge.nu        int             140734799802148    4

こんな塩梅。
ワーニングは全部アドレスを無理矢理10進数で出したため。
「ポインタ本」と同じように構造体で確認した。
こうすれば普通の処理系では間に別の変数を入れたりする余地はあるまい:-)
で、確かにアドレスを次のメンバ変数のサイズで割り切れない場合、ギャップが出ている。
なるほど、こうすればどのCPUでもスマートに動くというわけですかい。
勉強になった。

C言語ポインタ完全制覇 (標準プログラマーズライブラリ)

著者/訳者:前橋 和弥

出版社:技術評論社( 2001-01-01 )

定価:

Amazon価格:¥ 2,462

単行本 ( 323 ページ )

ISBN-10 : 4774111422

ISBN-13 : 9784774111421



ゲームプログラマになる前に覚えておきたい技術

著者/訳者:平山 尚

出版社:秀和システム( 2008-11-14 )

定価:

Amazon価格:¥ 4,860

単行本 ( 872 ページ )

ISBN-10 : 4798021180

ISBN-13 : 9784798021188


Post to Twitter

, , , ,

No Comments

スタックオーバーフローをテスト

最近、「C言語ポインタ完全制覇 (標準プログラマーズライブラリ)」という本を読んでる。
タイトルの通り、C言語の、それもポインタだけに的を絞った本。
これがなかなかどうして面白くて、文字も大きいのですらすら進んでしまう。
ただ、簡単かと言われれば全く違って、興味深くて考えさせられるような話題ばかり。

ようやく100ページまで進んだ。
そしたらセキュリティーホールの温床になると言うスタックオーバーフローをテストしようという楽しいコードが書いてあった。
で、試したくなるじゃないですか。
若干情報多めに書いて写経してみたのが下記。

// stack_overflow_test.c
#include <stdio.h>
void hello()
{
static int i = 0;
fprintf( stderr, "stderr:\tHello xD\n" );
printf( "\tNo.%d Hello:P\n", i++ );
}
void func()
{
void        *buf[10];
static int  i;
printf( "buf\t%p\n*buf\t%p\n&i\t%p\n&hello\t%p\n\n", buf, *buf, &i, &hello );
for ( i = 0; i < 16; ++i ) {
buf[i] = hello;
printf( "buf[%d]\t%p\n", i, &buf[i] );
}
printf( "\n\tEnd of func()\n\n" );
}
int main()
{
int buf[1000];
printf( "Start\n\n" );
func();
printf( "End\n\n" );
return 0;
}

で、実行するとこう。

% gcc -o stack_overflow_test stack_overflow_test.c
% ./stack_overflow_test
Start
buf     0x7fff5fbfdf20
*buf    0x100000eee
&i      0x10000108c
&hello  0x100000d1c
buf[0]  0x7fff5fbfdf20
buf[1]  0x7fff5fbfdf28
buf[2]  0x7fff5fbfdf30
buf[3]  0x7fff5fbfdf38
buf[4]  0x7fff5fbfdf40
buf[5]  0x7fff5fbfdf48
buf[6]  0x7fff5fbfdf50
buf[7]  0x7fff5fbfdf58
buf[8]  0x7fff5fbfdf60
buf[9]  0x7fff5fbfdf68
buf[10] 0x7fff5fbfdf70
buf[11] 0x7fff5fbfdf78
buf[12] 0x7fff5fbfdf80
buf[13] 0x7fff5fbfdf88
buf[14] 0x7fff5fbfdf90
buf[15] 0x7fff5fbfdf98
End of func()
zsh: segmentation fault  ./stack_overflow_test

あれ~?
hello()が実行されてない・・・??
と言うわけで、落ちる前に一度hello()を呼んでみた。

#include <stdio.h>
void hello()
{
static int i = 0;
fprintf( stderr, "stderr:\tHello xD\n" );
printf( "\tNo.%d Hello:P\n", i++ );
}
void func()
{
void        *buf[10];
static int  i;
printf( "buf\t%p\n*buf\t%p\n&i\t%p\n&hello\t%p\n\n", buf, *buf, &i, &hello );
hello();
for ( i = 0; i < 16; ++i ) {
buf[i] = hello;
printf( "buf[%d]\t%p\n", i, &buf[i] );
}
printf( "\n\tEnd of func()\n\n" );
}
int main()
{
int buf[1000];
printf( "Start\n\n" );
func();
printf( "End\n\n" );
return 0;
}

するとこうなる。

% gcc -o stack_overflow_test stack_overflow_test.c
Start
buf     0x7fff5fbfdf20
*buf    0x100000eee
&i      0x10000108c
&hello  0x100000d10
stderr: Hello xD
No.0 Hello:P
buf[0]  0x7fff5fbfdf20
buf[1]  0x7fff5fbfdf28
buf[2]  0x7fff5fbfdf30
buf[3]  0x7fff5fbfdf38
buf[4]  0x7fff5fbfdf40
buf[5]  0x7fff5fbfdf48
buf[6]  0x7fff5fbfdf50
buf[7]  0x7fff5fbfdf58
buf[8]  0x7fff5fbfdf60
buf[9]  0x7fff5fbfdf68
buf[10] 0x7fff5fbfdf70
buf[11] 0x7fff5fbfdf78
buf[12] 0x7fff5fbfdf80
buf[13] 0x7fff5fbfdf88
buf[14] 0x7fff5fbfdf90
buf[15] 0x7fff5fbfdf98
End of func()
stderr: Hello xD
No.1 Hello:P
stderr: Hello xD
No.2 Hello:P
stderr: Hello xD
No.3 Hello:P
stderr: Hello xD
No.4 Hello:P
stderr: Hello xD
No.5 Hello:P
zsh: segmentation fault  ./stack_overflow_test

これでしっかり動いた。
5回勝手(不正)に実行されちょる。

本の説明によるとメモリの状態は下記のような感じとなるらしい。
–ここ移行に別の変数が追加されていく–
buf[0] 0x7fff5fbfdf20
buf[1] 0x7fff5fbfdf28
buf[2] 0x7fff5fbfdf30
buf[3] 0x7fff5fbfdf38
buf[4] 0x7fff5fbfdf40
buf[5] 0x7fff5fbfdf48
buf[6] 0x7fff5fbfdf50
buf[7] 0x7fff5fbfdf58
buf[8] 0x7fff5fbfdf60
buf[9] 0x7fff5fbfdf68
==他の自動変数==
==関数終了時に戻るアドレス情報など==
==その他の領域==

考察:buf[10]~buf[15]までの6個分余分に配列があると仮定して上がいてます、と(・・アレ?5回しか実行されてない??)。
自動変数を食い破り、呼び出し元の関数情報を破壊して行きます、と。
あ、わかった。。。
funcに自動変数iがあるから1回少ないんだ。
funcをこう変えた。

void func()
{
void        *buf[10];
static int  i;
int a, b, c, d, e; // 追加
printf( "buf\t%p\n*buf\t%p\n&i\t%p\n&hello\t%p\n\n", buf, *buf, &i, &hello );
hello();
for ( i = 0; i < 15; ++i ) {
buf[i] = hello;
printf( "buf[%d]\t%p\n", i, &buf[i] );
}
printf( "\n\tEnd of func()\n\n" );
}

結果はこう。
(ちなみに、ちょっとプログラムいじっちゃったからアドレスと表記が若干違う。。)

Start
&func   0x100000d3f
buf     0x7fff5fbfeea0
*buf    0xffffffffffffffff
&i      0x10000108c
&hello  0x100000cf8
stderr: Hello xD
No.0 Hello:P
buf[0]  0x7fff5fbfeea0
buf[1]  0x7fff5fbfeea8
buf[2]  0x7fff5fbfeeb0
buf[3]  0x7fff5fbfeeb8
buf[4]  0x7fff5fbfeec0
buf[5]  0x7fff5fbfeec8
buf[6]  0x7fff5fbfeed0
buf[7]  0x7fff5fbfeed8
buf[8]  0x7fff5fbfeee0
buf[9]  0x7fff5fbfeee8
buf[10] 0x7fff5fbfeef0
buf[11] 0x7fff5fbfeef8
buf[12] 0x7fff5fbfef00
buf[13] 0x7fff5fbfef08
buf[14] 0x7fff5fbfef10
End of func()
End
zsh: segmentation fault  ./stack_overflow_test

ほら、実行されない。
ただ不思議なことに、

int a;

だけにした場合と、

int a, b, c, d;

までにした場合、

Start
&func   0x100000d3f
buf     0x7fff5fbfeeb0
*buf    0x7365745f776f6c66
&i      0x10000108c
&hello  0x100000cf8
stderr: Hello xD
No.0 Hello:P
buf[0]  0x7fff5fbfeeb0
buf[1]  0x7fff5fbfeeb8
buf[2]  0x7fff5fbfeec0
buf[3]  0x7fff5fbfeec8
buf[4]  0x7fff5fbfeed0
buf[5]  0x7fff5fbfeed8
buf[6]  0x7fff5fbfeee0
buf[7]  0x7fff5fbfeee8
buf[8]  0x7fff5fbfeef0
buf[9]  0x7fff5fbfeef8
buf[10] 0x7fff5fbfef00
buf[11] 0x7fff5fbfef08
buf[12] 0x7fff5fbfef10
buf[13] 0x7fff5fbfef18
buf[14] 0x7fff5fbfef20
End of func()
stderr: Hello xD
No.1 Hello:P
stderr: Hello xD
No.2 Hello:P

となる。
エラーは起きない。
けど変。

結論:戻り先アドレスまで上がいたら、そこにある情報を実行しちゃいました、的な感じの認識。

まぁ、何にせよ、バッファが溢れないように注意しないといつか痛い目見ますよ、と。
精進します。

C言語ポインタ完全制覇 (標準プログラマーズライブラリ)

著者/訳者:前橋 和弥

出版社:技術評論社( 2001-01-01 )

定価:

Amazon価格:¥ 2,462

単行本 ( 323 ページ )

ISBN-10 : 4774111422

ISBN-13 : 9784774111421


Post to Twitter

, , , ,

No Comments

「OpenGLで作るiPhone SDKゲームプログラミング」が面白かった

OpenGLで作るiPhone SDKゲームプログラミング

著者/訳者:横江 宗太(株式会社パンカク)

出版社:インプレス( 2009-12-18 )

定価:

Amazon価格:¥ 7,735

単行本 ( 352 ページ )

ISBN-10 : 4844328085

ISBN-13 : 9784844328087


一言で言うと、この本がとても面白かった。
内容は非常にストレートで、「iPhone向けに簡単なレースゲームを作る」という趣旨の本。
1章がOpenGL ES 1.0を使った2Dの取り扱い。画面への描画。
2章がその応用で「はえたたきゲーム」を作る。

はえたたきゲーム


3章が”パーティクルシステム”と呼ばれる煙などの表現に使われる演出の実装。

パーティクルシステム


4章が「2Dレースゲーム」を作る。

2Dレースゲーム


5章が”衝突判定”の実装。

衝突判定の実装と画面調整


6章が全ての章を応用して「3Dレースゲーム」を作る。

3Dレースゲーム

まず、Objective-CとC++の知識が最低限求められる。
あと、最終章の3Dレースゲームと言ってもそれほどのものを期待してはいけない。
ただ、ゲームの骨組みを組む方法や、画面描画と操作の連携などを学ぶことが出来る。
俺みたいな脳たりんには丁度良い内容となった。

基本的なゲームロジックはC++で実装されているため、そのままではつまらないのでObjective-Cで実装することにした。
これがC++のコードとの対比が出来てなかなか面白かった。
実際問題、C++を前提に書いてあるので付け焼き刃な俺のObjective-C知識ではなかなか無駄な処理が出まくっているのはわかっているのだけれども、それでもやって良かったと思う。
書店で見かけたら、ちょっと目を通してみるのもいいかもしれない。
これだけでゲームは作れないけど、本当に良いきっかけを作ってくれると思う。

ちなみに、上記のゲーム画像はアセットこそお借りしたものの、ソースコードは写経+Objective-Cで書き直したものをiPhone 3G上で実際に動かしたもののスクリーンショット。
一応この程度のものは出来る。

また、Windows用になってしまうけれど、この本の冒頭に書いてある参考文献である「ゲームプログラマになる前に覚えておきたい技術」をこの本の次に読むといい気がしてきた。
実は「ゲームプログラマになる前に~」も持ってはいるものの、なかなか読み進められないでいた。
けど、今回の「OpenGLで作る~」を読んでからなんとなく進められるようになってきた。
基本的に本に書かれているコードは執筆時のいずれかの段階のコードなので、実は動かないものも多いけど、ソースコードに当たれば問題ないレベル。
徐々にステップアップして行くには良いかなぁ、と自分に言い聞かせつつ読み進めてまふ。
参考までに。

ゲームプログラマになる前に覚えておきたい技術

著者/訳者:平山 尚

出版社:秀和システム( 2008-11-14 )

定価:

Amazon価格:¥ 4,860

単行本 ( 872 ページ )

ISBN-10 : 4798021180

ISBN-13 : 9784798021188


そんだけ。

Post to Twitter

, , , , ,

No Comments

WindowsでBitBltなるものを勉強

最近ちょっと落ち着いてきたので、「プログラミングWindows」を読み進めていたんだけど、どーにもつまらない。
俺がやりたいのは、ドット単位での画面描画なのに、ずーっとベクター系。
で、終わったと思えば「キーボード」の章。
ん~・・・。

やりたいことは結構単純で、リアルタイム3DCGの本があるからその公式やらを元にウィンドウ内に書き込みたいなぁ、と。
だから、勉強方法を変えてみることにした。
ウィンドウ描いてその中をラスタライズして2Dにしたリアルタイム3Dを表示したいだけ。
思い切ってページをすっ飛ばして、画面のビットマップ情報をいじることに。
その手始めとして「BitBlt(Bit-block transfer:ビットブリット)」というのをやってみた。

#include <windows.h>
LRESULT CALLBACK MyWindow(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance, LPSTR lpCmdLine,int iCmdShow)
{
static	TCHAR	szAppName[] =
TEXT("おいらのアプリ");	//アプリ名?
HWND			hwnd;	//ウィンドウハンドル
MSG				msg;	//メッセージ
WNDCLASS		wndCls;	//ウィンドウクラス構造体
//ウィンドウスタイルを指定
wndCls.style			= CS_HREDRAW | CS_VREDRAW;
//ウィンドウプロシージャを指定
wndCls.lpfnWndProc		= MyWindow;
wndCls.cbClsExtra		= 0;
wndCls.cbWndExtra		= 0;
//インスタンスハンドル変数を指定
wndCls.hInstance		= hInstance;
//ウィンドウのアイコンを指定
wndCls.hIcon			= LoadIcon(NULL, IDI_APPLICATION);
//カーソルの形を指定
wndCls.hCursor			= LoadCursor(NULL, IDC_CROSS);
//バックグラウンド色を指定
wndCls.hbrBackground	= (HBRUSH) GetStockObject(WHITE_BRUSH);
wndCls.lpszMenuName		= NULL;
wndCls.lpszClassName	= szAppName;
if (!RegisterClass(&wndCls)) {
MessageBox(NULL,
TEXT("このプログラムはWindowsNT以降対応です。"),
TEXT("そーりー"), MB_ICONERROR);
return 0;
}
hwnd = CreateWindow(szAppName, TEXT("どうよ? - Bitmap Viewer"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
//ウィンドウ表示状態を指定
ShowWindow(hwnd, iCmdShow);
//ウィンドウ更新実行
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0)) { //メッセージループ
TranslateMessage(&msg);	//命令変換?
DispatchMessage(&msg);	//命令転送
}
return msg.wParam;
}
LRESULT CALLBACK MyWindow(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static int	cxClient, cyClient, cxSource, cySource;
HDC			hdcClient, hdcWindow;
PAINTSTRUCT ps;
int			x, y;
TCHAR		buff[512];
switch (message) {
case WM_CREATE:
cxSource = GetSystemMetrics (SM_CXSIZEFRAME) +
GetSystemMetrics (SM_CXSIZE);
cySource = GetSystemMetrics (SM_CYSIZEFRAME) +
GetSystemMetrics (SM_CYCAPTION);
wsprintf(buff,
TEXT("GetSystemMetrics (SM_CXSIZEFRAME):%d\n")
TEXT("GetSystemMetrics (SM_CXSIZE):%d\n")
TEXT("GetSystemMetrics (SM_CYSIZEFRAME):%d\n")
TEXT("GetSystemMetrics (SM_CYCAPTION):%d"),
GetSystemMetrics (SM_CXSIZEFRAME),
GetSystemMetrics (SM_CXSIZE),
GetSystemMetrics (SM_CYSIZEFRAME),
GetSystemMetrics (SM_CYCAPTION));
MessageBox(NULL, buff, TEXT("情報"), MB_OK);
return 0;
case WM_SIZE:
cxClient = LOWORD (lParam);
cyClient = HIWORD (lParam);
return 0;
case WM_PAINT:
hdcClient = BeginPaint (hwnd, &ps);
hdcWindow = GetWindowDC (hwnd);
for (y = 0; y < cyClient; y += cySource)
for (x = 0; x < cxClient; x += cxSource) {
BitBlt (hdcClient, x, y, cxSource, cySource,
hdcWindow, 0, 0, SRCCOPY);
}
ReleaseDC (hwnd, hdcWindow);
EndPaint (hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage (0);
return 0;
}
return DefWindowProc (hwnd, message, wParam, lParam);
}

ほぼほぼ「プログラミングWindows」上のソースの写経。
何がやりたいかというと、タイトルバーの左端にあるアイコンをウィンドウ内に敷き詰めるというもの。
改造点はプログラムが立ち上がる際に、アイコンの左端の取得アイコン座標の情報を表示する点。

一生やってろと言いたくなるくらいのペースに苦笑い。

そんだけ。

Post to Twitter

, ,

No Comments

C++でbad_alloc例外を2回取るテスト

と言うか、2回エラーをキャッチすることができるンかい?と言うお話し。
ほら、メモリを取得するときに多めにとってダメで、少なめに取ったらOKかもしれない場合、どうなのかなぁ?とかそんなこと思っただけ。
まぁ、メモリ取れなかった時点で普通はもうやめちゃう訳なんだけれども。
一応出来るのか出来ないのかをはっきりさせたかった。

で、テストコード。

#include <iostream>
#include <iomanip>
#include <new>
using namespace std;
#define MEMSIZE 10000000
int main()
{
double* pDat;
int i = MEMSIZE;
while (true) {
try {
pDat = new double[i];
}
catch (bad_alloc) {
pDat = NULL;
break;
}
cout << "#1 count " << setiosflags( ios::right )
<< setw(2) << i/MEMSIZE << "  |  "
<< setw(9) << i << "bytes | "
<< setw(6) << i/1000 << "Kbytes | "
<< setw(3) << i/1000000 << "Mbytes\n";
delete pDat;
i += MEMSIZE;
}
cout << "after bad_alloc\n";
i -= MEMSIZE;
cout << i/1000000 << "Mbytes can take " << i/sizeof(char) << " characters\n\n";
i = MEMSIZE;
while (true) {
try {
pDat = new double[i];
}
catch (bad_alloc) {
pDat = NULL;
break;
}
cout << "#2 count " << setiosflags( ios::right )
<< setw(2) << i/MEMSIZE << "  |  "
<< setw(9) << i << "bytes | "
<< setw(6) << i/1000 << "Kbytes | "
<< setw(3) << i/1000000 << "Mbytes\n";
delete pDat;
i += MEMSIZE;
}
cout << "after bad_alloc\n";
i -= MEMSIZE;
cout << i/1000000 << "Mbytes can take " << i/sizeof(char) << " characters\n\n";
return 0;
}

実行結果。

#1 count  1  |   10000000bytes |  10000Kbytes |  10Mbytes
#1 count  2  |   20000000bytes |  20000Kbytes |  20Mbytes
#1 count  3  |   30000000bytes |  30000Kbytes |  30Mbytes
#1 count  4  |   40000000bytes |  40000Kbytes |  40Mbytes
#1 count  5  |   50000000bytes |  50000Kbytes |  50Mbytes
#1 count  6  |   60000000bytes |  60000Kbytes |  60Mbytes
#1 count  7  |   70000000bytes |  70000Kbytes |  70Mbytes
#1 count  8  |   80000000bytes |  80000Kbytes |  80Mbytes
#1 count  9  |   90000000bytes |  90000Kbytes |  90Mbytes
#1 count 10  |  100000000bytes | 100000Kbytes | 100Mbytes
#1 count 11  |  110000000bytes | 110000Kbytes | 110Mbytes
#1 count 12  |  120000000bytes | 120000Kbytes | 120Mbytes
#1 count 13  |  130000000bytes | 130000Kbytes | 130Mbytes
#1 count 14  |  140000000bytes | 140000Kbytes | 140Mbytes
#1 count 15  |  150000000bytes | 150000Kbytes | 150Mbytes
after bad_alloc
#2 count  1  |   10000000bytes |  10000Kbytes |  10Mbytes
#2 count  2  |   20000000bytes |  20000Kbytes |  20Mbytes
#2 count  3  |   30000000bytes |  30000Kbytes |  30Mbytes
#2 count  4  |   40000000bytes |  40000Kbytes |  40Mbytes
#2 count  5  |   50000000bytes |  50000Kbytes |  50Mbytes
#2 count  6  |   60000000bytes |  60000Kbytes |  60Mbytes
#2 count  7  |   70000000bytes |  70000Kbytes |  70Mbytes
#2 count  8  |   80000000bytes |  80000Kbytes |  80Mbytes
#2 count  9  |   90000000bytes |  90000Kbytes |  90Mbytes
#2 count 10  |  100000000bytes | 100000Kbytes | 100Mbytes
#2 count 11  |  110000000bytes | 110000Kbytes | 110Mbytes
#2 count 12  |  120000000bytes | 120000Kbytes | 120Mbytes
#2 count 13  |  130000000bytes | 130000Kbytes | 130Mbytes
#2 count 14  |  140000000bytes | 140000Kbytes | 140Mbytes
#2 count 15  |  150000000bytes | 150000Kbytes | 150Mbytes
after bad_alloc

結論、出来るw

おそまつ。

Post to Twitter

, , ,

No Comments

Visual Studio 2008 Express Editionを使い始める、の巻

ちょっとCとC++の勉強もあったまってきたので、「猫でもできるプログラミング」を参考にしながらWindows SDKプログラミングもちょっくらいじりだそうかと思っている昨今。
さっそくイントロダクションでつまったので、備忘録を載せておくことに。

写経すること数分、正直言ってビルド失敗。
で、チキン野郎なので、そのままコピペ・・・したら(エンコーディングの問題からか、)1行になったので、それを分解・・・。
するとどうでしょう。
うごかねぇ~~。。

そのエラーを探ること数分、見つけましたよ、原因を。
そもそも、このページのオーナーさんである粂井さんが一連の記事を書き始めたときのVCのバージョンが4.0だったことが事の発端なんだとか。
というわけで、どこを直せばいいかを書いておこうかなぁ、と。

まず、コンパイラのエラーを見る。

error C2440: '=' : 'HGDIOBJ' から 'HBRUSH' に変換できません。
'void*' から非 'void' 型への変換には明示的なキャストが必要です。
error C2440: '=' : 'char [25]' から 'LPCWSTR' に変換できません。
指示された型は関連がありません。変換には reinterpret_cast、C スタイル キャストまたは関数スタイルのキャストが必要です。
error C2664: 'CreateWindowExW' : 2 番目の引数を 'char [25]' から 'LPCWSTR' に変換できません。(新しい機能 ; ヘルプを参照)
指示された型は関連がありません。変換には reinterpret_cast、C スタイル キャストまたは関数スタイルのキャストが必要です。

最初に、本来HBRUSH型を要求するhbrBackgroundにGetStockObject(WHITE_BRUSH)を入れていることからエラーとなったご様子。
HGDIOBにキャストしてみる。

2番目はszClassNameがchar型なのに対して、LPCWSTR型を要求する場所を引数として入れていることが問題のご様子。
LPCWSTRにキャストしてみる。
(3箇所)

で、該当箇所はこの辺り。

	WNDCLASS myProg;
if (!hPreInst) {
myProg.style			= CS_HREDRAW | CS_VREDRAW;
myProg.lpfnWndProc		= WndProc;
myProg.cbClsExtra		= 0;
myProg.cbWndExtra		= 0;
myProg.hInstance		= hInstance;
myProg.hIcon			= NULL;
myProg.hCursor			= LoadCursor(NULL, IDC_ARROW);
// VC 6.0以降は型のチェックが徐々に厳しくなっている。
// 本来HBRUSH型を要求するhbrBackgroundにGetStockObject(WHITE_BRUSH)を入れたところ、
// エラーとなるのでHGDIOBにキャストしている。
//myProg.hbrBackground	= GetStockObject(WHITE_BRUSH);
myProg.hbrBackground	= (HBRUSH) GetStockObject(WHITE_BRUSH);
myProg.lpszMenuName		= NULL;
// myProg.lpszClassName	= szClassNme;
myProg.lpszClassName	= (LPCWSTR) szClassNme;
if (!RegisterClass(&myProg)) return FALSE;
}
hWnd = CreateWindow(
//szClassNme,
//"俺にもできる?Windowsプログラミング",
(LPCWSTR) szClassNme,
(LPCWSTR) "俺にもできる?Windowsプログラミング",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);

で、これを実行すると・・・。
blank_window_false
タイトル、化けちゃってるね・・・。

どうやら、そもそもの修正方法が違うらしい。
粂井さんのサンプルはどうやらSJIS前提らしく、関数もSJIS前提の関数とかっぽい??
現状のプロジェクトファイルはUnicodeで設定されていて、それを直したりすれば行けるっぽい。

とりあえず、文字エンコーディングを直す。
(Alt-F7でプロジェクトのプロパティ)
プロジェクトのプロパティで「構成」が「Unicode文字セットを使用する」とか書いてあると思うんだけど、
vc2008express_property_unicode
「マルチバイト文字セットを使用する」とする。
vc2008express_property_sjis
恐らくこれでSJISになる。

で、今度は別のエラーが。

error C2440: '=' : 'LPCWSTR' から 'LPCSTR' に変換できません。
指示された型は関連がありません。変換には reinterpret_cast、C スタイル キャストまたは関数スタイルのキャストが必要です。
error C2664: 'CreateWindowExA' : 2 番目の引数を 'LPCWSTR' から 'LPCSTR' に変換できません。(新しい機能 ; ヘルプを参照)
指示された型は関連がありません。変換には reinterpret_cast、C スタイル キャストまたは関数スタイルのキャストが必要です。

なんだとか。
・・・LPCWSTR型にしなくてよかった、と。。。

さっきの箇所は、これでいいらしい・・・。

	WNDCLASS myProg;
if (!hPreInst) {
myProg.style			= CS_HREDRAW | CS_VREDRAW;
myProg.lpfnWndProc		= WndProc;
myProg.cbClsExtra		= 0;
myProg.cbWndExtra		= 0;
myProg.hInstance		= hInstance;
myProg.hIcon			= NULL;
myProg.hCursor			= LoadCursor(NULL, IDC_ARROW);
// VC 6.0以降は型のチェックが徐々に厳しくなっている。
// 本来HBRUSH型を要求するhbrBackgroundにGetStockObject(WHITE_BRUSH)を入れたところ、
// エラーとなるのでHGDIOBにキャストしている。
//myProg.hbrBackground	= GetStockObject(WHITE_BRUSH);
myProg.hbrBackground	= (HBRUSH) GetStockObject(WHITE_BRUSH);
myProg.lpszMenuName		= NULL;
myProg.lpszClassName	= szClassNme;
if (!RegisterClass(&myProg)) return FALSE;
}
hWnd = CreateWindow(
szClassNme,
"俺にもできる?Windowsプログラミング",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);

結果はこれ。
blank_window_success

お粗末さまでした。

Post to Twitter

, , , ,

No Comments

Cの仮引数付きマクロ(マクロ関数)について考える

今ちょうどCをまじめに勉強中。
何度目かの正直・・・。
で、仮引数付きマクロ、いわゆるマクロ関数について整理しておこうかなぁ、と。

マクロ関数の基本としては、

#include <stdio.h>
#define PI 3.141592674
#define circle(r) (r) * (r) * PI
#define echo(str) printf("%s", str)
main()
{
double radius;  // 半径
char str[] = "半径を入力して下さい\n";
echo(str);
scanf("%lf", &radius);
printf("面積は%.2fです。\n", circle(radius));
return 0;
}

とすると、

McLaren% ./marco
半径を入力して下さい
2.1
面積は13.85です。

となる。

何をやっているかと言えば、定数PIは3.141592674と定義。
問題はcircle(r) (r) * (r) * PI。
ソース内でcircle(radius)と呼び出すとその部分が(radius) * (radius) * PIと置換される。
文字通り置換されるらしい。
そのタイミングはコンパイル時。
で、それを連発すると関数呼び出しによるオーバーヘッドが無い分高速化はされるけど、ソースと実行ファイルは肥大化しますよ、と。
それはカーニハン&リッチーのプログラミング言語C(以下、K&R)に書いてある通り。
(p.109 第2版)

で、なぜ(radius) * (radius) * PI と回りくどい書き方をしているかと言うと、それもK&Rに書いてある。
仮引数r がもしhoge – 100 とかだと、r * r * PI だとhoge – 100 * hoge – 100 * PIとなる。
もうちょっと詳しく書くとhoge – (100 * hoge) – (100 * PI) となってしまう。
だからこう書かないと、期待した値にならないよ、と。
実はこのコードの初版ではそのことに気がつかずに書いていたから、急いで直したのはここだけの話w

そうそう、K&Rでは一言も「マクロ関数」とは書いていない。
あくまで「仮引数付きマクロ」とのこと。
まぁ、実際問題関数じゃないからそこは意見が分かれることなんだろうけど、所詮ニュアンスの違いじゃないの?と思ってしまう今日この頃。

そんだけ。

Post to Twitter

, , , ,

No Comments