PPPUC++#12.7

10.8 User-defined output operators

  • 自前の << を作る事自体は実は簡単
  • 難しいのは、どう表現するのか?というところ
    • 2011/03/05なのか 2011-03-05なのか Mar 5th 2011なのか・・・
    • 誰もが納得できる表示と言うのは難しいですね
  • しかしそれでもとりあえず作っておくのは役に立ちます。特にデバッグ時や開発初期など
      • LL言語では classに to_s, repl, __str__などなどそれ相当のものがたいていありますね
      • コンソールで直接いじれるのでよけいに重要と思うです
  • 後からキレイに清書すれば良いし、それでもダメなら普通にメンバにアクセスして表示するヘルパー作れば良いのだ
  • Dateクラスでは (2011,03,05) てな感じに表示している。こういう単なるリスト表示ってのはメンバが少なくて、他に良いのが見つからないときとかによく使います
  • 演算子オーバーロードって要するに対応する関数が呼ばれるだけなんだけど、cout << "hoge" のような二項演算子の場合は (左辺値, 右辺値)という並びで呼び出されます
  • operator << ()は返り値として ostream& を返すので、cout << "hoge" << "moge" のようにチェインが可能です.
    • (cout << "hoge") << "moge" ---> (ostream = cout) << "moge" .....

10.9 User-defined input operators

  • 自前の >> はエラーの処理が結構大変
  • Dateの場合はどうなるかって言うと
istream& operator >>(istream& is, Data& dd)
{
  int y,m,,d;
  char ch1, ch2, ch3, ch4;
  is >> ch1 >> y >> ch2 >> m >> ch3 >> d >> ch4; /// マジカイ!w
  if (!is) return is;
  if (ch1 !='(' || ch2 1= ',' ......) /// again マジカイ!
    is.clear(ios_base::failbit);
    return is;
  }
  dd = Date(y, Data::Month(m), d);    // hey! how about validation of month????
  return is;
}
      • ・・・・・ぽかーん。。。。。ここまでやるなら sscanf()、、、いや scanf()で良いじゃん。。。
  • いやいや、、、
    • つーか
  • いやいや、、、
    • つーか
std::istream& operator>>(std::istream& is, const char& c)
{
    char ch;
    is >> ch;
    if (c != ch) {
        is.unget();
        is.clear(std::ios::failbit);
    }
    return is;
}

// cin >> '(' >> y >> ',' >> m >> ',' >> d >> ')'; とかできるようになる
    • なぜにこーゆーのが標準ライブラリに無いんだろう? だれかおしえて
    • 気を取り直して・・・
  • これだけでも厄介なのですが(自分で厄介にしているだけな気も)エラーがあった場合は目も当てられません
  • 理想的には、使わなかった文字は消費すべきではないんだけど、、、
    • 実際には本書のプログラムだといらんところまで捨ててしまう(もろcharに読んでるので・・・)
    • まあそうでなくても(2011,3,5} とか最後の文字だけ間違ってる場合、 } を読むまでは進んでしまうので(入力を消費してしまうので)リカバリは非常に困難
  • unget()が保証しているのは一文字までのロールバックなので、これはちょっと無理ゲー
  • 値がおかしかった場合、Dateのコンストラクタが例外発してくれるはず
    • ほんま? monthのエラーには脆弱だと思うケド(実はDateのコードはdayに関しては閏年まで考慮しているが、monthのvalidateは enumに頼っている(コンパイル時情報のみに頼っている))
      • うーむこのへんボロボロですね・・・

10.10 A standard input loop

  • データ列をリードする場合の入力ループについての考察
  • 全行読めると言う前提で書いていたけど良いのか?
    • OK。なぜなら読めるところまで読んでみて、そこで終端かどうか確かめれば済むから
    • 大抵は各行のフォーマットチェックと、終端部分のチェックは独立してやるのでオKなのです
  • で、あらためて istream::exceptions()の話とかで input loopをリファクタリングしていくんだけど、、、
    • ちょっと芸が無い。また関数に切り出すのね
  • 終端文字、というのを用意しておくのは便利なことが多いとか。データ構造がネストしてて、内部レベルで止めたい場合とか。。。
      • 個人的には反対。帯域外データというのは帯域内データと混同しては行けない、というのが僕の考え。ネスト構造は構造をちゃんと追うべき。適当に文字列処理するのはイクナイ

10.11 Reding a structured file

  • 上のを具体的に使ってみる
  • 温度ファイル
{ year 1990}
{year 1991 {month jun}}
{ year 1992 {month jan ( 1 0 61.5) } {month feb (1 1 64) (2 2 65.2) } }
{year 2000
      { month feb (1 1 68) (2 3 66.66 ) (1 0 67.2)}
      { month dec ( 15 15 -9.2) (15 14 -8.8) (14 0 -2) }
}
  • てな感じのファイル。 year が年指定で {} でネストしてて、 month で月指定、 ()の中が実際の値で 日、時間、温度という順のデータ
  • ヘンテコ。けどXML全盛の今でもこういうのはあるし、一般に仕事では入力を選べない
    • あまりにヒドけりゃフォーマットコンバータ使えとの事。 んーそうしようか(違
10.11.1 In-memory representation
  • 上記の表現のためにYear, Month, Readingをつくるんだけど、、、
      • んーこのへんほんとヒドい。day のvector添字とint表現がずれてたりとか。しかもそのためにdy vectorの確保数が32とか。うーん、美しくな過ぎる。この章だけヒドい過ぎる
  • 一応 year, monthは自分の値と下にvectorを持つイメージ
10.11.2 Reading structured values
  • Reading(実際の観測データ)はvector要素なしで素直に 日、時間、温度。
    • しかし美しくない事に Reading::operator>>()は一文字だけ チェックしてとか・・ 確かにね、このフォーマットの場合 Readingが何個続くかわからないので先読みが必要なのはわかるのですが、別に failbitたてるだけでぇもぉぉぉお・・・
      • ugu- やる気がぁ(汚いコードってやる気をそぎますよね・・・)
10.11.3 Changing representations
  • 月名のテーブルを最初に初期化しましょうね、という話。
  • 本文でも書いてますが map使うべきですよね。