SSブログ

Enumと文字列をマクロでうまく対応させる方法 [C++]

Visual C++ Team Blog : Enums, Macros, Unicode and Token-Pasting
http://blogs.msdn.com/vcblog/archive/2008/04/30/enums-macros-unicode-and-token-pasting.aspx


MYENUM(dog)

MYENUM(cat)

MYENUM(fish)

MYENUM(bird)

And the following C++ code:

enum Animal {

#define MYENUM(e) _##e,

#include "animal.inc"

#undef MYENUM

};



wchar_t* AnimalDescription[] = {

#define MYENUM(e) L"_" L#e,

#include "animal.inc"

#undef MYENUM

};


これは目からうろこだった。
汚いマクロ汚いといわれていても、これ他の言語でやれるかっていったらやれないもんなぁ。まさに必要悪。


分割統治法 [C++]

ABAの日記 - 分割統治法の徹底適用
http://d.hatena.ne.jp/ABA/20080417

ひげねこにっき - 実践
http://higeneko.com/diary.php?Date=2008-04-06#Date2008-04-06

汎用的なアルゴリズムがどうとかっていう話じゃなくて、単純に総当り計算しなくていいところはしないようにしましょうって言うだけの話。ただ、これを分割統治法っていうことを知らなかった。自分では結構普通に計算量の計算とか意識してプログラムするので、名前は知らなくても実際にはやってたりすることが多い。(と言い訳)

リンク先のリンク先の例だと、弾と自機の当たり判定をするのに、ブロック分割された範囲にあるものでのみ行うことで計算量を減らすのに成功したようだ。

ソースは不明だが、弾オブジェクトツリーのルートノードを分割数だけ用意して、オブジェクト移動時にノード間移動を処理。その後自機がいるところのノードと総当りチェック。

今回は平面だったが、FPSでは三角錐の中かどうかというところでもうまく使えそうかな。と思ったけどロケランみたいな軌道が遅い場合はどうするんだろ。明らかに遠いオブジェクトと、そうじゃないオブジェクトを区別する方法は結構めんどくさそうだ。球オブジェクトを自分の周りの制空権として考えて、それの中or外でノードを分割すればいいのかな。まあ、FPSで弾幕張ることも無いからゲームシステム次第か。


http://d.hatena.ne.jp/faith_and_brave/20080418/1208499075

いつも猛っているfaith_and_braveさんのところ

この手の話は、プログラマーしかしないのが問題であって、結局商売でやる場合には、欧米流の動けばオッケーとするのが手っ取り早いんじゃないかなぁと思うわけです。現実として、動けばおっけーっていうにもレベルがあって、動かないことが多々あるんですが。

現実問題として、これらの「見えない技術」を見える化するにはどうしたらいいのか。

例えば、領域の開放をするのに

#define SAFE_FREE(ptr) {if(ptr){free(ptr);ptr=NULL;}

というマクロですべて書くというようなことを某所で言ったら、アルゴリズムがちゃんとしてればそんなNULL代入は必要ないしパフォーマンスの低下だ!なんて言われてしまいました。

例えばこうしておくとどういうことが起こるか。
私の経験上、かなりの確率で起きる現象ですが
・NULLを入れることで、開放した領域にアクセスしようとするとアクセスバイオレーションが発生してバグが見つかる。

 そもそも、ちゃんと動いてたら私は仕事しなくていいわけです。ちゃんと動いてないからこそ、こういう小技でバグをあぶりだす。これは私が動いてないプロジェクトばかり担当してきたから、そういう老婆心が染み付いてきてるんですが、上のような考えの人はコンセプトだけ作って、あとは私のような人に丸投げすることが多いんですよね。だから上のようなこと言いいたいのは、「やるのは勝手だが俺のそばに来るな。自分のケツは自分で拭け」になります。

よくあるのが文字列で例えば以下の場合を考えて見ます。

int copystring( **dst,*src ) { int len = strlen(src); *dst = malloc(len); strcpy( *dst,src ); return len; }


これはいくつか問題点があります。

・srcのNULLチェックが無い
・lenが0のとき、大きすぎるときのチェックが無い
・バッファが確保されなかった場合のチェックが無い

 例えば、アルゴリズム的にこれで問題なかったとしましょう。しかし、将来的に外部の仕様が変更になった、もしくは思っていた仕様とは違っていた、という場合、これらがバグの引き金になることは容易に想像できます。私はそういうことまで含めてプログラミングをするように心がけているのですが、そうじゃない人に説明するのは一苦労です。そういう人はこのコードをコピペしてばらまいてたりもしますが・・・

 あと、もう一つすごく重要なことですが、エラー処理を追加したときに、必ずログを出すようにすることです。アルゴリズムとして明確に起きるエラーで無い限り(例えばファイルが存在しないエラー)必ずログに出すようにします。これをやらないと、誰が悪いのかわからなくなります。世の中ハードを雑に扱ったり、無知ゆえに間違った使い方をする人がいっぱいいます。ただ「動かない」のではなく、「~~というエラーで動かない」ということを、周りの人にわからせないと、すべての責任がプログラマーに集中します。これはなんとしても回避しなければなりません。

 で、こういうのをやったとして、これらによってどれぐらい効果があったかと言われると、明確に数字では表現できないんですよ。だって「動いて当然だけど動いてなかった」のを動くようにしただけですから、最初の計画の数字に入ってないんですよ。そもそもバグは想定外で起きるので、計画に入れない人が多い。

 明確にやろうとすると、バグが何件発生して、何件修正された、みたいなバグトラッキングシステムみたいなことになりますが、技術うんぬんって話じゃないんですよね。やりたいことを現実的な範囲で実装する能力というのは、やって当たり前で、失敗するとできる人のほうへ飛ぶだけだったりして、やっぱり評価されない。そして、無謀な計画を立てる人の給料のほうが高い。プログラマーとしてうまく立ち回るのはなかなか難しいものです。


[特集]カプコン×インテル。「ロスト プラネット」のマルチスレッド最適化対談 [C++]

[特集]カプコン×インテル。「ロスト プラネット」のマルチスレッド最適化対談
http://www.4gamer.net/specials/capcom_x_intel/capcom_x_intel_01.shtml

Windowsプログラマじゃないとわかりにくいが、わかる人にとっては一般論を話しているのであまり目新しい要素は無かったが、マルチコア対応フレームワークを作るうえで参考になりそうなので、会話の内容からまとめて見る。

マルチスレッドでパフォーマンスを上げるためには個々のスレッドの独立性が高くなければなりませんが,ゲームはそうじゃない


完全に2つのタスクが同時に別々の処理をしていれば、2つのPCを使って動かしているのと同じようになるから速くなる。しかし、互いに関係性がある(片方がもう片方の処理が終わるのを待つなど)の状態が発生すると待ち時間が発生し、結果として遅くなる。

MT Frameworkでは,キューに積んで並列で実行します。最後にソートしてスレッド間のタイミングを取るという

最近の3Dゲームでは,処理してから描画されるまでの遅延があるんです。ロスト プラネットだと1フレーム遅れるんですよね。だから120fpsで動かして,実際には60fps相当になるんです。


これは入力とオブジェクトの動画を非同期で行っていて、入力があるとイベントをシグナルして対象オブジェクトをアップデート、アップデートされるされないに関わらずキューに積んで、描画対象のオブジェクトを順次CPUで描画していく。というイメージかな。120fpsで処理しているうち、オブジェクトのアップデートと描画のタイミングが交互に行うイメージなので1フレーム遅れてしまう。と解釈したがあっているのかどうか。

調べたところ,クリティカルセクションの処理が,Xbox 360と比べて非常に重いということが分ったんですよ。それで,コアな部分ではできるだけクリティカルセクションを使わないように書き換えたんです。これで,かなりパフォーマンスが上がりました。


CriticalSectionは待ち状態になると、まず規定回数のスピンロックで試し、それでも駄目な場合はカーネルモードに移行する。カーネルモードに移行しないようにSpinlockを増やせばそのまま対応できたのでは?と思ったのだが、どうなのだろうか。CriticalSectionも内部ではInterlock使ってるはずなんで。

Xbox 360とWindowsはCPUが違いますよね。Xbox 360のCPUはL2キャッシュを三つのCPUコアで共有しています。だから速い。Core 2 Quadは,(CPUコアは四つあるが,2コアずつ)L2キャッシュが独立しているので,そのオーバーヘッドがかなり大きいんです。Core 2 Duoだとシングルコアに対して1.8倍のパフォーマンス向上を実現できるんですけど,Core 2 Quadだと(シングルコアに対して)2.3倍くらいしか出ない


これはAthlon 64 X2のほうがアドバンテージあるといわれていた部分。Intel自身はアーキテクチャの変更を避け、キャッシュを増やすことで対応した(笑)。でも同期の問題だけはどうにもならない。

でも,8コアなら600%くらいの性能が欲しくならないですか?


この情熱は見習いたい




ペアリング暗号 [C++]

very fast etaT pairing for Core 2 Duo
http://homepage1.nifty.com/herumi/crypt/pairing.html

ペアリング暗号?なるものがイマイチよくわからない人は
http://homepage1.nifty.com/herumi/crypt/crypt00.html
こっちを読むと多少わかりやすいかも。

比較的新しい分野なのか、ぐぐってみてもさっぱりわかりません。

Short Coding [C++]

Short Coding ~職人達の技法~

Short Coding ~職人達の技法~

  • 作者: Ozy
  • 出版社/メーカー: 毎日コミュニケーションズ
  • 発売日: 2007/08/09
  • メディア: 単行本(ソフトカバー)

積読シリーズ。
実践で役に立つ本と思って買ったが、実践で使ってはいけない本だった(笑)
EffectiveC++などとも違った、言語仕様及びHWに依存した狭くて深い知識を要求される。
自分の知識がどれぐらい正しいかどうかを確認したい、ShortCodingそのものが楽しいと思える人にはオススメ。
このコードは仕事では絶対に使ってはいけない本なので、初心者が買ってもまったく理解できないと思われる。しかし、どういう方法があるかは知っておいても損は無いし、こういうことする馬鹿が出るかもしれないという教訓にもなるかもしれない。
また、初心者であることに気がついていない中級者は、読むことで自分の凝り固まった思想を破壊してくれるかもしれない。
プログラマが息抜きに読むには面白い本。


VS2008にTR1のベータが登場 [C++]

http://www.microsoft.com/downloads/details.aspx?FamilyID=d466226b-8dab-445f-a7b4-448b326c48e7&DisplayLang=en
Visual C++ 2008 Feature Pack Beta

This feature pack also includes an implementation of TR1. Portions of TR1 are scheduled for adoption in the upcoming C++0x standard as the first major addition to the ISO 2003 standard C++ library. Our implementation includes a number of important features such as:

* Smart pointers

* Regular expression parsing

* New containers (tuple, array, unordered set, etc)

* Sophisticated random number generators

* Polymorphic function wrappers

* Type traits

* And more!

VS2005に環境移したばかりだけど、2008はいいかもしれん。
MSはC/C++の環境としてVC6を出したときは良かったが、その後.netに力を入れるあまり、VB.netやC++/CLIやったりして、メインのCについてはあまり力を入れていない感じがする。

それにしてもHTMLヘルプになってからのMSDNは使いにくくてかなわん・・・


3D関係 [C++]

ゲームに限らず3Dベースでオブジェクトを動かすことを考える
ベースはC++でパフォーマンスにある程度考慮する
が、簡単にいじれるようにスクリプトでいじれるようにする

ってことで、すごく誰かがやってそうなのに、検索だと大してひっかからない

いつも見てるところでHyperionというのがあるが
どうもDemoniak3Dという名前に変わったのかな?

http://www.ozone3d.net/demoniak3d/
Xmlでシーンを決定してLuaで記述して、3Dに必要と思われる部分をほとんど網羅している

が!
Game Development: with the support of all usual medias such as 3D models, 2D textures, sound, the use of the powerful LUA scripting language and the integration of NovodeX Physics Engine, Demoniak3D is ready for the development of all your little games.

all your little gamesのlittleが気になる。やっぱりスクリプトなんでハイパフォーマンスは無理ってことなんだろうか。とりあえず、これをいろいろいじって見ようと思う。
ソースが公開されてないので、これだけでうまく動かないようだったら、自力で実装になるのかもしれず。

とりあえず、Luaのページ
http://www.lua.org/
http://staff.aist.go.jp/yutaka.ueno/lua/docsjp.html

将来お世話になるかもしれないライブラリのページ
http://irrlicht.sourceforge.net/
http://www.libsdl.org/

Luaの参考書

入門Luaプログラミング

入門Luaプログラミング

  • 作者: 上野 豊
  • 出版社/メーカー: ソフトバンククリエイティブ
  • 発売日: 2007/12/22
  • メディア: 単行本


VS2003以前のプロジェクトをVS2005以降でビルドする [C++]

さすがにVS6のプロジェクトは少なくなってきたが、以前海外のプロジェクトではVS6やVS2003で止まっていることも多い。

それらのプロジェクトをVS2005以降でコンパイルする際には以下の3つを定義しておいたほうがよい。

_CRT_SECURE_NO_DEPRECATE
_CRT_NON_CONFORMING_SWPRINTFS
_USE_32BIT_TIME_T

おまけ

quake3 1.32bのソースをVisual Studio 2005以降でビルドする

Quake2のソースがGPLで公開されているので、それを基にしたフリーのFPSがいくつかあるようだ
http://www.warsow.net/

Quake2は今だったらほとんどのマシンでもさくさく動くだろうし、欧州では未だにQuake2は遊ばれているぐらい根強い人気ではあるが、今でこそ見劣りもするがグラフィック的にはQuake3のほうがよいし、自分が一番最初にはまったFPSなので思い入れがある。
で、なんか作れるかわからないけど、とりあえずコンパイルしてみることになった。

プロジェクトの改行がすべてLFなので、VSで読み込み、変換ができない。
海外のオープンソースにはありがちな罠で以下のようにして対応する。

・*.vcprojでソースディレクトリを検索する
・すべてのvcprojの改行をLFからCR+LFに変更する
・ついでにプリプロセッサの定義に、上記のSWPRINTFS以外を追加しておく
・マップ作成用はq3Radiant/Radiant.sln 、ゲーム本体はcode/quake3.slnにある

d:\quake3-1.32b\q3radiant\splines\splines.h(450) : error C4430: 型指定子がありません - int と仮定しました。メモ: C++ は int を既定値としてサポートしていません
d:\quake3-1.32b\q3radiant\splines\splines.h(454) : error C4430: 型指定子がありません - int と仮定しました。メモ: C++ は int を既定値としてサポートしていません

関数の戻り値が未記述なのでvoidにする

Radiantプロジェクトのプリプロセッサに

WINVER=0x0500
_WIN32_WINNT=0x0500

を追加する。MFC使ってるくせにWINVER定義しないとは何事か・・・

・iやnなど未定義の変数を宣言する
これはfor文の中に宣言された変数がfor文の外でも使える古いタイプのコンパイラと、最新の規格では中でしか使えないことになったことの矛盾により発生している

・EDITCONTINUE昨日を使わなければ、EDITCONTINUEではないプログラムデータベースを選択する
個人的にあまり信用して無い機能なんで、こうしました

・ソースコードもLFのみなので、新規にファイルを開くたびにCR+LF変換する旨の警告がでる
・タブ長が2のつもりで書かれているらしく、スペースの部分とタブの部分でインデントがあってない。
・プロジェクトファイルがそもそも無いのにプロジェクトファイル開こうとして失敗すると例外が出る(NULLチェックしてない)

とかいろいろと外人クオリティが満載ではある
これでどうこうする部分までもってくのは難しいな・・・


乱数生成 [C++]

数学的に乱雑であることが求められるようなことはともかく、通常のアプリケーションでは乱数はあまり利用されないので、乱数生成は軽視されがちでもある。

ANSI Cの乱数の精度の悪さは、誰かがやってくれていたが、下位1ビット(数ビット)に規則性があるので、2の剰余などを選んだ場合にかなり偏りが出ることが、すでに判明している。

もっと精度がよく、しかもパフォーマンスのよい乱数は無いかということで、メルセンヌツイスタというものがよく使われている。
そして、そのメルセンヌツイスタが、Boostにも存在し、randomをインクルードすることで利用できる。

しかし、乱数を利用するときに、安易に剰余を使うと偏りが出やすく、また、MTに対して負の値を使うと結果は未定義であるという。(便利な代わりに何かしら対価を払うのは当然ではある、この場合は制約や手間)

よって正しい使い方はboostにあるrandom_demo.cppにあるようにしなければならない。

種に負の値が入らないようにunsigedとして定義する
// Define a random number generator and initialize it with a reproducible
// seed.
// (The seed is unsigned, otherwise the wrong overload may be selected
// when using mt19937 as the base_generator_type.)
base_generator_type generator(42u);

値の範囲を定義するときは、安易に剰余を使わず、uniform_realやuniform_intを使う
// Define a uniform random number distribution which produces "double"
// values between 0 and 1 (0 inclusive, 1 exclusive).
boost::uniform_real<> uni_dist(0,1);
boost::variate_generator<base_generator_type&, boost::uniform_real<> > uni(generator, uni_dist);

あとはuni()を呼び出すだけで乱数が生成される。
上記の例だと種が固定されているので、以下のようにすることで、時間ベースの種が設定される

generator.seed(static_cast<unsigned int>(std::time(0)));

しかし、ANSIベースのtimeはms単位ではないので、連続して実行されるような場合には種が統一になる可能性をはらんでいる。

timeの戻り値は1970 年 1 月 1 日午前 0 時以降の経過秒数を返してくるが、ネットワークで同期したときに、違うPCで同じ結果を生む可能性もあるなど、秒単位では心もとないことも多い。(乱数を使うときは、違う結果が出ることを求めていることが多い)。
よって、Windowsでの開発に限って言えば、time(0)ではなくGetTickCount()を使うのが何かと便利であろう。


ATLメモ [C++]

ATLを使ってコンテキストメニューに追加しているのだが、コンパイルが通らなかった・・・

ATLプロジェクトは通常のWin32やMFCのプロジェクトと比較して以下の点が違う

・DLLMainはVCでプロジェクトを作るときに「自動的に」作成される。
・VC6,2003,2005で、自動的に作成されるソースが違う。そのため、VC6なプロジェクトをVC8でコンパイルしようとするとコンパイルエラーが発生する。
・PlatformSDKのバージョン違い?によって、ヘッダファイルが「自動的に」更新される。
・Win32などのプロジェクトでは、DebugとReleaseしかプロジェクトが無いが、ATLプロジェクトでは、Releaseの変わりに、Release MinSize,Release MinDependencyの2つがあり、さらにMBCSとUNICODEのバージョンがある(よって3*2=6プロジェクトある)
・マルチバイトなプロジェクトでCRT関数を使うと、LIBCMT.LIBが_mainが無いと怒られる。これは、マルチスレッドCRT関数は、main内で初期化を必要とするためである。ちなみにunicodeな場合、内部でmemcmpなどの関数に置き換えられるので、コンパイルが通る。
 「ATL ライブラリ リファレンス CRT スタートアップ コードに依存する関数の確認」参照
http://support.microsoft.com/kb/99456/ja
 MinDependencyでコンパイルするための関数比較表
 この表によれば、strcmpの変わりにlstrcmpを使うことで対応できるが、CRT関数をサポートしている数が異常に少ない。特にstrncmpに対応する関数がない。
・VisualStudioにDDKのパスが入っている場合、コンパイルが通らない。
 intsafe.hが見つからない > inc\crtにパスを通せばOK
 atlalloc.hのLLONG_MINが見つからない > inc\atl30 にパスを通せばOK
 TCHARが再定義 > 未解決

・VisualStudioとATLのバージョンの関係
 VC6 ATL3.0
 VC2003 ATL7.0
 VC2005 ATL7.1
 2003からバージョンをコンパイラのバージョンに揃えたようだ。

・DDKとATL
 DDKのフォルダにはATLがついている。
 XP,2003 SP1 DDK ATL21,ATL30
WDK ATL21,ATL30,ATL71(crtの下にある)

・それらを使ってコンパイルしてみる
2003SP1DDKのATL30 : TCHARの再定義だのなんだのと怒られる
WDKのATL71 : atlcore.hでnLengthが無いと文句を言われる

結論、ATLを使う可能性がある場合、DDKのパスをVSに登録してはいけない。

DDKなプロジェクトばかり作る場合、めんどくさいんでDDKのパスをVSに登録しちゃうんですが、そうすると、結構変なエラーが出たりする。SCSI_PASS_THROUGHのようなユーザーレベルアプリケーションを作成しようとすると、DDKのパスは通しておきたい。PlatformSDKが前に定義されてると再定義だのなんだのと言われた記憶があるので、DDKのパスを優先的にしておく。CRTをインクルードすると、PlatformSDKなどで再定義エラーが出るので、それはいれない。必要な場合のみプロジェクト側でインクルードするようにする。通常のアプリケーションはこれで問題なかった。が、ATLで問題が出てきてしまった・・・正しいパスの設定方法ってあるのか?

結局プロジェクトごとにDDKのパスを設定するのが正しい気もするが、なんだかなーって感じ


この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。