トップページgamedev
935コメント361KB

OOとゲームプログラミング

■ このスレッドは過去ログ倉庫に格納されています
0001名無しさん@お腹いっぱい。01/11/07 23:55ID:HnYWCQK1
OOをどのように用いれば美しくゲームプログラミングを
行うことが出来るのか語り合うスレです。
075371902/03/19 01:30ID:9e3mOAQn
>>751
おお!THX!ビンゴです。早速問い合わせてみます。

>>752
THX!です。見てみます。
でも、本業の方が押してきてるので、作業進まないかも。
(でも、やりたいのはSTGw)

今、某CUBEやってるんですが、コールバック関数のラッピングに手間取ってます。
(static)関数内でクラスにアクセスのに、とりあえず、グローバルポインタ渡すしか
思いつかない、、。
0754名前は開発中のものです。02/03/19 01:56ID:???
>>746
 当たり判定は当たり判定管理クラスにタスクを登録して結果を
コールバックで取得&反映って感じでしょ。

 後は判定管理クラス内をレイヤー化して置く事で様々な状況に
対応できるようにすると。
075571902/03/19 04:05ID:9e3mOAQn
>>754
すいません。良く解らないです。

コールバックって、キリの良い所で処理を返す必要がないものに
対して使うものだと認識してます。
(メモカ、CDの裏読み等)当り判定って、フレームごとに全て完了
させないとイケナイ気がするんですが・・。
0756名前は開発中のものです。02/03/19 04:17ID:???
>>755
> コールバックって、キリの良い所で処理を返す必要がないものに
> 対して使うものだと認識してます。
認識が間違ってます。
0757名前は開発中のものです。02/03/19 05:05ID:???
>>755
ウンウン、コンシューマな人だねぇ…。
別にハードウェア割り込みから呼ばれる関数をコールバックと限定
するワケじゃないハズだよ。

当たり判定管理クラスを1つのデヴァイスと見なして、そこからの
情報伝達をコールバックという形で見れば大体言いたい事は解って
もらえるかな?

 だから1タスクは他のヒット効果をもつタスク全てを知らなくても
自分と判定管理クラスとの相互関係のみで当たり判定を管理できるよ
うになる訳。719氏のタスクシステムの全貌は知らないけど、
HitMe( Task& enemytask , int layer );
Hit時にこーゆー関数がコールバックされば大体の処理は管理しきれる
し、パワーアップアイテムとかとの住み分けの汎用性も高いっしょ?

 次にレイヤー化する事によるメリットって?みたいな質問がくると
更に嬉しいんだけど。
0758名前は開発中のものです。02/03/19 11:42ID:???
レイヤー化する事によるメリットって?
0759名前は開発中のものです。02/03/19 11:53ID:???
>758
オマエ755じゃないだろ
0760名前は開発中のものです。02/03/19 12:07ID:???
うん
0761名前は開発中のものです。02/03/20 01:06ID:OKAtcaEA
>>757
 それより先に、コールバックで結果を通知することのメリットの方が聞きたい。
(レイヤー化のメリットは、わかる。俺もそうしよ(w )
 当たり判定管理クラスにAddHitData(Task &, int) IsHit(Task &, int) ってなメソッド
を用意して、前者で当たりの登録(これはTaskスタート時1回でいい)、でIsHitで結果
を知るってな方が自然だと思う。
 コールバック方式って、やっぱ、HitMeの時には登録が行われるだけで、タスク処理
が全部終わったあと、当たり判定管理クラスの実際に判定を行うメソッドを呼んでやる
と、その中でコールバックが呼ばれる感じだよね?となるとコールバックの中で進入禁
止の処理やら、アイテム拾ったときの処理やら、攻撃受けたときの処理やら全部やら
なきゃいけないわけで、そういう処理はタスク処理の中に書けた方がわかりやすいので
は?
0762名前は開発中のものです。02/03/20 01:11ID:???
>>761
本筋からずれるが、なんでもかんでも Task クラスに突っ込まずに、

- 当たり判定関係のメソッドだけ抽出したインターフェースを用意
 (純粋仮想関数だけ定義したクラスを容易)
- タスク実装クラスに、そのインターフェースを多重継承

の方が、柔軟性が増すぞ。
076376102/03/20 01:15ID:OKAtcaEA
あ、わかった。タスク処理の中であたり判定処理しちゃうとタスクの実行順で
当たり判定データが現フレームの物だったり前フレームの物だったりしちゃう
からか。
0764名前は開発中のものです。02/03/20 01:20ID:???
>>761
757 じゃないが、メリットは

1 当たり判定順序をタスク実行順序と切り離せる

2 タスクの状態変化のタイミングと、タスク実行のタイミングを分離できる
 タスクを順次読んでる途中で、タスクの状態が変わって、タスク管理クラス側
 にも影響を与える(たとえばタスクを殺して管理クラスから削除)場合には、
 よくよく考えないと dangling pointer を参照するバグが出る

3 特殊な判定(特定のキャラクタにのみ当たり判定があるとか)を付け加える
 ときに、その処理が各タスクに分離せずに、管理クラスで一括して処理でき
 る

っつーあたりじゃないか。

当たり判定はともかく、タスク実行と描画は実行順序を独立に定義するために、
分離するのが普通だよな。
0765名前は開発中のものです。02/03/20 01:21ID:OKAtcaEA
>>762
多重継承より、オブジェクトコンポジションの方が良いと思われ。
0766名前は開発中のものです。02/03/20 01:27ID:???
>>765
インターフェースの多重継承と、実装の多重継承を勘違いしてないか?

コンポジションだと、タスク内部の情報にアクセスするのが難しくなるぞ。
076776102/03/20 01:33ID:OKAtcaEA
>>764
>当たり判定はともかく、タスク実行と描画は実行順序を独立に定義するために、
>分離するのが普通だよな。
うい、そうですな。

 俺のタスクシステムの場合、タスク処理本体を二つの関数に分けてあって、基本
的な処理は1番目の関数をオーバーライドして定義し、実行順の問題がある処理
は2番目の関数をオーバーライドして、ってな感じでやってるんですが、これだと、
2つじゃたんない、3つ必要!ってな事になったらおいそれと足せない。
 なんかもっといい実装例ないかなぁ。それか、実行順に依存させたくない処理は
ぜんぶコールバックでやるべきなんだろうか?(この方式にこだわるのは、処理の
流れがソースからわかりやすい、から)

#ちなみにその二つのメソッドはanimate、renderという名前。renderといいつつ、
当たり判定とそれによる位置変更もやる……名前と内容が一致してない……。
076876602/03/20 01:38ID:???
実例を挙げた方が良いか。

struct hittable {
  virtual void get_hit_rect(rect&, hit_type&) = 0;
};

class player_task : public hittable
{
  bool invisiable_;
public:
  void get_hit_rect(rect& rc, hit_type& htype)
  {
    if (invisible_)
      rc = INVALID_RECT;  // 不可視状態の時には、当たり判定なし
    else
      ...
  }
};

インターフェースの多重継承を利用すれば、こんな感じでタスクの内部状態
を利用して当たり判定を変更することが、容易に可能になる。コンポジション
で同じ事をやろうとしたら、思い切り不自然なコードを書くことになるぞ。
0769名前は開発中のものです。02/03/20 01:40ID:???
>>766
ん、勘違いしてた。
ああ、762が言っている方式では、タスクマネージャーの方から当たり判定用の
メソッドを呼び出して当たり判定を実現するって事なのかな?(柔軟、か?)
077071902/03/20 01:52ID:3YnMUW/P
winのような、イベント駆動型とかをイメージすればいいのかな?
いやー。正直、よくわからんスw避けてました。
勉強してきます。

>719氏のタスクシステムの全貌は知らないけど、
HitMe( Task& enemytask , int layer );
Hit時にこーゆー関数がコールバックされば大体の処理は管理しきれる
し、パワーアップアイテムとかとの住み分けの汎用性も高いっしょ?

自分が今実装してる方法は、
UpdateFrame()、(全タスクに対するAI処理等)
EndScene()、(全タスクのキルスク、チェンジタスク要求チェック、実行)
Draw()、全描画
てな感じです。
判定は、キルタスク同様、大まかな範囲チェックに引っ掛かったものを、
EndScene()内でさらに判定、て感じで考えてました。

で、判定関数は、矩形,球ぐらいを用意しといて、タスククラスに
持たせようと思ってました。

>次にレイヤー化する事によるメリットって?みたいな質問がくると
更に嬉しいんだけど。

それじゃ、
次にレイヤー化する事によるメリットって?
0771名前は開発中のものです。02/03/20 01:54ID:???
>>767
> 実行順に依存させたくない処理は ぜんぶコールバックでやるべきなんだ
> ろうか?
そう思う。

上のほうで挙がってた DxApp だけど、あれは描画とタスク実行を切り離して
別々の優先順位で処理するようになってる。

 タスク関係  ITask, CTaskManager
 描画関係 IObject, CObjectManager

が対応するクラスなんで、読むと参考にはなるかも。

(あと優先順位無し、登録順で処理する IObjectCommand っつーのもある)
0772名前は開発中のものです。02/03/20 01:56ID:OKAtcaEA
 >>768 の方式だと、オブジェクトごとに当たり判定の実装が必ず
違うのであれば問題ないけど、このオブジェクトとこのオブジェクトは
実装一緒ってな事が多いとコピペが氾濫したり、もう一個クラス作って
へんちくりんなメッセージングすることになったりしない?

 俺は757のいう当たり判定管理クラスってのを導入した方が
スマートだと思うから。
 メンバとして持ったhittableのインスタンスの参照を当たり判定
管理クラスに丸投げしてやるだけでいいと思うので、複雑にはな
らないと思う。
0773名前は開発中のものです。02/03/20 02:05ID:???
>>772
> このオブジェクトとこのオブジェクトは実装一緒ってな事が多いと
そのときには template を使って Mixin すれば OK。

っつか 768 も

- 当たり判定管理クラスを作り
- そこに当たり判定チェックを行うインスタンスを登録
- 管理クラスから、各インスタンスをコールバック的に呼び出す

のは同じだけど。設計方針は変わらず、たんに C++ で都合がいい実装方法を
紹介しただけ。(コールバックの代わりに多重継承を使うと this の受け渡しが楽
だぜ、という程度の話)
077476702/03/20 02:11ID:OKAtcaEA
 俺のは2つだけど、>>770 のは3つに別れている、って事なのね(w
 こうなるとじゃぁ4つに分割、5つに分割と仕様要求次第じゃきりがない
って話になるな。
 タスク処理メソッドを可変長リストで持って、とかすると無駄に複雑に
なりそうだから、>>771 の言う通り、「実行順に依存させたくない処理は
ぜんぶコールバックでやるべき」なのかも。

>上のほうで挙がってた DxApp だけど、あれは描画とタスク実行を切り離して
>別々の優先順位で処理するようになってる。
 フロー制御はタスク機構だけで十分、というのは強引かな、やっぱ。(あ、俺
のシステムだとanimate、renderメソッドの呼び出しそれぞれに別々の実行順を
設定できるから、同じ事が実現できてはいる)

>>773
>> このオブジェクトとこのオブジェクトは実装一緒ってな事が多いと
>そのときには template を使って Mixin すれば OK。
あ、なるほど(w
0775名前は開発中のものです。02/03/20 02:17ID:OKAtcaEA
>>773
>(コールバックの代わりに多重継承を使うと this の受け渡しが楽
だぜ、という程度の話)
たしかに、関数のポインタを渡すより、オブジェクトのインスタンス
渡した方がいいですな。

 つまり、基本はタスクでフロー制御して、その実行順に依存したく
ない物は全部GoFの言うところのCommandパターンでやるのが吉、
ってことかな?
0776名前は開発中のものです。02/03/20 02:29ID:???
>>772
C++ 限定の手法だが、template を使った Mixin の例。

template <class T>
struct hittable_invisible : public hittable
  void get_hit_rect(rect& rc, hit_type& htype)
  {
    T& obj = static_cast<T&>(*this);
    if (obj.invisible_)
      rc = INVALID_RECT;
    else
      ...
  }
};

class player : public hittable_invisible<player>
{
  bool invisible_;
public:
  ...
};

効率と汎用性、そして型安全性を全て損なわずに実現してる。ペナルティは
コードサイズの増大。
077771902/03/20 02:47ID:3YnMUW/P
>>774
3つ?ですか?

大まかな流れはさっき書いたものなんですが、言葉足らずだったかも。
ちょっと細かく言うと、
UpdateFrame()は,ゲームアプリ、タスクマネージャ、
描画エンジン、vプリミティブ(描画)、v各TCB、v各タスククラスが持ってて、
vがついてる奴が仮想関数です。

EndScene()は今の所、託すマネージャのみのメンバ間数です。

Draw()はタスク処理とは関係ないです。
こちらは描画エンジン、各(と言っても今実装してるのはスプライトのみ)
プリミティブクラスで使ってます。

タスククラスは、
tcbBase<-tcbSprite(今これだけ)
tskBase<-各キャラタスク となってて、
tcbSpriteのメンバにVectorでtskを保持して、チェンジタスクで
切り替えています。

tcbSpriteが、(スプライト)プリミティブからリソースを貰って
UpdateFrameで更新してます。

描画エンジンの方にも、UpdateFrameはあって、アニメ処理、タスクが
弄った頂点データをビデオボードに転送したりしてます。
0778名前は開発中のものです。02/03/20 02:51ID:???
>>757
話が落ち着いたところで、そろそろ「レイヤー化する事によるメリット」を
語ってくれると嬉しいトコロだが。

そもそも「レイヤー化」を、どういう意味で使ってるのか分かってなかったり
するので、単語の定義からお願いします。
0779名前は開発中のものです。02/03/20 03:03ID:???
>>777
 ああ、タスク毎の処理は1種類で、当たり判定の処理(タスク実行順に
依存したくない処理)はタスクマネージャーがグローバルに行う、ってわ
けですね。
(´ー`)。oO( 本当、人それぞれだなぁ ) 
0780名前は開発中のものです。02/03/20 06:31ID:9RjuXxJB
このスレの最初のほうにCとC++ではCの方がC++で作ったときよりも
実行速度が速い、もしくは同等と書かれていたんだけど、
ヘボプログラマはCで作っておいたほうが無難ということなの?
そこで言われているCとC++との違いはクラスの使用から来るもの?
0781名前は開発中のものです。02/03/20 07:15ID:???
>>780
実際にわずかなオーバーヘッドがあるし、まずい設計では
暗黙のコンストラクタ/デストラクタによって積み重なって遅くなる。
C++についてよく理解してないと、ただ遅いだけの道具になるよ。
078278102/03/20 07:18ID:???
C と C++ の違いは主にクラスによる。
同一な C プログラムなら C++ でコンパイルしても
速度はほぼ同一なはず。ライブラリとか最適化の具合にももちろんよるけど。
0783名前は開発中のものです。02/03/20 11:13ID:???
>>780
書籍「Inside the C++ Object Model」で詳細に解析してるから、それを
読むのがいいと思うが。

メンバ関数(メソッド)を呼び出す場合に this が暗黙のうちに渡されたり、
仮想関数を呼び出すと間接参照が何度か(一般には 2 回)入ったりす
る。ただ、このあたりのことは C でも同じ処理をやろうと思ったら、

 関数の引数として、構造体へのポインタを渡す
 関数ポインタを使う

必要があるから、ホントは変わらないんだけど。

> 暗黙のコンストラクタ/デストラクタによって積み重なって遅くなる。
synthesize constructor / destructor のことを言ってるんだと思うが、
それなら C だって「初期化処理を入れたら遅くなる」というのと同じ。
trivial constructor / destructor で済むクラスなら、初期化・終了処
理を省くことも出来るし。

C でも C++ でも「同じ処理をさせよう」と思うと、実際には「同じだけの
コストがかかる」ことになる。ただ C++ では、synthesize constructor/
destrurctor や temporary object のように

 コンパイラが、見えないところでコードを付け加える

ことがあるから、そこを意識していないと、たった数行のコードなのに
コンパイルすると数千命令に展開されていた、ということが有りうるの
で注意が必要。
0784名前は開発中のものです。02/03/21 00:42ID:I/OXI3by
>>778
 フォトショップのレイヤーを思い浮かべると良いと思われ。
 当たり判定を取る当たり同士をグループ化して、グループに番号を付けておく。

 進入禁止系のあたりをレイヤー0番に置くとして、壁、NPC、敵キャラのあたりは
HitMe( ???, 0)と呼び出してやる。プレイヤーオブジェクトはそれが壁なのかNPCな
のか区別せずにすむ。
 で、レイヤー1番はアイテム系の判定として、アイテムオブジェクトとプレイヤー
オブジェクトだけが所属する。そうすればNPCオブジェクトの方で接触のあった
オブジェクトがアイテムだったら何もしない、なんてコードを書かなくて済む、と。

 こんな感じ?
0785名前は開発中のものです。02/03/21 11:11ID:???
すげぇ、この板に役に立つスレがあったのか。
0786名前は開発中のものです。02/03/21 13:39ID:???
>>784
なるほど。参考になったよ。
0787名前は開発中のものです。02/03/22 10:21ID:???
レイヤー化って、OS−ミドルウェア−アプリケーションみたいなプログラムの階層化と思ってたけど、違ったのね
0788名前は開発中のものです。02/03/22 23:51ID:???
>>784
 すまそん。マスター入れてやっと家に帰って来れました。

 レイヤー化のメリットはまさに784さんの言うとおり。汎用的な当たり判定管理クラスを
1つ作ってあとはゲームデザイン次第でプログラマが自由にレイヤーを利用みたいな感じね。

 更なるメリットとしてヒットアルゴリズムの分離ってのもありまして。
「矩形の重なりによるヒット判定」
「球形や線分との距離による判定」
「背景物のようなn次元マップデータとの判定」
「ランドスケープやポリゴン等のベクトルデータとの判定」
など、これまたゲームデザインの要求に応じて利用/拡張すればいいと。
はっきりいってこの機構自体は1個作っちゃえば3Dゲームにもバリバリ
に応用できるし、斑鳩みたいな2Dの3Dの混在なんかも結構簡単に出来
るよね?

 ただまぁ、C++としての実装は自分はヘタッピなのでだれか上手い実装例
をみせてくれないかなーと。テヘッ。
0789名前は開発中のものです。02/03/23 01:20ID:???
コールバック関数による当たり判定って

全キャラクタのタスク更新

判定管理クラスの関数を呼び出し→各キャラクタのタスクのコールバック関数呼び出し

描画

みたいな流れかと思ったんですが、
コールバック関数内でタスクの状態(位置など)を変化させられないと、
キャラクタ同士めり込んだ状態とかで描画されてしまいますよね?
その辺はどう処理されてるのでしょうか?
0790名前は開発中のものです。02/03/24 05:59ID:WWKNmPMo
良スレなのでage
0791 02/03/24 09:15ID:DgRqu+K2
C++なんぞOOPLではないのでsage
0792名前は開発中のものです。02/03/24 11:04ID:???
>>788
なるほど、それは便利だ。
ちゅうか今、当たり判定で困ってるところなんだわ。

>>791
すでにそんなことはどうでもいい
0793名前は開発中のものです。02/03/24 11:12ID:???
>791
マ板で語り尽くされた事を隔離板で吠えられてもナァ...
つーか、sagaってねぇし(藁
0794名前は開発中のものです。02/03/25 12:06ID:???
>>789
俺の場合、基本的にあたり判定の中では当たったかどうかの判定だけで
めり込んでも無視です。
当たり中に状態変化(移動、フラグ立てなど)を行うと実効順によってそれ
以降の判定が異なってしまうからです。
どうしてもやりたい場合、完全な例外処理ですね。
こいつはこういう処理だから気をつけるとかコメント書いてます。
つーか、うまい処理が思いつかん。
0795名前は開発中のものです。02/03/31 00:07ID:???
このスレに影響されて、当たり判定管理クラスを構築中age
0796名前は開発中のものです。02/03/31 06:17ID:???
頑張れー&公開キボン。
0797名前は開発中のものです。02/03/31 08:22ID:???
当たり判定はその処理のタイミングも必要なので、
それ単体をクラスで定義しただけでは難しいと思うのだが
何かいい解決手段あったらよろしく
0798名前は開発中のものです。02/03/31 09:34ID:???
>>764
それだと、当たり判定の種類毎にレイヤー用意しなきゃいけなくなって面倒じゃない?

079979802/03/31 09:40ID:???
>>764
じゃなくて
>>784
でした。
0800名前は開発中のものです。02/03/31 11:32ID:???
800get
0801関係ないが02/04/01 00:49ID:z86bt0A7
最近C++でゲーム開発しているんだが、ヘッダーファイルをincludeするだけで
最終コードのサイズが増えるという現象に遭遇して思いっきり萎え。
コンパイラのバグだがあまりにもひどいっす。
Cならこんなことはならないよなぁ・・
0802名前は開発中のものです。02/04/01 00:56ID:???
どのコンパイラ?
0803名前は開発中のものです。02/04/01 01:01ID:???
CWだな…。オレも経験済み。
0804名前は開発中のものです。02/04/01 01:35ID:???
参照してないシンボルもリンクしちゃうってこと? >>801
080580102/04/01 02:25ID:z86bt0A7
g++なんだがCWでもあるんか・・。
classの中のstaticなinline関数が関数ローカルのstatic変数を持つコードが
あると、その関数を一度も使わなくともコード、データ領域ともその翻訳処理
単位で展開されてしまうんだよ。いわゆるシングルトンパターンの構造をもつ
ヘッダファイルをインクルードするだけで飛躍的にコード、データサイズが増加する
罠。
やっぱC++は組み込みには向いてないよな・・・。
0806名前は開発中のものです。02/04/01 02:31ID:???
組み込みだからって、そんな関数なんでinlineにすんだよ?>>801
0807名前は開発中のものです。02/04/01 02:34ID:???
>>805
ちなみに gcc のバージョンいくつ?
080880102/04/01 02:34ID:z86bt0A7
class foo {
static foo& Instance() {
static foo bar;
reurn bar;
}
}

これだけですが何か?
080980102/04/01 02:36ID:z86bt0A7
>>807
2.9-9911、2.96-1003、2.96-1003-1で確認したよ。
0810名前は開発中のものです。02/04/01 02:58ID:???
>>808
parse error だぞ(w まぁ、言いたい事は分かるから問題ないけど。

今、手元の gcc 2.95.3 (cygwin-special) で

class foo
{
public:
  static foo& Instance()
  {
    static foo bar;
    return bar;
  }
};

void func() { foo s; }

だけのコードを g++ -S でコンパイルしてみたが、確かに .lcomm セクションに
変数 bar が確保されちゃうね。Borland C++ Compiler 5.5.1 や Visual C++
6.0 だと

警告 W8027 singleton.cpp 6: <静的変数>を含む関数はインライン展開できない(関数 fo
o::Instance() )
singleton.cpp(15) : warning C4514: 'Instance' : 参照されていないインライン関数は削除
されました。

となって、いずれのケースでも出力されたアセンブリコードに bar は存在しない。
0811名前は開発中のものです。02/04/01 03:06ID:???
>>801
inlineにしなくてもまともな実現方法がいくらでもあるんだから、
> C++は組み込みには向いてない
なんてことにはならんだろ。
0812名前は開発中のものです。02/05/16 11:00ID:3bT9vj92
保守ageついでに。上っ面撫でてるだけの記事だけど。

GameDev.net - Object-Oriented Scene Management
http://www.gamedev.net/reference/articles/article1812.asp
0813名前は開発中のものです。02/08/15 13:27ID:EIasLKDH
保守age

OOのゲームプログラミングへの応用ということで、シリアライズの話を。
いつでもどこでもセーブできて、その場の状況を完全再現するには
シリアライズしかないと思うが、やったこと無いので良くわからず。

MFCなんかでやれるオブジェクトの動的生成って全部自前で実装する場合
どうやったら良いんだ?
0814名前は開発中のものです。02/08/15 14:23ID:???
クラスファクトリを作って、
IDとクラスを1:1でマッピング。
読み込み時にIDによって生成するクラスを決める。
0815名前は開発中のものです。02/08/15 14:47ID:???
>>813
> MFCなんかでやれるオブジェクトの動的生成って全部自前で実装する場合
> どうやったら良いんだ?
エピステーメーの「オブジェクト指向的日常」って本で、簡単な解説と実装例を
紹介をしてた。(初版ではコードに致命的なミスがあったりしたが……)
0816名前は開発中のものです。02/08/15 14:57ID:???
生成プログラムをスマートに抽象化するとしたら?
0817名前は開発中のものです。02/08/15 17:33ID:???
>>816
クラスファクトリを static オブジェクト化して、別の static オブジェクトに登録。
main が走り始めた段階で準備完了、としておく。
0818名前は開発中のものです。02/08/15 17:58ID:???
簡単な実装例キボン
0819名前は開発中のものです。02/08/15 19:30ID:???
>>818
マジメに書くと長くなるんだが…。昔のコードから抜粋すると、こんな感じ。

// 永続オブジェクト基底クラス
class CPObject {
private:
  static const BYTE bytMagic[3];
  static const UINT MAGICLEN;

public:
  virtual ~CPObject() {}
  virtual BOOL equalsTo(const CPObject* obj) const {
    return this == obj;
  }
  virtual BOOL saveIt (FILE*) const = 0;
  virtual BOOL restoreIt(FILE*) = 0;
  void save(FILE*) const;
  static CPObject* restore(FILE*);

  virtual UINT isA() const = 0;
};
0820名前は開発中のものです。02/08/15 19:31ID:???
// 永続オブジェクト ID 辞書クラス
class CPIDDict {
private:
  struct CPIDRec {
    UINT id;
    CPObject* (*func)();
  };
  CPIDRec* array;
  UINT size;
  UINT cap;
  CPIDDict(const CPIDDict&);
  CPIDDict& operator=(const CPIDDict&);

public:
  CPIDDict();
  ~CPIDDict();
  static CPIDDict* theCreator();
  void regist(UINT, CPObject* (*)());
  CPObject* create(UINT) const;
};
0821名前は開発中のものです。02/08/15 19:32ID:???
// ファクトリを ID 辞書に登録する
// クラス実装ファイル (*.cpp) の先頭に記述
#define REGISTER_CPOBJECT(X, ID) \
static CPObject* X ## creator() { return new X; } \
class X ## Creator { public: X ## Creator(); }; \
X ## Creator::X ## Creator() {\
    CPIDDict::theCreator()->regist(ID, X ## creator);\
};\
static X ## Creator X ## dummy __UNUSED__;

// 永続オブジェクト関連メソッドの宣言
// クラスへッダファイル (*.h) の public セクションに記述
#define DECLARE_CPOBJECT(ID) \
virtual BOOL saveIt(FILE*) const;\
virtual BOOL restoreIt(FILE*);\
virtual UINT isA() const {\
    return ID;\
}

#define RESTORE_CPOBJECT(TYPE, FP) \
    (dynamic_cast<TYPE>(CPObject::restore(FP)))
0822名前は開発中のものです。02/08/15 22:55ID:LqMXg9lL
class Class { public: virtual ~Class(){} };
typedef int ID;
class IGenerator;
class ClassFactory : private map<ID,IGenerator*>
{
typedef map<ID,IGenerator*> Base;
public:
bool can_create( const ID& id ) const { return ( Base::find( id ) != Base::end() ); }
void insert( const ID& id , IGenerator* const p ) { Base::insert( make_pair( id , p ) ); }

template<class CLASS>
auto_ptr<CLASS> create( const ID& id ) const
{
Base::const_iterator const found = Base::find( id );
return auto_ptr<CLASS>( ( found != Base::end() && found->second )
? dynamic_cast<CLASS*>( found->second->Generate().release() )
: 0 );
}
};
ClassFactory& getClassFactory() { static ClassFactory instance; return instance; }
struct IGenerator
{
IGenerator( ID id ) { getClassFactory().insert( id , this ); }
virtual ~IGenerator(){}
virtual auto_ptr<Class> Generate() = 0;
};
0823名前は開発中のものです。02/08/15 22:55ID:LqMXg9lL
class MyClass : public Class
{
public:
static const ID key;
MyClass(){}
private:
struct Generator : IGenerator
{
Generator() : IGenerator( MyClass::key ) {}
auto_ptr<Class> Generate() { return auto_ptr<Class>( new MyClass ); }
};
static Generator generator;
};
const ID MyClass::key = 12345;
MyClass::Generator MyClass::generator;
0824名前は開発中のものです。02/08/15 22:57ID:???
int main()
{
cout << "create MyClass by correct key... ";
auto_ptr<MyClass> p1 = getClassFactory().create<MyClass>( MyClass::key );
cout << ( p1.get() ? "ok" : "ng" ) << endl;

cout << "create MyClass by wrong key... ";
auto_ptr<MyClass> p2 = getClassFactory().create<MyClass>( 100 );
cout << ( p2.get() ? "ok" : "ng" ) << endl;

return 0;
}
0825修正02/08/15 23:05ID:???
template<class CLASS>
auto_ptr<CLASS> ClassFactory::create( const ID& id ) const
{
Base::const_iterator const found = Base::find( id );
if( found != Base::end() )
{
IGenerator* const p = found->second;
if( p )
{
auto_ptr<Class> pbase = p->Generate();
CLASS* const ptest = dynamic_cast<CLASS*>( pbase.get() );
if( ptest )
{
pbase.release();
return auto_ptr<CLASS>( ptest );
}
}
}
return auto_ptr<CLASS>();
}
0826あぼーんNGNG
あぼーん
0827名前は開発中のものです。02/08/16 00:57ID:???
シリアライズ=排他処理
0828名前は開発中のものです。02/08/17 20:32ID:???
尻穴is=排泄処理
082981302/08/18 17:49ID:???
おお、解説&ソースうpありがとうございます。
(って、まだ全部ちゃんと読んでないですが)

ちょっくらやってみるか。ってシリアライズなしでやってたものに
シリアライズつけるの厳しそうだな……(いちおうスーパークラスはあるんだけど)
0830名前は開発中のものです。02/10/06 17:36ID:4VoU6WQ6
>>828

ワラタ
0831名無しさん02/10/06 17:40ID:???
あ3w45えrgtyくぉえぴVん:http://genie.gaiax.com/home/nakatai
HGBTR4MHGBP0T5え@ご9JR4QPV「Jる5VG
DB5VHG4ヴぇR4TPヴぇR5MHGBTじhttp://genie.gaiax.com/home/nakatai
http://genie.gaiax.com/home/nakatai
さDRGヴぁでRGBはえ;んごいえろげいR
あえRSHGべSDRHDSVGDRFせVDRFせVせDFGVRFせDヴぇDRFSげDFS
http://genie.gaiax.com/home/nakatai

0832あぼーんNGNG
あぼーん
0833名前は開発中のものです。02/10/28 04:08ID:???
ほしゅ
0834名前は開発中のものです。02/11/03 01:14ID:???
OOってそれぞれの人で考えてる程度が違うから、
チーム開発に適用するのは難しいよね。
0835あぼーんNGNG
あぼーん
0836名前は開発中のものです。02/11/03 01:35ID:???
>>834
> OOってそれぞれの人で考えてる程度が違うから、
プログラミング自体それぞれの人で考えてる程度が違う
0837名前は開発中のものです。02/11/03 10:51ID:ZJseP0w7
なんつーか、あれだよ。
まずはヘッダファイルの一括インクルードする奴は駄目な奴だと
いうことを同僚にたたき込む方法を教えてくれ。
0838名前は開発中のものです。02/11/03 10:59ID:???
>>837
どいうレバルで?VCとかだとコンパイル時間早くするためにやったりするし。
ライブラリレイヤーなんか#include "KaisyaLib.h"で済ませちゃうのが普通じゃない?

アプリケーションレイヤーでそれやられると結構泣けるな。
0839あぼーんNGNG
あぼーん
0840名前は開発中のものです。02/11/03 11:48ID:???
てことはstdafx.hもアウトか?
0841名前は開発中のものです。02/11/03 16:12ID:???
>>837
そいつは、ずっと一人(多くてせいぜい2人)で開発をしてきたか
もしくはソースコードの管理はその一人で(しかも人力で)やってきたか
多人数で効率よく開発する仕組みを全く知らなくても良い立場なんだろ。
 
 
他のモジュールとの依存関係を把握してない。orしたくない。orする必要ない。
  ↓
とりあえず全部インクルードしちゃえ。単体テスト不能?知るかボケ。
  ↓
いきなり結合テスト。make時にリンクされるモジュールが具体的に
どれなのか勿論分かってない。まぁ他のモジュールは全て安定版と
確信してるから何も問題ないね。根拠?知るかボケ。
  ↓
不具合を確認。デバッグ作業開始。3日経過。あ、ボスが来た。
「ボス:どうかね、進捗のほうは」「漏れ:はい順調です(苦笑)」
084284102/11/03 16:20ID:???
複数メンバーとの並行作業が事実上不可能。
 
実際にやったら
あちこちに(作業上の)クリティカルセクションがありそうだ。
ある一人のデバッグ作業が終了するまで他の全ての人間の作業が
停止することが日常茶飯事みたいな。
 
的外れだったらスマン。
0843名前は開発中のものです。02/11/03 16:30ID:???
>まずはヘッダファイルの一括インクルードする奴は駄目な奴だと
これがよくわからんが、プロジェクト中の自分が使わないヘッダファイルまでインクルードするってことか?
0844名前は開発中のものです。02/11/03 17:06ID:???
開発当初から最後まで一貫して手を入れずに使い続けるようなものは
ある程度の単位にパッケージ化して(安定した)基本セットとして渡す
ことはよくあるな。
 
もっともそれは#include <xxxx.h>がずらーりと並ぶようなヘッダファイルを
用意するということではなく、リリース用の.oファイルと.hファイルのセットを
用意(というか自動生成)するという意味だが。
0845名前は開発中のものです。02/11/03 17:06ID:???
VCなら.libと.hか。
0846名前は開発中のものです。02/11/03 18:46ID:???
>>843
それやられると、一つのヘッダを書き換えただけで常にフルビルド状態に
なっちゃうんだよね。特に、前方宣言ですむところを #include するのは
犯罪だ。
0847名前は開発中のものです。02/11/03 19:14ID:???
VC++ならプリコンパイル済みヘッダがあるので
コンパイル時間については心配いらないんだけどね。

ヘボいコンパイラしかない環境って悲惨だね。
0848名前は開発中のものです。02/11/03 20:55ID:rqxcf8ww
837ですけど。
841さんと842さんのいう状況そのまんまで爆笑しました。
なんつーか説得は不可能ですよね。
なにいっているかわからない人はわからなくていいです。
そのほうが幸せだと思います。
0849名前は開発中のものです。02/11/03 20:56ID:???
>>847
意地の悪い質問で恐縮だが、ぜひ質問させてくれ。
 
00.cpp
01.cpp
02.cpp
03.cpp
(中略)
fe.cpp
ff.cpp
の先頭部分でそれぞれ
#include "00.hpp"
#include "01.hpp"
#include "02.hpp"
#include "03.hpp"
(中略)
#include "fe.hpp"
#include "ff.hpp"
という具合にインクルードしてたとして
00.hppを書き換えてmakeしても時間を気にする必要がないのか。
 
本当か?
085084902/11/03 21:08ID:???
いや、ゴメン。訂正。
 
#include "00.hpp"
#include "01.hpp"
#include "02.hpp"
#include "03.hpp"
(中略)
#include "fe.hpp"
#include "ff.hpp"
 
という内容のmarugoto.hppなるヘッダファイルを用意。
で、全ての.cppファイルの先頭で

#include "marugoto.hpp"

みたいな感じかな。スレの流れ的にいうと。
0851名前は開発中のものです。02/11/03 21:10ID:???
>>849-850
そのケースだと、最初の.cppをコンパイルするときに
プリコンパイル済みヘッダーが作られて(marugoto.hppを指定する)、
それ以降はそのヘッダを使いまわすことになります。
つまりmarugoto.hppとそのファイルが参照するヘッダファイルの読み込み&処理は
一度しか走らないんです。想像以上に速くなりますよ。

自分の経験ですが、30万行のC++コードのビルドが2分で済みます(P3-800)。
.cppが700ファイル、.hが800ファイルでした。
0852名前は開発中のものです。02/11/03 21:17ID:???
DelphiやJava信者から言わせると、CやC++のインクルードファイルっていう仕様が腐ってるだけなんだよね。
■ このスレッドは過去ログ倉庫に格納されています