デバッグの果てに辿り着いた真相は
こんばんは、チームダイコンです。相変わらずの夜の更新となります。
前回の記事でデバッグが停滞していることについて少し触れましたが、日を跨いでやっと解決しました。
もちろん、解決に到っては先生の力をお借りしました。もし一人だったら恐らく一週間以上掛かっていたでしょうね……。
単純では有りましたが、思い入れ深いことではありますので、この場を借りて事の詳細をお伝えしようと思います。
酷い長文になります。画像ありません。動画ありません。申し訳ないです。それでも書きます。書かせて下さい。
今回直面していたのは、悪名高い、ポインタに関するエラーでした。
そして作成していたプログラムについて部分的に抜き出すと、 GameScene → ObjectManager → Player という形でポインタを保持していました。
具体的な処理順は、以下の通りです。
【ゲームスタートを押した後の処理順】
1. GameSceneのコンストラクタ開始
2. ObjectManager.cppのコンストラクタ開始
3. ObjectManager.cppのコンストラクタ終了
4. GameSceneのコンストラクタ終了
5. Gameのinitialize開始
6. ObjectManagerのinitialize開始
7. Playerのコンストラクタ開始
8. Playerのコンストラクタ終了
9. Playerのinitialize開始
10. Playerのinitialize終了
11. ObjectManagerのinitialize終了
12. Gameのinitialize終了
13. その他処理に続く……
エラーは上記の 7. あたりで起きていると推測されました。具体的には『 player_m = new Player(); 』という文です。
実際、この文のみをコメントアウトすることで、処理は問題なく続行されることも確認できました。
また、不思議なことではありますが、『 player_m = new Player(); 』を『 Player* player_2_m = new Player(); 』と置き換えても問題なく続行できました。
メンバ変数を使う代わりに、その場で宣言したメソッド内のローカル変数を使うだけであって、処理は同じはずなんですが……これは特に疑問を強めました。
その後、さらにデバッグを進め、ステップインで一文ずつ実行して行くことで、詳細に、どこの処理で止まるのかを突き止めようとしてみました。
結果、Playerのコンストラクタが完了( newが終了 )し、代入演算に入った所でエラーが起きました。
さて、これらのことから、すっかり player_m にアクセスできないのか? それはなくとも、この周辺がおかしいのか? という方向に考えが進んだことは、言うまでもありません。
ここから、泥沼に足を踏み入れていくこととなったのです。
……実際の原因はというと、至極単純なことにも、ObjectManager の initialize を呼ぶ時に使ったポインタが、初期化されていないというだけのことでした。
何故そのような当然の部分に目がいかなかったのか……言い訳にはなりますが、デバッグ時、ObjectManager の initializeは実行されていたのです。
いえ、正常に実行されていたかは分かりませんが、前述した通り、ObjectManager の initialize 開始後から少し進んだところ、Playerのコンストラクタ終了直後にプログラムは止まったのです。
この時点で、もし 前述のObjectManager*型ポインタが初期化されていなければ、initialize 開始時点で止まるだろうと思い込んでしまっていたので、そこに目を向けることをしなかったのです。
もっと言うと、ポインタが初期化されているかどうかなどの確認方法をよく分かっていなかったということもありました。
先生が普通にやっているのを傍から見て、そんなことできるのか! と、一人でデバッガを胸中で称賛していました。
結局、ソースコードとエラー情報を見せたところで、先生が素早くポインタに目をつけ、中身をチェックして下さったので、初期化されていないことが分かり、問題は解決しました。
初期化してないかどうかくらい、ざざっと見ても分かるだろう、ということですが、実は初期化しているつもりになっている文が以下のようになっていまして。
『 objectManager_m->ObjectManager::getInstance(); 』
まあ、なんというか、物凄い奇特な文になっていたわけですが、逆に気づきませんでした。本来の文はこうです。
『 objectManager_m = ObjectManager::getInstance(); 』
シングルトンにはありがちな形です。
間違っている方の文が、文法チェックで検出されないことにはびっくりしましたが……よくよく見れば文の意味は通っているんですよね。凄まじくまどろっこしいというか、冗長、奇々怪々な文ではありますが。
あまりにもありがちなミスであっただけに、先生に指摘された際には力が抜けましたが、いろいろな要素が複合した結果、ミスリードされてしまった、という経緯があるので、自分的には仕方がなかったかも知れないと、合理化している部分があります。
とりあえず、『 まずは全ての変数の中身、特にポインタに注意してひと通り確認することが大切』 を結論、教訓、戒めとして、この話を締めくくろうと思います。
お疲れ様でした。
前回の記事でデバッグが停滞していることについて少し触れましたが、日を跨いでやっと解決しました。
もちろん、解決に到っては先生の力をお借りしました。もし一人だったら恐らく一週間以上掛かっていたでしょうね……。
単純では有りましたが、思い入れ深いことではありますので、この場を借りて事の詳細をお伝えしようと思います。
酷い長文になります。画像ありません。動画ありません。申し訳ないです。それでも書きます。書かせて下さい。
今回直面していたのは、悪名高い、ポインタに関するエラーでした。
そして作成していたプログラムについて部分的に抜き出すと、 GameScene → ObjectManager → Player という形でポインタを保持していました。
具体的な処理順は、以下の通りです。
【ゲームスタートを押した後の処理順】
1. GameSceneのコンストラクタ開始
2. ObjectManager.cppのコンストラクタ開始
3. ObjectManager.cppのコンストラクタ終了
4. GameSceneのコンストラクタ終了
5. Gameのinitialize開始
6. ObjectManagerのinitialize開始
7. Playerのコンストラクタ開始
8. Playerのコンストラクタ終了
9. Playerのinitialize開始
10. Playerのinitialize終了
11. ObjectManagerのinitialize終了
12. Gameのinitialize終了
13. その他処理に続く……
エラーは上記の 7. あたりで起きていると推測されました。具体的には『 player_m = new Player(); 』という文です。
実際、この文のみをコメントアウトすることで、処理は問題なく続行されることも確認できました。
また、不思議なことではありますが、『 player_m = new Player(); 』を『 Player* player_2_m = new Player(); 』と置き換えても問題なく続行できました。
メンバ変数を使う代わりに、その場で宣言したメソッド内のローカル変数を使うだけであって、処理は同じはずなんですが……これは特に疑問を強めました。
その後、さらにデバッグを進め、ステップインで一文ずつ実行して行くことで、詳細に、どこの処理で止まるのかを突き止めようとしてみました。
結果、Playerのコンストラクタが完了( newが終了 )し、代入演算に入った所でエラーが起きました。
さて、これらのことから、すっかり player_m にアクセスできないのか? それはなくとも、この周辺がおかしいのか? という方向に考えが進んだことは、言うまでもありません。
ここから、泥沼に足を踏み入れていくこととなったのです。
……実際の原因はというと、至極単純なことにも、ObjectManager の initialize を呼ぶ時に使ったポインタが、初期化されていないというだけのことでした。
何故そのような当然の部分に目がいかなかったのか……言い訳にはなりますが、デバッグ時、ObjectManager の initializeは実行されていたのです。
いえ、正常に実行されていたかは分かりませんが、前述した通り、ObjectManager の initialize 開始後から少し進んだところ、Playerのコンストラクタ終了直後にプログラムは止まったのです。
この時点で、もし 前述のObjectManager*型ポインタが初期化されていなければ、initialize 開始時点で止まるだろうと思い込んでしまっていたので、そこに目を向けることをしなかったのです。
もっと言うと、ポインタが初期化されているかどうかなどの確認方法をよく分かっていなかったということもありました。
先生が普通にやっているのを傍から見て、そんなことできるのか! と、一人でデバッガを胸中で称賛していました。
結局、ソースコードとエラー情報を見せたところで、先生が素早くポインタに目をつけ、中身をチェックして下さったので、初期化されていないことが分かり、問題は解決しました。
初期化してないかどうかくらい、ざざっと見ても分かるだろう、ということですが、実は初期化しているつもりになっている文が以下のようになっていまして。
『 objectManager_m->ObjectManager::getInstance(); 』
まあ、なんというか、物凄い奇特な文になっていたわけですが、逆に気づきませんでした。本来の文はこうです。
『 objectManager_m = ObjectManager::getInstance(); 』
シングルトンにはありがちな形です。
間違っている方の文が、文法チェックで検出されないことにはびっくりしましたが……よくよく見れば文の意味は通っているんですよね。凄まじくまどろっこしいというか、冗長、奇々怪々な文ではありますが。
あまりにもありがちなミスであっただけに、先生に指摘された際には力が抜けましたが、いろいろな要素が複合した結果、ミスリードされてしまった、という経緯があるので、自分的には仕方がなかったかも知れないと、合理化している部分があります。
とりあえず、『 まずは全ての変数の中身、特にポインタに注意してひと通り確認することが大切』 を結論、教訓、戒めとして、この話を締めくくろうと思います。
お疲れ様でした。
トライデントコンピュータ専門学校 ゲームサイエンス学科についてはコチラ!
by trident-game
| 2013-06-03 23:01
ゲームプログラミング体験入学
以前の記事
2019年 06月
2019年 01月
2018年 09月
2018年 07月
2018年 06月
2018年 04月
2018年 03月
2018年 02月
2018年 01月
2017年 12月
2017年 11月
2017年 10月
2017年 09月
2017年 08月
2017年 07月
2017年 06月
2017年 05月
2017年 04月
2017年 03月
2017年 01月
2016年 12月
2016年 10月
2016年 09月
2016年 08月
2016年 07月
2016年 06月
2016年 05月
2016年 04月
2016年 03月
2016年 02月
2016年 01月
2015年 11月
2015年 10月
2015年 09月
2015年 08月
2015年 07月
2015年 06月
2015年 05月
2015年 04月
2015年 03月
2015年 02月
2015年 01月
2014年 12月
2014年 11月
2014年 10月
2014年 09月
2014年 08月
2014年 07月
2014年 06月
2014年 05月
2014年 04月
2014年 03月
2014年 02月
2014年 01月
2013年 12月
2013年 11月
2013年 10月
2013年 09月
2013年 08月
2013年 07月
2013年 06月
2013年 05月
2013年 04月
2013年 03月
2013年 02月
2019年 01月
2018年 09月
2018年 07月
2018年 06月
2018年 04月
2018年 03月
2018年 02月
2018年 01月
2017年 12月
2017年 11月
2017年 10月
2017年 09月
2017年 08月
2017年 07月
2017年 06月
2017年 05月
2017年 04月
2017年 03月
2017年 01月
2016年 12月
2016年 10月
2016年 09月
2016年 08月
2016年 07月
2016年 06月
2016年 05月
2016年 04月
2016年 03月
2016年 02月
2016年 01月
2015年 11月
2015年 10月
2015年 09月
2015年 08月
2015年 07月
2015年 06月
2015年 05月
2015年 04月
2015年 03月
2015年 02月
2015年 01月
2014年 12月
2014年 11月
2014年 10月
2014年 09月
2014年 08月
2014年 07月
2014年 06月
2014年 05月
2014年 04月
2014年 03月
2014年 02月
2014年 01月
2013年 12月
2013年 11月
2013年 10月
2013年 09月
2013年 08月
2013年 07月
2013年 06月
2013年 05月
2013年 04月
2013年 03月
2013年 02月