例外原理主義
「れいがい!」、「REIGAI REIGAIせよ!」、「そろそろれいがいさんへの思いを適当にまとめる」も参照のこと。
- assert/エラー/例外の区別。
- assert/エラー/例外の使い分け。
- なにか問題が起きた場合は基本的に例外を投げる。
- リリース版ではそもそも発生することがあってはならない類の問題については assert を使用する。
- パフォーマンスが問題にならない場合は assert とともに例外やエラーを併用することを推奨。
- 問題が起きても無視してよいものあるいは例外ではパフォーマンスや呼び出し側の実装の都合が悪い場合にはエラーする。
- リリース版ではそもそも発生することがあってはならない類の問題については assert を使用する。
- なにか問題が起きた場合は基本的に例外を投げる。
- assert/エラー/例外の役割。
- 問題が起きたことの通知。
- 通知先は呼び出し元。
- 末端の呼び出し元はユーザー/管理者/プログラマを問わず、人。
- 形式:UI上のメッセージあるいはログ
- 内容:呼び出し履歴(引数を含む)+末端でどんな問題が起きたのか。
- 代替手段がある場合にそちらを利用。
- 安全な終了。
- ここでいう安全とはデータを破損・損失しないという意味での安全。
- 問題が起きたことの通知。
- どんな情報を例外として送出するべきか?
- 「なにをやった時」に「なにが起きた」のか。
- 多くの状況下で、呼び出し側は「なにをやった時」かは分かるが、例外を送出する関数が呼び出された際の仮引数に含まれる情報は呼び出された側が含めるべき。そうしなければより親のレイヤーに情報を返す為にその関数を呼び出す全ての箇所で実引数に含まれる情報を埋め込むコードをばらまかなければならなくなる。
- エンドユーザーに見せる場合はこれに「なにをすれば問題を解決できるのか」を付与すること。
- 適切に情報を提示できればプログラマでないエンドユーザーでも問題の把握と対処が可能になる。
- そしてその情報はなによりもまずその製品を開発中の開発者自身を助ける。
- 適切に情報を提示できればプログラマでないエンドユーザーでも問題の把握と対処が可能になる。
- 中途半端にコミットしてしまっている動作があるのならばその情報。
- 前提としては例外に限らず問題が起きた場合は可能な限りロールバックを行う。
- 「なにをやった時」に「なにが起きた」のか。
- 自動伝播について
- 自動伝播については一長一短があるが、自動伝播しなければ「問題が発生したことにすら気づかない」という事態を招いてしまう点を重要視し、自動伝播であることを善しとする。
- 見えるべき情報が見えなくなるというのはそれだけで避けるべき理由に足る。
- まずファイルの保存やサーバーへのデータのポストなどと言ったコミット(書き込み)系操作に失敗していることは絶対に検知されなければならない。この問題は検知できれば単にリトライするなり別の場所に待避させるなりして致命的な状況を回避できるが、検知できなければそれも叶わない。
- 次にデータの読み込み操作に失敗していた場合、これについても絶対に検知されなければならない。この問題は検知できれば単にリトライするなり読み込み可能な状態にするなりすれば済む話だが、データの一部を読み込みに失敗していることに気づかずにデータを上書きをしてしまい致命的な状況を招いてしまう。
- データの演算操作についても読み書きと同様の理由で問題を検知する必要があり、要は結局のところ全ての問題はまず検知されなければならない。
- 自動伝播はいまの形ではなく、関数(やスコープ)を抜ける度に自動でネストされるべき。
- ネストされる際、引数あるいはそれに相当する情報を含めるべき。
- これらの情報がないと対応が困難になる。
- ネストされる際、引数あるいはそれに相当する情報を含めるべき。
- 上位で中断するかどうかを判断することについては機能の切り分けの問題でもあると思う。また、実際に上位に中断するかどうかを問い合わせる実装については trickerr.h で実現しているので参照されたし。(といっても利用例が欠けているのあまり参考にはならないかも。)
- 例外の型については関数と1対の関係になるべきものなのかも。
- その上で例外情報を木構造でネストする。
- 関数自体もクラス扱いすれば、そのまま例外情報として送出可能になるし、処理内容によってベースとなるクラスを継承していれば catch の利便性もよい。
- 捕捉された例外はネストはしても原則として加工するべきではない。
- 自動伝播については一長一短があるが、自動伝播しなければ「問題が発生したことにすら気づかない」という事態を招いてしまう点を重要視し、自動伝播であることを善しとする。
- Icon 言語のゴール指向について*2
- エラー忘却型コンピューティング/フェイルソフトについて
- 本質的にこれらの目的はダウンタイムの最小化であり、この意味でも例外を使いすべての問題を検出することは重要。
- 問題を検出したからと言って全ての処理を止める必要はない。
- 問題の検知というか早期発見を怠るとより大きなダウンタイムを招くことになりかねない。
- 本質的にこれらの目的はダウンタイムの最小化であり、この意味でも例外を使いすべての問題を検出することは重要。
- フェイルセーフについて
- 例外そのものがフェイルセーフな性質を持っている。
- ただし、(当然ではあるが)ただ例外を使えばフェイルセーフになるというものではないので注意。
- 例外そのものがフェイルセーフな性質を持っている。
- 今現在例外に統一されてないことについて
- 今現在例外に統一されてないだけで例外(の発展系)で統一されるべき。
- 昔は言語使用的にそもそも例外が存在しておらずその間にエラーやassertが蔓延ってしまっただけで例外(の発展系)で統一されるべき。
- インターフェイス上、モジュール間での例外処理は難があるが例外システムの共通化を図り、例外(の発展系)で統一されるべき。
- 例外(の発展系)で統一されるべき。
- ・・・ぶっちゃけいまの例外システムのままではダメだと思う。ひょっとしたら例外システムがダメなんじゃなくてそのまわりの処理体系のほうに手を入れるべきなのかも知れないけど。少なくとも正常系の戻り値と同列で例外を処理できるようにし、且つ木構造例外に対応する必要がある。
バグ論
細かい論説すっとばしてますが、いつまでもネタを寝かしたままにしておくのもよくないので、以下、要点だけでも・・・
エントロピー(複雑さ)には勝てない
- 数学的視点による見解
- 複雑な方程式はいとも簡単に解のない式となる。
- 仕様のレベルで、複雑な要求に対しバグがない仕様は論理的に存在できなくなる。
- プログラムも同様に、複雑な仕様に対しバグがないプログラムは論理的に存在できなくなる。
- 複雑な方程式はいとも簡単に解のない式となる。
- 生物学的視点による見解
- 生物(生体)は複雑さに対して「大小様々な問題があろうが致命的な問題がないように」というアプローチでこの問題を克服している。
- これらを踏まえたバグとのつきあい方
- どんなに科学が進歩しようが「絶対に間違った判断をしないAI」などというものは論理的に無理。
- 可能な限り簡略化を試みる。
- 生物(生体)と同じアプローチを採用する。
- バグがあってもそれを隠蔽する方向で実装する。
- デバッグ版では逆に assert() などで可能な限りバグを検出する。
- 開発全体においてテストに注力する。
- 仕様や工程などに対するテスト(リビュー等)も。
- 直さなくていいからとにかくバグをより多く検出し記録する。
- 致命的なバグがあって且つそれを直さないままでも、バグがあると分かっていればまだ対処のしようもある。
- バグがあってもそれを隠蔽する方向で実装する。
開発効率の最大化
- 次の順番で注力することで、開発効率の最大化が可能になると思われる。( バグにフォーカスした視点においての話 )
- [最優先]簡略化>>>バグを作らないインプリメンテーション>>>テスト(特に自動テストによるバグの早期発見)>>デバッグ[優先度低]
- 効率の最大化という観点から考えるとバグがひとつもあってもはならないとするのはナンセンス。
以上は中規模以上のプログラムを前提の話とした話であって、小規模なプログラムあるいは小さなモジュール単位であればエントロピーに勝てないなんてことはないし、バグがひとつもあってもはならないとすることが効率の最大化にもなり得ます。
不具合仕様のススメ
- 許される不具合と許されない不具合について記述する。
- 適切なテストケースを作成するのにも本来必要な資料。
- 求められる品質が明確になることにより、アンダーエンジニアリング/オーバーエンジニアリングを未然に防ぐ。
- 予め想定バグを列挙されることで、バグを生んでしまう機会を減少させる。
パスワードの定期更新の有効性に関する考察
パスワードの定期更新の有効性について疑問を抱いている自分の考えをまとめてみました。
ブルートフォースな攻撃に対する有効性
このことを簡単に説明する為にここでは数字4桁限定のパスワードで考えてみましょう。
例えば、まず初期状態として 2032 がパスワードとして設定されているとします。さらにここで攻撃者はブルートフォースな攻撃を 0000 から順番に行うとします。攻撃者が 2032 に達するまでに運良くパスワードが定期更新により変更されれば、一見難を逃れたように見えます。
しかし、よくよく考えて見てください。これは本当に難を逃れたと言えるのでしょうか? 残念ながらこれは難を逃れたとは言えません。と、言うのも、攻撃者が 0500 のあたりを試行中に 0600 あたりのパスワードへ変更したのであれば攻撃が成功するまでの時間を反って短くしただけです。また逆に 9000 あたりの攻撃者がそこへ達するまでに時間のかかるパスワードへ変更したとしても結局は攻撃者はそこへいずれたどり着きます。
幸運にも攻撃者が 1000 あたりを試行中に 0500 あたりのパスワードへ変更できたとしても、攻撃者は 9999 まで試行してダメだったら 0000 からやり直すだけなので多少の時間稼ぎにしかなりません。非常な幸運に恵まれれば攻撃者のブルートフォースな攻撃をかわし続けることができるかもしれませんが、定期的なパスワード変更のブルートフォースな攻撃に対する有効性というのは結局の所、攻撃が成功するまでの時間を期待値で概ね2倍*1にするだけの効果しかありません。
ショルダーハックに対する有効性
これについては多少は意味があるのではないかと思われますが、そもそもパスワードをタイプするところを他人に見られるべきではありません。監視カメラ等が動作している環境でパスワードをタイプするなどに至っては言語道断です。*2
パスワードが割れてしまった場合に・・・
攻撃者に対してそのパスワードが有効な期間を短くすることは可能ではあるかも知れませんが、クラックを許した時点でそれがどこまで意味のあることなのかは疑問です。しかし、退会/退職などにより無効にするべきアカウントがいつまでも有効なままになってしまっていることにより招かれる不正アクセスの軽減策や、ダメもとで可能な範囲で被害を少しでも少なくするという観点においては意味があると言えなくはないでしょう。パスワードの定期更新の有効性については恐らくこれに尽きるのではないかと思います。
定期更新よりは、ひたすら長いパスワードを推奨
JIS X 3014 : 2003 盗作疑惑の真相
以前、cppll でもこのブログでも触れた JIS X 3014 : 2003 盗作疑惑についてですが、現 C++WG 主査の林田様より、事の真相を教えて頂きました。
実は「国際規格ISO C++ ライブラリ ハンドブック」の著者である越田一郎先生は当時のC++WG 主査であり、同じ誤訳があったのは単に著作者による著作の流用が行われていただけのことであり、盗作などではありませんでした。
知らなかった故のこととは言え、大変お騒がせいたしました。
TrickPalace 及び TrickLibrary のCSSを修正しました。
以前から、TrickPalace 及び TrickLibrary の一部のページ*1で Firefox 及び Safari でレイアウトが致命的に崩壊してしまう問題を認識していたのですが、「こっちを直せば、あっちが崩れる」な状態であった為、どう直すべきか思案していたのですが、今日、Twitter で id:DigitalGhost さん( @Twitter )に min-height を使えば直るのではないかとのお言葉を頂き min-height を使ってみたところ、無事この問題を解決するに至りました。まだ、Firefox 及び Safari で一部、表示が変になってしまう部分があるにはあるのですが、コンテンツの閲覧に支障があるレベルの問題はこれで無くなりました。
実際に min-height を適用するにあたっては【CSSでmin-heightをクロスブラウザにする最も簡単な方法『Easiest cross-browser CSS min-height』】を参照しました。
それから自分でやっといてなんですが、IE の場合に常にヘッダーとフッターが画面の上下に表示されるようにしていた CSS がウザかった上にページ内アンカーの障害にもなっていたので、ついでに外しておきました。
`Д´)ノ バグベアード抜きで printf デバッグを語るんじゃねぇ!
...くそぅ、まだまだバグベアード( why バグベアード? )の知名度が足りねぇのか。
printf デバッグにおいてはバグベアードのようなモジュールを利用するかどうかで大きく話の前提が変わるんだけどなぁ。自分的には「この作者、頭おかしい!」と呼んでもらうに足るレベルの作り込み*1に達していると思ってんだけど、その存在を知ってもらってなけりゃそりゃお話にならんわな。('A`)
しかもこの人、 Debug Hacks の著者かよ。あの様子だとデバッグツールの紹介としても一言も触れられていないんだろうなぁ。バグベアードの存在はまさに Debug における Hack だと言うのに。
printf デバッグの是非について少し自分の意見を述べておきますと、多くの場合、printf デバッグはそれが場当たり的に行われるものである点が非常にまずいものだと考えています。ソースコードを修正する以上はデバッグの為の一時的な埋め込みであろうとプログラミングであることには変わりなく、プログラミングである以上は場当たり的に行うべきではありません。printf デバッグそのものに一長一短はあるにせよ「 printf デバッグ=悪」とするほどのものではなく、悪いのは「場当たり的に行われる printf デバッグ」かと。
進捗率クラスを更新しました。
http://tricklib.com/cxx/dagger/progress.h
...更新したと言っても...
rational_progress(int_T a_denominator) :numerator(0) ,denominator(a_denominator) { assert(0 < denominator); } rational_progress(int_T a_numerator, int_T a_denominator) :numerator(a_numerator) ,denominator(a_denominator) { assert(0 < denominator); }
...となっていた箇所を...
rational_progress(int_T a_denominator, int_T a_numerator = 0) :denominator(a_denominator) ,numerator(a_numerator) { assert(0 < denominator); }
...としただけですが。旧版から新版に差し替える場合、引数の順番が変わっているのでご注意ください。
# どうしてもこの引数の順番の設計ミス(より重要な引数が先になっていない)は許せなかったもので。