debugではバッチリ。releaseしたら不具合が発覚。

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
土門

debugではバッチリ。releaseしたら不具合が発覚。

#1

投稿記事 by 土門 » 12年前

製作中のゲームが完成したんで、releaseしたんです。
exeファイルを実行すると、
デバッグモード中は問題無くまわっていた処理がまわらない。
キャラが動かない。
原因をさぐろうと思ってデバッグモードでまた実行すると
問題無くまわってくれるんです。
だから原因を突き止めにくい、、、。

以前製作したゲームも、release後に発覚したプログラムミスはありました。
結果は変数の初期化をしてなかったという単純なものだったのでよかったのですが、
今回はそんなもんじゃ無い予感がします。

releaseモードだけに生じてる不具合である場合、その原因に目星はつけられますか?
前回はデバッグモードは初期化に対して緩い、みたいなことをこちらで聞かせて頂きまして
なので初期化の類を確認していてミスを発見したんです。
今回は初期化では無さげで、目星がつけられず、困っております。。

ここからはちょっと愚痴ですが、
release後に発覚させるなんて意地悪だと思うんです。
プログラム打ちながら逐一releaseで確認しなきゃならないわけでも無し、
デバッグモードで散々確認しながら製作し、バッチリだということでいざ
releaseしたらミスが発覚させられるとか、非常に歯がゆいのですが。
デバッグモードでのvisualstudio側のチェックの緩さには理由があるんでしょうけど
ミスをフォローし、releaseまでひっぱられると
こっちはいつ書いたプログラムでそうなってしまったのか探すのが大変です。
そんなんだったら、厳しめに逐一、増やした処理に問題や漏れがあれば
すぐエラーを吐いてもらえるほうがありがたいと思います。

ベテランの皆さんはrelease後に不具合が生じた、なんてことは
あまりないのでしょうか?もしrelease版のみ、不具合が出てる場合
どう調べるんでしょう?

また、こんな目に合わないようにするには、
たまにはreleaseモードで確認してみる、ってこと、でしょうか?

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: debugではバッチリ。releaseしたら不具合が発覚。

#2

投稿記事 by softya(ソフト屋) » 12年前

releaseで不具合は日常茶飯事です。
そもそも、出力される機械語コードが全然違うので動かなくて当たり前と思ったほうが良いでしょう。
プログラムの動作速度が上がるのでタイミングバグも出やすくなりますし、メモリの配置も変わるので変数の位置関係が違うためポインタ系のバグの動作が変わってきます。
なので、定期的にreleaseモードで動作確認は必須です。

printfデバッグ法やらログを出すやらできる事はたくさんありますよ。
考えましょう。これも含めてのプログラミングです。

【補足】
そういうところも含めて開発環境がフォーロー出来ない部分があるのがC/C++のアセンブラに近い言語と言われる所以でもあります。
その分実行速度が早いわけですが。
再現性が高いバグは問題を修正することは困難ではありません。再現性の低い(稀にしか出ない)タイミングバグを取ることは非常に困難で死にそうになります。

と言うことでprintfデバッグ法です。
「簡単RPG講座 番外編。 デバッグ入門 • C言語交流フォーラム ~ mixC++ ~」
http://dixq.net/forum/blog.php?u=114&b=982&c=2
releaseビルドでも使えます。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

M.R

Re: debugではバッチリ。releaseしたら不具合が発覚。

#3

投稿記事 by M.R » 12年前

確実に言えることは正しく動作しないのはどこかに間違いがあるからです。
それがライブラリやコンパイラの場合もありますがそれも使い方で回避できます。

デバッガはあくまでデバッグを手助けしてくれるだけのものです。
デバッグのスキルはプログラマにとって重要です

土門

Re: debugではバッチリ。releaseしたら不具合が発覚。

#4

投稿記事 by 土門 » 12年前

疑問に思ったので質問させてください。
基底クラスで作ったいくつかの変数があるとするじゃないですか。
で、継承先のいくつかのクラスの中で
その変数を使わないクラスがあるとします。
(本来ならば利用するクラスの中でその変数を作れよ、なんでしょうけど)
その場合、その基底クラスの変数をまったく利用しない継承クラスの中ででも
基底クラスのすべての変数に対して
コンストラクタやinitializeなりで初期値をいれてやらないとダメですか?
使わないクラス内で無視してもいいんですよね?
自分はどこぞでそう聞いたので、そう認識しております。
その変数を指す処理が無ければ
使わない変数は初期化もせず、無視していても大丈夫と。
、、そうなんですか?
、、あ、でも今思ったらその変数を使わない継承クラスであろうと
すべての継承クラスのオブジェクトを格納した配列に対して
その変数を指すようなfor文とか書いていたら、
その使わないからと初期値を入れてない
継承クラスも通ってその変数の値を見ようとするってことですよね?
その場合、値を入れてない状態はまずいですか?

えっと、こういうことです。
基底クラス{
変数a;
変数b;


継承クラスA{
変数a=1;
変数b=0;
}

継承クラスB{
変数b=0;
変数aは無視
}

main.cpp
{
for(int i=0; i<2; i++){
if(継承クラスたちのインスタンスが格納された配列.変数a == 1)
{

}
}

この状況だと、使わない変数といえど、使ってるようなもんですよね?
結局、継承先で使おうが使わまいがすべて初期化しといてやるのが
いいのでしょうか?

softya(ソフト屋) さんが書きました:releaseで不具合は日常茶飯事です。
そもそも、出力される機械語コードが全然違うので動かなくて当たり前と思ったほうが良いでしょう。
プログラムの動作速度が上がるのでタイミングバグも出やすくなりますし、メモリの配置も変わるので変数の位置関係が違うためポインタ系のバグの動作が変わってきます。
なので、定期的にreleaseモードで動作確認は必須です。


そうですか、、、、そこまで別物なんですね、、
今後のゲーム製作は頻繁にreleaseモードで確認していきたいと思います。
ログを出せば、具体的になにがわかるのですか?


M.R さんが書きました: デバッグのスキルはプログラマにとって重要です


最近それを痛感しております。
debugのスキルなんてのはどう向上させればいいのでしょう?

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: debugではバッチリ。releaseしたら不具合が発覚。

#5

投稿記事 by softya(ソフト屋) » 12年前

> この状況だと、使わない変数といえど、使ってるようなもんですよね?
> 結局、継承先で使おうが使わまいがすべて初期化しといてやるのが
> いいのでしょうか?

人はミスをするので、ミスをした時に被害なしか、最低限の被害で済むように考えるのが良いコードを書くコツです。
そう考えれば自ずと対処は決まってきますよね。基底クラスの細かいところまで把握しないと継承でバグるのは設計ミスといって良いと思います。
※ コンストラクタで初期化するか、そもそも継承しないのが正しそうです。

> ログを出せば、具体的になにがわかるのですか?
出力したタイミングでの今の変数の値が分かります。当然ながらタイミングが分かる情報やら関数名やら行番号も同時にログに書くんですよ。

> debugのスキルなんてのはどう向上させればいいのでしょう?
まずテストスキルの向上ですね。関数・クラスの単体テストってやってますか?
あるいは試験項目での網羅テストです。
あとは勘を磨くしか無いでしょうね。つまり、バグを適当に直さず完璧に原因を突き止めて経験値を増やすんです。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

Ryo

Re: debugではバッチリ。releaseしたら不具合が発覚。

#6

投稿記事 by Ryo » 12年前

OutputDebugString()
なら、リリースビルドしたものでも、デバッガに文字列送信します
VisualStudioなら、リリースビルドしたものをVisualStudioから起動すればOK

アバター
usao
記事: 1889
登録日時: 12年前
連絡を取る:

Re: debugではバッチリ。releaseしたら不具合が発覚。

#7

投稿記事 by usao » 12年前

Releaseではだめという状況はわりと良く目にします.やらかしてるコードを見ると
変数初期化してないことが理由であることが多いですね.
VisualStudioのdebugモードだと未初期化状態の変数領域は特定の値で埋め尽くされるみたいなので
それによって たまたま 動いていたのが
Release時には その勝手な初期化 が無くなるために異なる結果になる,みたいな.
これの防止策は
そもそも確固たる理由なく変数を初期化しない状態で放置するということをしなければいいだけですね.
(未知の値な状態から処理開始!きっと動くさ!! とか私なら不安すぎて絶対やりません)

>継承クラスたちのインスタンスが格納された配列
これって何型なんでしょうかね?
継承クラスBの配列であれば,当然ながら
>if(継承クラスたちのインスタンスが格納された配列.変数a == 1)
このときの変数aは不定値ですよね.
(このことが疑問になっている状況であれば,継承の意味(機能)を今一度確認してから使用されたほうが良いように思います)

オフトピック
継承先で使わない変数って…
基底クラスは全く自身での利用予定の無いメンバ変数をわざわざ抱えているのでしょうか?
……この継承云々の話って単なる例え話なんですよね?(本当にやっているなら愚痴っている場合ではない)

土門

Re: debugではバッチリ。releaseしたら不具合が発覚。

#8

投稿記事 by 土門 » 12年前

softya(ソフト屋さん
>出力したタイミングでの今の変数の値が分かります。
>当然ながらタイミングが分かる情報やら関数名やら行番号も同時にログに書くんですよ。

それは重宝しそうです、、今後はぜひ利用していきたいと思います。

>まずテストスキルの向上ですね。関数・クラスの単体テストってやってますか?
>あるいは試験項目での網羅テストです。

、、、ですよね、そういう細かいチェックが本来必要なんですよね、、
がんばります。あとはやはり経験値ですよね
アドバイスありがとうございます。

Ryoさん
レスありがとうございます。
OutputDebugString()について調べてみましたが、
「アプリケーションがデバッガを持たないときは、システムデバッガが文字列を表示します。」
ちょっとまだなにを言ってるのか意味がわかりませんでした、、、
とくに「デバッガを持たない時」という状態がわかりません。
持っていて当たり前なのか、なんなのか、、
これ、releaseして吐き出されたexeファイルの話ですよね?
通常デバッガを持ってるもんなんでしょうか。よくわかりません。

usaoさん
レスありがとうございます。
>継承先で使わない変数って…基底クラスは
>全く自身での利用予定の無いメンバ変数をわざわざ抱えているのでしょうか?

ですよね、、言い訳にもなりませんが今はまだ慣れておらず、
また時間との闘いもあり、基底、継承クラス内の変数が散らかってしまっています。
よくあるのが、個別のキャラクタで利用する変数をそれらの基底クラスに設けるも
一部のキャラには必要無かったな、みたいなオチになることです。
そういう場合は、本来ならば、利用する個別キャラclassと基底との間に
継承クラスをかますんだとは思うんですが、一部すぎる個別キャラのためだけに
継承クラスを増やすのが面倒だったり、また、継承クラスをかまさないとしても
一部のキャラclassのヘッダに同じ変数を作りまわるってのもなんだか手間に思えるので
放置してしまうのです。
例えば、usaoさんなら下記の状況時、どうされますか?

基底)キャラclass

継承)各キャラクタclass(A、B、C、D、E、Fの六つあるとする
Aclass 変数a使用
Bclass 変数a使用
Cclass 変数a使用
Dclass 変数a使用
Eclass 変数a未使用
Fclass 変数a未使用

この場合、自分は、もう基底クラスに変数aを作ってしまいます。
それがよくないとするのがusaoさんの意見であれば、
こういう場合、やはりA,B,C,Dのclass内にて
律儀に同じ変数を宣言しまわるのでしょうか?
(基底と継承ABCDとの間にABCD用のヘッダをかます方法もあるかと思いますが、
変数aのためだけにそれをするのは無いですよね?)
その辺の意見をお聞きしたいです。

アバター
usao
記事: 1889
登録日時: 12年前
連絡を取る:

Re: debugではバッチリ。releaseしたら不具合が発覚。

#9

投稿記事 by usao » 12年前

>例えば、usaoさんなら下記の状況時、どうされますか?
私なら…という話で答えますが,

原則というか:
・実装を持つクラスは自身の受け持つ仕事を責任をもってやる.
・クラスは「自身が管理する変数=自身の受け持つ仕事に必要なもの」のみを持つ.

以上から,あるクラスが自身の仕事の範囲に必要のない変数を持つことはありません.
なので,その例で言えば 
その基底クラス(キャラclass)が変数aを持つのは私的にはおかしいです.
Aclass~Fclassに本当に共通な要素だけが キャラclass に含まれる候補になります.
aはその中に含まれません.

●A~Dのクラスがその変数aを同じ意味で用いていて,同じ方法で取り扱うのだとすれば,
つまるところ,表現しようとする世界の中に
「変数aに関わる機能を有するキャラ」と「〃有しないキャラ」という2種類のカテゴリが存在するわけですよね?
だったらそれを素直な形に実装します.

コード:

//共通基底
class キャラclass
{  /*本当に必要なのは多分,インタフェースクラス*/ };

//aに関する処理を実装するクラス.
//※このクラスは「変数aを宣言するためだけ」に存在するのではない.
// 変数aに関わる処理 はこのクラスが受け持つ.
// 例えば,「変数aにセットすべき値を何らかの演算により求め,aに代入する」処理はこのクラスに設けられる.
// 派生クラス側の実装に,その処理結果たるaの値が必要なのであれば,このクラスは例えば
// protected: int GetA() const { return a; }
// のようなメソッドを持つかもしれない.
// aの値を決めるのに必要なデータを持っているのが実際は派生側だとしても,aを値を決める方法の実装はこのクラスで提供される.
//  protected: bool CalcAndSetA( 必要なデータ群 );
// のように.
class aを扱うキャラ : public キャラclass
{
publicかprotectedかわかんないけど:
  aに関わる処理を行うメソッド();
private:
  //変数aを管理するのはこのクラスであるから,
  //このクラスから派生したクラスとかに勝手に触らせるべきではない.
  int a;
};

class Aclass : public aを扱うキャラ { ... };  //B~Dもこんな形で.
class Eclass : public キャラclass { ... };  //Fもこんな形で.
●仮に,A~Dのクラスが変数aを同じ意味で使っているわけでもなく,扱う方法もてんでばらばらとかいう話であれば,
そもそもA~Dの間には「キャラである」以外の共通性はない
(aに関する事柄は共通な要素ではない.「たまたま」同じ型の変数を1個有していて同じ変数名を用いていた,というだけである)
のだから,A~Dの各クラスが独立してaなる変数を持つ形になるべきです.

コード:

class Aclass : public キャラclass
{
private:
  int a;
};

class Bclass : public キャラclass
{
private:
  int a;
}

...

アバター
usao
記事: 1889
登録日時: 12年前
連絡を取る:

Re: debugではバッチリ。releaseしたら不具合が発覚。

#10

投稿記事 by usao » 12年前

あと,その 個別キャラ というのがどういう風に互いに何かが異なるのか がわかりませんが,
場合によっては,その違いをStrategyパターンみたいな感じに書く方が向いていたりするかもしれません.
オフトピック
StrategyとBridgeとStateあたりの呼び分け方がどうにもはっきりしない私.

土門

Re: debugではバッチリ。releaseしたら不具合が発覚。

#11

投稿記事 by 土門 » 12年前

usaoさん
やっぱりそうしないと駄目ですよね。
詳しく解説してくださり、ありがとうございます。
勉強になりました!

閉鎖

“C言語何でも質問掲示板” へ戻る