Posts Tagged C++
続・dprintf
Posted by yu++ in Programming on 2010 年 7 月 5 日
前回「Visual CのGUIアプリで「出力」ウィンドウへデバッグメッセージを出す」でのdprintfは文字サイズ固定だったからちょっとどうかなぁ〜と思った次第。
多少は動的にして文字数に余裕を持たせたいところ。
あと、MSのAPIにはprintf_sとかsprintf_sとか、さらにはvsnprintf_sとか「_s」付きのセキュリティ強化版がある。
で、これ使ってエラー出すと完全に止まる。。。
例えばバッファより文字が多かった場合、即止まって怒られて落ちる。
本当はその後にバッファをより多くreallocする予定だったのにも関わらず・・・。
そしてそのエラーの止め方がわからない。。
なので、デバッグと言う名目なので、若干セキュアじゃないvsnprintfを使うことに。
(超後ろ向き。。。)
UNIX系でサポートされてない関数をバカスカ使うのも正直気が引けてたから、これで良いのだ〜♪
・・・なんてね。
とりあえず今回書いたコード。
// debug.h #define _DPRINTF_MALLOC_ERR_ -100 #define _DPRINTF_ARG_ERR_ -101 #define _DPRINTF_REALLOC_ERR_ -102 int dprintf( const char *format, ...);
// debug.cpp
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include "debug.h"
#define _DEBUG_BUFF_BASE_SIZE_ 256
int dprintf( const CHAR *format, ...)
{
va_list argPtr;
char *debugMsgBuffer;
char *tmpMsgBuffer;
int msgLen = 0;
int maxBuffSize = _DEBUG_BUFF_BASE_SIZE_;
va_start( argPtr, format );
debugMsgBuffer = (char *) malloc( maxBuffSize );
if ( debugMsgBuffer == NULL ) {
OutputDebugStringA( "dprintf:malloc error.\n" );
return _DPRINTF_MALLOC_ERR_;
}
if ( format == NULL ) {
OutputDebugStringA( "dprintf:format argument is null.\n" );
return _DPRINTF_ARG_ERR_;
}
msgLen = vsnprintf( debugMsgBuffer, maxBuffSize - 1, format, argPtr );
// vsnprintfにてバッファ終端に'\0'が書き込まれない
// 時があったので、strlenでも長さチェックをかける。
// あるいは、この時点でスタックを破壊している可能性もある。
if ( (int) strlen( debugMsgBuffer ) > msgLen ) msgLen = -2;
// メモリが足りないときの処理
while ( msgLen < 0 ) {
maxBuffSize += _DEBUG_BUFF_BASE_SIZE_;
tmpMsgBuffer = (char *) realloc( debugMsgBuffer, maxBuffSize );
if ( tmpMsgBuffer == NULL ) {
free( debugMsgBuffer );
OutputDebugStringA( "dprintf:realloc error.\n" );
return _DPRINTF_REALLOC_ERR_;
}
debugMsgBuffer = tmpMsgBuffer;
msgLen = vsnprintf( debugMsgBuffer, maxBuffSize - 1, format, argPtr );
// vsnprintfの'\0'書き忘れ問題をここでも対処。
if ( (int) strlen( debugMsgBuffer ) > msgLen ) msgLen = -2;
}
OutputDebugString( debugMsgBuffer );
free( debugMsgBuffer );
return msgLen;
}
まぁ、コメントの通りなんだけど、
if ( (int) strlen( debugMsgBuffer ) > msgLen ) msgLen = -2;
なんてい言う小賢しい処理を入れてる。
何でかというと、下記のコードを実行してもらいたい。
vsnprintfでおかしくなる。
// debug.cpp
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include "debug.h"
#define _DEBUG_BUFF_BASE_SIZE_ 4
int dprintf( const CHAR *format, ...)
{
va_list argPtr;
char *debugMsgBuffer;
char *tmpMsgBuffer;
int msgLen = 0;
int maxBuffSize = _DEBUG_BUFF_BASE_SIZE_;
char test[1024];
int testLen = 0;
setlocale( LC_ALL, "C" );
va_start( argPtr, format );
debugMsgBuffer = (char *) malloc( maxBuffSize );
if ( debugMsgBuffer == NULL ) {
OutputDebugStringA( "dprintf:malloc error.\n" );
return _DPRINTF_MALLOC_ERR_;
}
if ( format == NULL ) {
OutputDebugStringA( "dprintf:format argument is null.\n" );
return _DPRINTF_ARG_ERR_;
}
msgLen = vsnprintf( debugMsgBuffer, maxBuffSize - 1, format, argPtr );
// vsnprintfにてバッファ終端に'\0'が書き込まれない
// 時があったので、strlenでも長さチェックをかける。
// あるいは、この時点でスタックを破壊している可能性もある。
// if ( strlen( debugMsgBuffer ) > msgLen ) msgLen = -2; // ここをコメントアウト
// メモリが足りないときの処理
while ( msgLen < 0 ) {
{ // debug
testLen = sprintf( test, "maxBuffSize appended from \"%d\" to", maxBuffSize );
maxBuffSize += _DEBUG_BUFF_BASE_SIZE_;
sprintf( &test[testLen], "\"%d\".\n", maxBuffSize );
OutputDebugStringA( test );
} // debug end
tmpMsgBuffer = (char *) realloc( debugMsgBuffer, maxBuffSize );
if ( tmpMsgBuffer == NULL ) {
free( debugMsgBuffer );
OutputDebugStringA( "dprintf:realloc error.\n" );
return _DPRINTF_REALLOC_ERR_;
}
debugMsgBuffer = tmpMsgBuffer;
msgLen = vsnprintf( debugMsgBuffer, maxBuffSize - 1, format, argPtr );
{ // debug
sprintf( test, "maxBufferSize = \"%d\"\n msgLen = \"%d\"\n strlen( debugMsgBuffer ) = \"%d\"\n", maxBuffSize, msgLen, strlen( debugMsgBuffer ) );
OutputDebugStringA( test );
} // debug end
// vsnprintfの'\0'書き忘れ問題をここでも対処。
// if ( strlen( debugMsgBuffer ) > msgLen ) msgLen = -2; // ここをコメントアウト
}
OutputDebugString( debugMsgBuffer );
free( debugMsgBuffer );
return msgLen;
}
さっきの小賢しいコード2カ所を撤去してメモリを何バイト取得し、文字列の長さはいくらで、実際の文字列の長さはいくらかを表示しているコードも入ってる。
これを額面通り
dprintf( "hoge = %5d\n", hoge );
とか文字数を変えて実行してみると、変な文字が付いてくる可能性がある。
自分の環境では
glnWidth = 1432 glnHeight = 815
を期待したところ、
glnWidth = 1432 glnHeight = 815 ォォォォォォォォォ
と言うようなゴミが付いてきた。
どうやら境界線ギリギリで書き込みを行う場合に’\0′が書き込まれていないような気もする。
とは言え、vsnprintfの第2引数を-1から-2に変えたところで同様のエラーが起きる。
個人的には手詰まり。
なので、本当に文字処理をプログラム中で扱うときはこの関数は使えない。。。
対処できないし、こんなの。
strlenによるネガティブな対処で良ければいくらでもやるけど、これ、正解じゃないよね?
そんなわけで、dprintfは前のバージョンの法が良かったのかもしれないと思えてきたり・・・。
(で、でも、文字数制限は無いぞ!と、自分を擁護しつつ、こちらを改良して行くことに。。。)
お粗末。
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」に続く。
c++でのファイル読み込みテスト
「ゲームプログラマになる前に覚えておきたい技術」を読んでいて、C++のファイル読み込みをやっていた。
Cの時でさえ、そんなにファイルいじったりしていなかったので、ちょっと自分でも書いてみる。
まぁ、写経的なコードになっちゃうんだけれども。
main.cpp
#include <iostream>
using namespace std;
bool readFile( char** buff, int* size, const char* filename );
int main()
{
char *fBuff = 0;
int fSize = 0;
if ( readFile( &fBuff, &fSize, "test_dat.txt" ) ) {
cout.write( fBuff, fSize );
}
delete[] fBuff;
return 0;
}
fileRead.cpp
#include <fstream>
using namespace std;
bool readFile( char** buff, int* size, const char* filename )
{
// 初期化
*buff = 0;
*size = 0;
// ファイルストリームをインスタンス化しつつストリームオープン
ifstream ifs( filename, ios::binary );
// 失敗
if ( !ifs ) { return false; }
ifs.seekg( 0, ifstream::end ); // ファイルストリームを最後まで移動
// 現在の読み込み位置(詰まるところサイズ)を取得
// tellg()はpos_type型を返すので、実体はintでも一応キャスト
*size = static_cast< int >( ifs.tellg() );
ifs.seekg( 0, ifstream::beg ); // ファイルストリームを先頭へ
// メモリ確保
*buff = new char [ *size ];
// ファイルの内容をメモリに読み込む
ifs.read( *buff, *size );
return true;
}
test_dat.txt
Hello. And say goodbye.
で、結果。
% g++ -o test main.cpp fileRead.cpp % ./test Hello. And say goodbye.
ちなみに、
delete[] *fBuff;
とても、
delete[] &fBuff;
としても怒られた。
ダブルポインタは大本の変数名だけ指定すれば良いんですかい?
メモリリークとかしないのか?
この前紹介した「OpenGLで作るiPhone SDKゲームプログラミング」では
ParticleSystem::~ParticleSystem()
{
int i;
for (i = 0; i < this->amount; ++i) {
delete this->particle[i];
}
delete this->particle;
}
なんてコードがあったから、面倒くさいけど個別に解放しないとリークしちゃうのかと思った。
そうか、その配列内でさらにnewしているから個別に解放しているのか。
そうじゃない場合はそのまま解放して良い、と。
ポインタをもっとよく勉強せねば。。。
そんだけ。
「OpenGLで作るiPhone SDKゲームプログラミング」が面白かった
Posted by yu++ in C++, iPhone, Objective-C, OpenGL on 2010 年 6 月 14 日
著者/訳者:横江 宗太(株式会社パンカク)
出版社:インプレスジャパン( 2009-12-18 )
定価:¥ 2,940
Amazon価格:¥ 2,940
単行本 ( 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-15 )
定価:¥ 4,725
Amazon価格:¥ 4,725
単行本 ( 872 ページ )
ISBN-10 : 4798021180
ISBN-13 : 9784798021188
そんだけ。
C++でのクラスと構造体
前回C++のクラスは構造体の拡張であるという話し。
まず一般的な構造体から見ていきましょう、と。
#include <iostream>
#include <string.h>
using namespace std;
struct hoge
{
int a;
double b;
char c[128];
} foo;
int main()
{
hoge* pFoo = &foo;
foo.a = 100;
foo.b = 3.141592674;
strcpy(foo.c, "構造体のテストじゃ~。");
cout << "直接参照:\n" <<
"foo.a = " << foo.a <<
"\nfoo.b = " << foo.b <<
"\nfoo.c = " << foo.c << endl;
cout << "間接参照:\n" <<
"pFoo->a = " << pFoo->a <<
"\npFoo->b = " << pFoo->b <<
"\npFoo->c = " << pFoo->c << endl;
cout << "\n\n";
pFoo->a = 500;
pFoo->b = 1.05;
strcpy(pFoo->c, "ポインタから更新じゃじゃ~。");
cout << "直接参照:\n" <<
"foo.a = " << foo.a <<
"\nfoo.b = " << foo.b <<
"\nfoo.c = " << foo.c << endl;
cout << "間接参照:\n" <<
"pFoo->a = " << pFoo->a <<
"\npFoo->b = " << pFoo->b <<
"\npFoo->c = " << pFoo->c << endl;
return 0;
}
まぁ、これは普通に読めること前提。
一応実行結果はこれ。
直接参照: foo.a = 100 foo.b = 3.14159 foo.c = 構造体のテストじゃ~。 間接参照: pFoo->a = 100 pFoo->b = 3.14159 pFoo->c = 構造体のテストじゃ~。 直接参照: foo.a = 500 foo.b = 1.05 foo.c = ポインタから更新じゃじゃ~。 間接参照: pFoo->a = 500 pFoo->b = 1.05 pFoo->c = ポインタから更新じゃじゃ~。
で、これをそっくりクラスで書くと下記の通り。
#include <iostream>
#include <string.h>
using namespace std;
class hoge
{
public:
int a;
double b;
char c[128];
} foo;
int main()
{
hoge* pFoo = &foo;
foo.a = 100;
foo.b = 3.141592674;
strcpy(foo.c, "クラスのテストじゃ~。");
cout << "直接参照:\n" <<
"foo.a = " << foo.a <<
"\nfoo.b = " << foo.b <<
"\nfoo.c = " << foo.c << endl;
cout << "間接参照:\n" <<
"pFoo->a = " << pFoo->a <<
"\npFoo->b = " << pFoo->b <<
"\npFoo->c = " << pFoo->c << endl;
cout << "\n\n";
pFoo->a = 500;
pFoo->b = 1.05;
strcpy(pFoo->c, "ポインタから更新じゃじゃ~。");
cout << "直接参照:\n" <<
"foo.a = " << foo.a <<
"\nfoo.b = " << foo.b <<
"\nfoo.c = " << foo.c << endl;
cout << "間接参照:\n" <<
"pFoo->a = " << pFoo->a <<
"\npFoo->b = " << pFoo->b <<
"\npFoo->c = " << pFoo->c << endl;
return 0;
}
こんな風にpublic:をつけるだけ。
感がいい人はわかってると思うけど、構造体でもprivate:を付けてしまえばクラスっぽくなると言うね。
となると、構造体でもメンバ関数(メソッド)を用意することができる。
同じようなコードにすると下記なような感じになる。
#include <iostream>
#include <string.h>
using namespace std;
class CFoo
{
private:
int a;
double b;
char c[128];
public:
CFoo():a(0),b(0.0)
{
for (int i = 0; i < 128; ++i)
c[i] = '\0';
}
~CFoo() {}
int setA(int num)
{
a = num;
return 0;
}
int setB(double num)
{
b = num;
return 0;
}
int setC(char* szStr)
{
if (strlen(szStr) > 127) {
return 1;
}
strcpy(c, szStr);
return 0;
}
int getA() { return a; }
double getB() { return b; }
char* getC() { return c; }
};
struct Bar_t
{
private:
int a;
double b;
char c[128];
public:
Bar_t():a(0),b(0.0)
{
for (int i = 0; i < 128; ++i)
c[i] = '\0';
}
~Bar_t() {}
int setA(int num)
{
a = num;
return 0;
}
int setB(double num)
{
b = num;
return 0;
}
int setC(char* szStr)
{
if (strlen(szStr) > 127) {
return 1;
}
strcpy(c, szStr);
return 0;
}
int getA() { return a; }
double getB() { return b; }
char* getC() { return c; }
};
int main()
{
CFoo obj;
Bar_t obj2;
cout << "初期化時のクラス:\n" <<
"\tobj.getA() = " << obj.getA() <<
"\n\tobj.getB() = " << obj.getB() <<
"\n\tobj.getC() = " << obj.getC() << endl;
cout << "初期化時の構造体:\n" <<
"\tobj2.getA() = " << obj2.getA() <<
"\n\tobj2.getB() = " << obj2.getB() <<
"\n\tobj2.getC() = " << obj2.getC() << endl;
obj.setA(10000);
obj.setB(11.11);
obj.setC("我が輩の年齢は10万と26歳である、フハハハハ!");
obj2.setA(-1);
obj2.setB(42.195);
obj2.setC("オラ、ワクワクしてきたぞ!");
cout << "代入時のクラス:\n" <<
"\tobj.getA() = " << obj.getA() <<
"\n\tobj.getB() = " << obj.getB() <<
"\n\tobj.getC() = " << obj.getC() << endl;
cout << "代入時の構造体:\n" <<
"\tobj2.getA() = " << obj2.getA() <<
"\n\tobj2.getB() = " << obj2.getB() <<
"\n\tobj2.getC() = " << obj2.getC() << endl;
return 0;
}
何となく命名規則をそれっぽくしてみたけど、こんな感じ?
ほとんどというか、private:とpublic:を付けてしまえば両方とも一緒という結果。
そんだけ。
C++でのメンバ変数として、動的オブジェクトを作る
C++を勉強していて何が困ったって、メインで動的にメモリ領域をとる方法はたくさん載っているのに、クラスのメンバ変数を動的にとる方法がなかなか載っていない。
と言うわけで、いろいろ試行錯誤した結果、下記のようになった。
テスト用コード
#include <iostream>
#include <string.h>
using namespace std;
class hoge
{
public:
char* szStr;
hoge(char* szNewStr)
{
szStr = new char[strlen(szNewStr)];
strcpy(szStr, szNewStr);
}
~hoge() { delete szNewStr; }
};
int main()
{
char szStr[] = {"ばかやろ~~~~~~~~~~~~~~~"};
cout << szStr << endl;
cout << "end" << endl;
cout << strlen(szStr) << endl;
hoge* obj;
obj = new hoge(szStr);
cout << "\ntest\n";
cout << szStr << endl;
delete obj;
return 0;
}
実行結果。
ばかやろ~~~~~~~~~~~~~~~ end 57 test ばかやろ~~~~~~~~~~~~~~~
まず、hogeなるクラスを定義します、と。
そのメンバ変数に動的に確保したい型のポインタを置きます、と。
それをコンストラクタなり、なんなりでnewしてあげるとそこにで動的に確保される、と。
ちなみに、ここで言う「型」というのは文字通りの型じゃなくても良くて、クラスでも良い。
なぜかというと、C++でのクラスは構造体を拡張したものだから。
その詳細はまた別の記事にて。
そんだけ。
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
おそまつ。
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);
で、これを実行すると・・・。

タイトル、化けちゃってるね・・・。
どうやら、そもそもの修正方法が違うらしい。
粂井さんのサンプルはどうやらSJIS前提らしく、関数もSJIS前提の関数とかっぽい??
現状のプロジェクトファイルはUnicodeで設定されていて、それを直したりすれば行けるっぽい。
とりあえず、文字エンコーディングを直す。
(Alt-F7でプロジェクトのプロパティ)
プロジェクトのプロパティで「構成」が「Unicode文字セットを使用する」とか書いてあると思うんだけど、

「マルチバイト文字セットを使用する」とする。

恐らくこれで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);
お粗末さまでした。


