トップページ⇒tech
90コメント29KB

【放置ぬるぽ】assert() 濫用【遠い原因】

■ このスレッドは過去ログ倉庫に格納されています
0001デフォルトの名無しさんNGNG
デバッグ支援マクロ assert()
が、使い方を誤ると却ってデバッグの邪魔になるという諸刃の剣。
素人にはおすすめできない。
0002放置ぬるぽNGNG
HOGESTRUCT *hoge_create(void)
{
HOGESTRUCT *hoge = malloc(sizeof(HOGESTRUCT));

assert(hoge); /*←これで止めたつもりになっているのが痛い*/
memset(hoge, 0, sizeof(HOGESTRUCT));

return hoge;
}
0003デフォルトの名無しさんNGNG
今は別にintでなくてもよかったような。
0004遠い原因NGNG
void hoge_proc(HOGESTRUCT *hoge, FUGASTRUCT *fuga)
{

assert(hoge);
assert(fuga); /*←このぬるぽ、発生箇所不明*/

/*中略*/

}
0005鉄則NGNG
1. NDEBUG が定義されていたら無視されることをお忘れなく。
 つまり、本番では放置しても問題ないけどデバッグ時に検出したい条件が
 あるときに使うのが正しい。
 そうでなければ通常のエラー処理に移行しろ。
2. assert()で止めるなら早めに止めろ。
 極力原因の直後で。
 それが無理ならその原因が影響しそうなモジュールの初期化部とか。
 原因から遠ければ遠いほどデバッグは困難になる。
3. そもそも、ぬるぽ如きでassert()使うな。
 絶対に必要なポインタなら通常のエラー処理へ。
 なくても困らないものならアクセスせずにスルーできるようなコーディングを。
0006orzNGNG
直属の部下なら激しく直させるのだが、その権限ないのが辛い。
こんな糞コードにために何度徹夜したことか…ったく。

しかも、リアルタイム処理に多数のassert()や放置ぬるぽが埋まっててまるで地雷原。
で、そのせいで頻繁にオチるからクライアントが怒り狂ってるし。
もう見てらんない _no

以上、出向先に多数のヘボPGを抱える俺の愚痴ですた。
0007デフォルトの名無しさんNGNG
assertのせいでオチてクライアントが怒る?
NDEBUGつけてないバージョンをリリースしてるのか。
0008デフォルトの名無しさんNGNG
こんなレベルなら生malloc使うな。
つーかCやめ。
0009デフォルトの名無しさんNGNG
DIALBO2はassertでよく落ちたな
0010デフォルトの名無しさんNGNG
例外があるような言語ならassertはあまり必要ない。
逆に例外がない言語では積極的に使わないと危険。
内部エラーをエラーコードで返してもswitch/caseでスルーされるだけ。
0011デフォルトの名無しさんNGNG
>>10
例外とassertの使い方の違いを知らないようだな。

変な使い方をされたときに発生するのが例外。
変な使い方をするのを検出するのがassert。
0012デフォルトの名無しさんNGNG
>>10
だからassertはエラー処理にならないって言ってるのに。
0013デフォルトの名無しさんNGNG
>>11
検出させることもできるが試験と本番では出来ないのがassert。
0014デフォルトの名無しさんNGNG
>>13
テストではassert有効にするだろw
0015デフォルトの名無しさんNGNG
>>11-12
たとえば範囲をチェックする関数
bool InRange(int n, int min, int max)
でInRange(x, 10, 1)を実行したとき、
C#ならSystem.ArgumentExceptionを投げるようにするけど
Cならassertで止めるね。あんたらならどうするんだ?
0016デフォルトの名無しさんNGNG
min、maxはマクロだからやめとけ。
n >= min(min, max) && n <= max(min, max)
0017デフォルトの名無しさんNGNG
>>15
変な使い方をされた場合は例外。
変化使い方を検出するときはassert。

だから、関数内でのチェックでは例外。
関数呼び出し元で変な使い方をしないように検出するためにassert。
0018デフォルトの名無しさんNGNG
>>17
答えになってないな。
具体的に関数のインターフェース・仕様を書いてくれよ。
0019デフォルトの名無しさんNGNG
>>18
これでわからないのか?
面倒だから擬似言語で書くぞ。

{
 int min=10;
 int max=1;
 assert(min<=max);
 InRange(x,min,max);
}

bool InRange(int n, int min, int max)
{
 if(min<=max) {
  return 範囲判定;
 } else {
  throw ArgumentExceptoin;
 }
}
0020デフォルトの名無しさんNGNG
>>19
話がずれてるよ。
あんたのC言語版でInRange(x, 10, 1)を実行した場合
InRange内では何をするのかと聞いてるんだよ。
なにもしないの?未定義動作?
0021デフォルトの名無しさんNGNG
>>20
InRangeの中では例外発生に決まっているだろ?

それとも例外が存在しない言語で
例外をどう実装するかを聞いているのか?
0022デフォルトの名無しさんNGNG
>>21
>それとも例外が存在しない言語で
>例外をどう実装するかを聞いているのか?
そうだよ。
>>10では
例外が存在しない言語では内部エラーの検出にassertを、
存在する言語では例外を使う。
だから例外のある言語ではほぼassert不要と主張したつもりなんだけど。

C#
bool InRange(int n, int min, int max){
 if(min>max) throw new ArgumentException();
 return 判定;
}

C
bool InRange(int n, int min, int max){
 assert(min<=max);
 return 判定;
}
0023デフォルトの名無しさんNGNG
>>22
だから例外がある言語でもassertは使うだろ。

assertは関数呼び出し側での引数チェックや、
関数内で引数のチェックが終わったあと
(このチェックに引っかかった場合は例外)で、
正常な引数だから正常に処理されるはず。・・・という状態での
関数の内部の処理のバグ検出で使うもの。
0024デフォルトの名無しさんNGNG
>>23
そこまで頑強にC言語版の実装を示さないのにはなんか意味あるの?
あんたの原則はもうわかったから早くコードくれよ。
0025デフォルトの名無しさんNGNG
>24
ばか?
0026デフォルトの名無しさんNGNG
>>25
なんか、頭の不自由な人が多いよね。
0027デフォルトの名無しさんNGNG
>>14
試験を本番コードでしないのか?
試験でバグが出たときのデバッグに使えれば使うこともあるが…

>>24
エラーになる可能性があるなら、エラーを通知するインターフェースが
必要なんだよ。グローバル変数でもlongjmpでも何でもいいからインターフェースを
規定する。assertは本番コードには残らないし、残しても制御不能な状態で
落ちるからエラーが起きる可能性のあるところのエラーチェックには使えな
いんだよ。仕様記述のコメント+デバッグ時にチェックがかかるかもしれない
機能がassertionだ。
002827NGNG
>>14
assertをONにして全assertionが問題なく通る確認という試験項目も考えられるね。
つーかやってない俺が間抜け?
0029デフォルトの名無しさんNGNG
Design by Contruct?
0030デフォルトの名無しさんNGNG
>>29
Contract
いまいち流行ってないけどな。DbC。
0031嫌がらせNGNG

assert(0 && "( ´,_ゝ`)プッ");
0032怪文書NGNG
HOGESTRUCT *hoge = malloc(sizeof(HOGESTRUCT));

assert(hoge &&
"あ〜あ、メモリ足りなくなっちゃったよ。あれほど実装量ケチるなといったのに。"
"偉い人の気紛れでできた追加仕様詰め込んだら案の定これだ。"
"俺はこんな奴の尻拭いなんて御免だからもう逃げる。"
"御意見・御要望は090-xxxx-xxxxの偉い人まで。"
);
0033デフォルトの名無しさんNGNG

assert(0 && "(゚Д゚)ハァ?");
0034デフォルトの名無しさんNGNG
>>32
似たようなの見たことあるぞw
0035デフォルトの名無しさんNGNG
#if 0 /* ( 'A')=j GATT!!*/
assert(p && "( ^^) <NullPo");
#endif
0036デフォルトの名無しさんNGNG
assertとstd::logic_errorの使い分けはどーすればいーんでしょ?
0037デフォルトの名無しさんNGNG
>>36
動作の違いに基づいて使い分けろ。
0038デフォルトの名無しさんNGNG
責任分界点のインタフェースや入出力など、作っている側がコントロールできな
い場合はチェックのコードを明示的に書く必要がある。

自分一人で全部作っているなら引数チェックをassertでやっても構わないんじゃ
ないかと思う。
0039デフォルトの名無しさんNGNG
>>38
同意。
要は、他人に迷惑をかけるなってこった。
0040デフォルトの名無しさんNGNG
引数チェックをassertでっていう悪しきスタイルはMFCが多用してるな
vectorの[]演算子のような、実行時チェック入れると速度半減するから
入れたくないような奴にはassert()が最適な気がするけど

まあでも、それとは別の話で、ヌルポインタだけをチェックすることに
何か意味があるのだろうかと俺は疑問に思ってたな
ヌルポインタは、不正なポインタ(いくらでもありえる)の例の中の
ほんの一つに過ぎない
0041デフォルトの名無しさんNGNG
それと(なぜか)上で一切触れられてない観点だが
実行時に起こり得るエラーとバグによるプログラムエラーでは
まるで違う話だ
後者があるものは本来納めちゃいかんだろ
それをなるべく早期に(テスト中に)検出するのがassert()やabort()
の目的なんだから
バグがあっても何となく動くように作りそのようなゴマカシを
残したまま納めるのは正しい姿勢か?
確かにassert()はDEBUG版コンパイルでは機能しない
ならば潔くabort()をつかえ
"バグです"
と言ってしぬのだ
0042デフォルトの名無しさんNGNG
いわずもがな
0043デフォルトの名無しさんNGNG
assertはバグというか、ミスや誤りを検出するためのものでそれ以上ではないでしょ。
0044デフォルトの名無しさんNGNG
バグ検出のためだけなら
UNIXではabort()で死んでコア吐かせたほうが便利
Windowsのabort()はコアを吐いてくれないような気がするが
わざとGPEを発生させればデバッガが何かしてくれる
DrWatsonしかなくてもダンプファイルは取れる
もともとGPEが発生するようなコードなら、わざわざ手を加えると
かえって原因が分かりにくくなるだけだ

assert()は半端。assert()は用済み。assert()はしつこい。
0045デフォルトの名無しさんNGNG
だから、バグじゃなくて、ミスや誤りを検出するわけだ。
0046デフォルトの名無しさんNGNG
ミスや誤りって バグ じゃないのか...。
0047デフォルトの名無しさんNGNG
バグ、だろ

>>15の例は「範囲をチェックする関数」なのであれば、assert使うのは
間違い
それじゃ、範囲をチェックできないだろ?
isascii()はascii以外の文字を食わせたら落ちるのが正しい仕様か?

「正しい範囲の引数でなければ動作しない。正しい範囲の引数を
渡すのは呼び出し側の責任であり、そうなってないならば
呼び出し側のバグである」というような関数であれば
(例えばポインタを扱う全ての関数はそうだ)
assert()してもいいけど、結局は無駄で余計なおせっかいなだけだ。
0048デフォルトの名無しさんNGNG
>>47
なんか勘違いしてない ?

bool InRange(int n, int min, int max) で InRange(100, 1, 10) の時の話なら、あんたの言う通りだけど、InRange(x, 10, 1) だよ ?

InRange() の作成者次第だけど、「min < max で引数を渡すのは呼び出し側の責任であり、そうなってないならば呼び出し側のバグである」と考える奴がいても不思議はないと思うが。
0049デフォルトの名無しさんNGNG
int hogefunc_rel(HOGESTRUCT *p){
int result;

if(!p)return ERROR_VALUE_FOR_HOGEFUNC;

/*中略*/

return result;
}

int hogefunc_dbg(HOGESTRUCT *p){

if(!p){
fputs("ぬるぽにガッ!!\n",stderr);
abort();
}
return hogefunc_rel(p);
}


で、必要に応じて関数を切り替え。これ最強。
0050デフォルトの名無しさんNGNG
>>49
単に、

int hogefunc_rel(HOGESTRUCT *p){
  int result;
#if defined(DEBUG)
  if(!p){
    fputs("ぬるぽにガッ!!\n",stderr);
    abort();
  }
#endif
  if(!p)return ERROR_VALUE_FOR_HOGEFUNC;
  /*中略*/
  return result;
}

とすりゃすむ話じゃないのか ?
0051デフォルトの名無しさんNGNG
>>50
それだと全体でしか切り替えられないやん。
モジュール書いた本人が使うだけならいいけど、複数人でコード書いてるときは
他のメンバーからしてみれば自分の作業範囲と関係ない強制 abort は邪魔なだけだからね。

呼ぶ側が使いたいところだけ
#define hogefunc hogefunc_dbg
にして使うですよ。
(このケースでは自分でチェックした方が早いけど、実際にはエラー条件が
複雑だったり可変だったりすることもあるんで、そのあたりのツッコミは却下)

…と書いたところで、_rel は付けない方が使い易いと思った。
元に戻すときは #undef だけで済むし。
0052デフォルトの名無しさんNGNG
あくまで例としてのコードだろうが
nullpointerを渡したときの動作は
両方落ちるのは同じで
メッセージを吐くかどうかの違い「だけ」

どのみちコアが吐かれるかデバッガが起動するので
メッセージなんぞ見なくても原因は分かるし
「ぬるぽにガッ!!」などというくだらないメッセージでは
スタックトレースや呼び出し引数等の情報は分からないから
コアやデバッガが提供する情報には叶うべくも無い

最強どころか無駄極まりないコードだな
0053デフォルトの名無しさんNGNG
assertの使いどころがいまひとつわからん。
一応状態のチェックあるいは明示をしておきたい、でもエラー処理は冗長ってときに
適当に入れとく一行ってことでええのん?

だいたい、なぜリリースモードでコンパイルすると削除されるようになってるのか。
残しておけばええやんって気もするんですが。
0054デフォルトの名無しさんNGNG
>>53
その使い方でええよ。
削除されるのが嫌なら NDEBUG を定義しなければいい。
0055デフォルトの名無しさんNGNG
>>53

この辺に結論は集約されてる。

>>40
>vectorの[]演算子のような、実行時チェック入れると速度半減するから
>入れたくないような奴にはassert()が最適な気がするけど

>>41
>実行時に起こり得るエラーとバグによるプログラムエラーでは
>まるで違う話だ
>後者があるものは本来納めちゃいかんだろ
>それをなるべく早期に(テスト中に)検出するのがassert()やabort()
>の目的なんだから
0056デフォルトの名無しさんNGNG
>>55
それらのレスはちょっと結論とは言えないくらいに主観の入ったものだと思う。
0057デフォルトの名無しさんNGNG
>>1
Unitテストのほうに集中的に使え
0058デフォルトの名無しさんNGNG
>>1
例外使え
0059デフォルトの名無しさんNGNG
assertが単なるマクロなのがちょっと残念。
リリースビルドでは言明した内容を仮定して最適化に役立ててくれればいいのに
とときどき思う。
0060デフォルトの名無しさんNGNG
>>59
D言語のassertは言語仕様の一部だから、そういうこともできるかも。
0061デフォルトの名無しさんNGNG
できませんが
0062デフォルトの名無しさんNGNG
ttp://www.digitalmars.com/d/pretod.html#macros
"assert() is a built-in expression primitive. Giving the compiler such knowledge of assert() also enables the optimizer to know about things like the _assert() function never returns."
0063デフォルトの名無しさんNGNG
それは>>59の求めるものとは違うような。
GCC用語でいうとassertがnoreturnだから、
(eがただの式ならば) _assertもnoreturnになるはずという、
一種のtype inferenceでしょう。

>>59はCommon Lispだと処理系によってはしてる気がする。
0064デフォルトの名無しさんNGNG
>>63
それだけの話ならoptimizerについて言及しないだろう。
>>62の引用元は
「D言語のassertは最適化の対象となり得る(C言語ではそうはならない)」
というのが言いたいんじゃないかな。
0065デフォルトの名無しさんNGNG
どういう最適化?
>>59のようなのだとするとeを仮定することから"the _assert() function never returns."を導くことになるけど、無理じゃね?
0066デフォルトの名無しさんNGNG
bool f(int x) { assert(x > 5); return x > 0; }
これを
bool f(int x) { return true; }
こうできるってことかな?
0067デフォルトの名無しさんNGNG
イラネ
0068デフォルトの名無しさんNGNG
>>48
bool InRange(int n, int r1, int r2){
  return min(r1, r2) <= n && n <= max(r1, r2);
}
0069デフォルトの名無しさんNGNG
>>68
そういうオーバーヘッドを支払いたくないから
InRange(int min, int max)ってインターフェースで話を進めてるんだろ。
0070デフォルトの名無しさんNGNG
http://www.amazon.co.jp/exec/obidos/ASIN/4756103642/250-4746584-2110605
この本にassertの使い方のってます。けっこういい本です。
0071デフォルトの名無しさんNGNG
http://news.fs.biglobe.ne.jp/politics/
今年の流行語で、あなたが一番印象に残ったのは?

●現在の集計結果です
http://cgi.news.fs.biglobe.ne.jp/vote/no384/vote_result.cgi
0072デフォルトの名無しさんNGNG
ttp://namazu.org/~satoru/sary/FAQ.html#tests

> たくさんのテストプログラムがあるのはなぜ?
>
> テストが好きだからです。testsディレクトリにテストスイートを揃えています。
> make check でテストできます。リファクタリング で述べられているソフトウェア
> 開発手法に従っています。 Suffix Arrayの操作にはたくさんのきわどい境界条件
> を要するため、厳格なテストは不可欠です。また、やっかいなバグを防ぐために
> g_assert を多用しています。
0073デフォルトの名無しさんNGNG
内部バグでしか起きない状況ならassert
外部要因で起き得る例外なら例外じゃないの?
0074デフォルトの名無しさんNGNG
>>73
だよね。
0075デフォルトの名無しさんNGNG
JavaにはInternalErrorというのもあるわけだが。
0076デフォルトの名無しさんNGNG
>>73
assertの後についでに例外もはさむべきだと思う。
人間がやっている以上コードもテストも100%というわけには行かない。
assertだとたいていスタックトレースは残らないから
デバッグしたくても情報が足りないことが多い。
0077デフォルトの名無しさんNGNG
>>76
それはパフォーマンスに対する要求も考慮して決めないといけないだろう。
0078デフォルトの名無しさんNGNG
>assertの後についでに例外もはさむべきだと思う。
イミネー
だったら例外投げるだけで十分。
0079デフォルトの名無しさんNGNG
>>78
俺も自分で書いてから思った。
まったく意味ねぇ。
なんかどうしてもassert使いたそうな人が居たもんで。
0080デフォルトの名無しさんNGNG
結論は言語によって異なるのではないかと書いてみるテスト
0081デフォルトの名無しさんNGNG
UnitTestはじめてからコード内部にassert埋め込むのは激減したよ。
デバッグ用途には例外とloggerがメイン。
0082デフォルトの名無しさんNGNG
0083デフォルトの名無しさんNGNG
昔COM上で書いたコード

assert( p->Release() );

0084デフォルトの名無しさんNGNG
>>44
UNIXならasertがabort呼ぶだろ.
0085デフォルトの名無しさん05/02/12 00:08:21
>>81
 UnitTestでブラックボックステストを行い、さらにテストターゲット中のassertに引っかからない
ことを確認することでホワイトボックステストを行うとさらに品質Up。
0086デフォルトの名無しさん05/03/11 05:06:21
FowlerがIEEE Softwareに書いた記事"Fail Fast"が公開されてますね。
早い話がassertのススメ。
0087デフォルトの名無しさん05/03/11 23:20:41
>>86
ソースきぼん
0088デフォルトの名無しさん05/03/12 10:16:02
fowlerのblog
0089デフォルトの名無しさん2005/05/16(月) 20:27:25
     ∧_∧
(.*´ー`)、 148乙。俺はまだ当たっ          (_ *%゚O(゙) でも俺は148が当たって心から喜べる。
        /`ヽ,)/|  おまいは月たんの婿にふさわしい。俺が認める。
        (_/ i,_ノ  これから月たんを慈しみ守り通してくれよな。おめでとう148。


0090デフォルトの名無しさん2005/05/16(月) 20:27:47
 ∧_∧
        (.*´ー`)、 148乙。俺はまだ当たってない。
          (_ *%゚O(゙) でも俺は148が当たって心から喜べる。
        /`ヽ,)/|  おまいは月たんの婿にふさわしい。俺が認める。
        (_/ i,_ノ  これから月たんを慈しみ守り通してくれよな。おめでとう148。


■ このスレッドは過去ログ倉庫に格納されています