PPPUC++#15.7

13.10 Managing unnamed objects

  • 無名オブジェクト、とか
    • 必要になる例として、 256色パレットを表示したい時! ってそうか、こうやって16x16に並べればさっきのわかりやすかったのかorz と後悔後を絶たず
  • まあ普通にnewでのヒープオブジェクト(のポインタ)を受け付けるVectorを用意しましたよ>Vector_refという話
      • うーん、そんなもん用意しなくてもvectorにpush_backした時点でコピーされるような気もするけど、これは純粋にnewを見せるための教育的配慮でしょうね
  • ポインタだけどデストラクタでちゃんと面倒みてるのでリークは心配ご無用とのこと
    • Appendix.E に全コードあるんですが、本もって無い人はMr.Sのサイトから

13.11 Text

  • テキストです。これもColorやLine_styleと同様に単にFLTKラッパー
    • 改行には対応して無いそうです。互換性のため
  • 引数で指定する点はテキストの左下を表しています
  • 自前の draw_lines() (という名前どうかとおもいますが)を持っています
      • なんで単なる draw() にしなかったんだろう
      • と、思ったら draw()もあるのね。これは callback名。draw_shape()にすべきだったのかな

13.12 Circle

  • 円ですね
  • 特別変わっているところも無いのですが、、、
  • I/F的には 中心点と半径を指定するようになっているけれど、内部的には左上(バウンディングボックス)と半径を保持するようになっています
    • これはFLTKのfl_arc()がそういう仕様(すなわち円だけじゃなく楕円も書けるようになっている)だからですね
  • ここで大事なのは APIと実装が分離しているので柔軟性が高くなっている、ということです

13.13 Ellipse

  • 楕円ですね
  • 中心と長軸・短軸を指定するようになっています
  • 後から変えることも出来るようになってます
  • 焦点を返す関数も含んでるみたい
    • なんで矩形はないのにこいつにはresizeがついてるんだろ?
      • って、どうやら、次の話と関連してるみたい。Circleにするにはresizeが問題だなあーとかいう話
  • というわけで、何故、円を楕円と分ける必要があるのか、という議論
    • 実際問題円って楕円の一種じゃん、と
    • しかし分けておかないと Circleのつもりで作ったオブジェクトが「時には楕円、時には円」となってしまう=つまり表明が変わってしまう、不変条件が守れない、というエンジニアリング的な意味で問題がある、ということ
      • 納得した。ちと円と楕円という例では微妙ですが
  • 以下のような落とし穴に注意喚起
    1. 巧妙にしようとしすぎて変な迷路に嵌る(この場合だと、bool circlep; みたいなフラグを用意して長軸・短軸がリンクするようなムチャ仕様にしてしまう、とかかな?)
    2. 直感に頼りすぎてコード上では意味を成さないようなところを無理にクラス化しようする
  • そうではなくて、全体に一貫したコンセプトを持たせる必要がある、とのこと
  • 単なるデータと関数の塊みたいにしてしまってはだめ。無計画にコードを突っ込んだその手の生成物は単なる「ハック」
  • 「ハック」は他人への説明が不能で他人によるメンテ不能。そんなコードを書いてはいけません
    • 「オレ様さえわかればいいんだ!」って?
    • 数ヵ月後の自分は他人。ですよですよ、という話

13.14 Marked_polyline

  • 説明の順序が微妙な気もするけど、こっちがMarks, Markより先になってます
      • 実はMark(s)はこのMarked_polylineをベースにしてるのです。ややこしいですがまたMarksは単数形の Markとは別物。さらにMarksはMarkのベース。ううむ。なんか微妙
  • グラフの点をラベル付けする、とかそういった用途を想定
  Marked_polyline mpl("1234");
  mpl.add(...);  // labeled with "1"
  mpl.add(...);  // labeled with "2"
  mpl.add(...);  // labeled with "3"
  mpl.add(...);  // labeled with "4"
  • というような使い方
  • Open_polylineベースにこの各点にTextを配置する感じ
    • 例によって継承で Open_polyline::draw_lines()した後 各点に一文字ずつのっけてく実装
    • mark_string[i%mark.size()] としてるので、指定したMarkが尽きると頭からサイクルするようになってる

13.15 Marks

  • 線なしのMarkを複数書きたい場合
  • 使い方はMarks作って addするというもの。1オブジェクト内に複数配置可能
  Marks mark("x");
  mark.add(...);
  mark.add(...);
  mark.add(...);
  • 実装は実はMarked_polylineのset_colorをColor::invisibleにして見えなくしてるだけ・・・
      • うーーーん
  • 初期化子での指定コンストラクタの呼び方が出てます
    • といっても単にそのままクラス名を書くだけですが
  // constructor
  Marks(const string& m) : Marked_polyline(m) {
   :

13.16 Mark

  • こっちは単一、単数形の Mark(ややこしい)
  • もともと不可視な概念であるPointを見えるようにするのがこのMark
  • この順番になっていたのは Mark が Marksを継承していたため
      • とか、なんでaddがprotectedかもこのクラスみたいなののためか・・・
      • add()なんてさせません、とかいうのが出来るように、なのね
  • というわけで add()できない(コンストラクタで決め撃ち)Marksです
  • ほんとにこんなんいるの? という疑問には論理的な答はないです
    • 試行錯誤の末に「まあ大して大変じゃないからいいか」というノリ

13.17 Images

  • gifとかjpegとか、リモート、ローカルを問わず大量にありますよね
  • それImageクラスで::使い方は簡単でコンストラクタに画像ファイルを指定してオブジェクト生成。あとはWindowにattachするだけ、という感じ。
  • set_mask(Point, int w, int h)をつかうとどの位置に表示するかを設定できます
  • ちなみに対応フォーマットは GIF, JPEGのみ。 struct Suffix::Encoding としてenumってあります
  • Image はコンストラクタで指定されたファイルをオープンして、大抵は拡張子から、あるいは引数で指定されたフォーマットとして読み込もうとします
    • だめだったらエラーイメージとして Bad_Image クラスを表示します
    • これは 'cannot open "filename"'みたいなラベルとバッテンマークつきのboxです。
      • バッテンマークとboxは FLTKの draw_empty()で描いてます
  • 無指定時のファイルフォーマットはget_encoding()でチェックしていますが、この関数、ファイル名しか見ていません(ヘッダ見てない)
  • 結構途中にFLTKがらみでポインタを使ったコードがあるのですが、17章までは「実装の詳細」ということでにげるっぽいです
  • あとは「ファイルがオープンできるか?」を調べる関数どーすんの?と
    • 単純 ifstream()でストリーム作ってその値をboolのコンテキストでチェックすればOK
    • オープンしてすぐクローズってなんかもったいないけど、まあうまく行くので良しと
      • なるほどね
    • get_encodingはstd::mapを使った例にもなっているらしい
      • FLTKは RGB, PNM, XBM, BMP, PNG にも対応しているので、演習としてこれ作ってみても良いかも