2012年4月30日月曜日

isctype.c Expression: (unsigned)(c + 1) <= 256 に漂う地雷臭

事の発端は、boost::spirit::qi で utf-8 のコードをパースしようとした事から始まった。 最初は、boost::spirit::ascii::space_type を使ってみたんですわ。ところが、どうも ASSERTION に引っかかる。どうやら、マルチバイトセット系は boost::spirit::standard 系を指定しろという話らしいんで、boost::spirit::standard::space_type を使うように切り替えてみたんですわ。mac 上のgcc でコンパイルしてみて通る感じだったんで、まぁ、いいのかな?と思ってました。  ところが、VCのデバッグ環境で、isctype.c Expression: (unsigned)(c + 1) <= 256 とか言うのに引っかかるみたいなんですわ。 もしかして、なんか全然違うところでロケールの設定をしておかないといけないのかな?って、嫌ーな気持ちになりました。とりあえず、この辺でぐぐってみると、目についたのは、この辺の議論 http://boost.2283326.n4.nabble.com/Spirit-Qi-How-do-I-use-UTF-8-encoding-with-Qi-td2683321.html  他には https://svn.boost.org/trac/boost/ticket/5086  あー、やっぱりかーというような対処方法。  遠い過去の記憶が蘇ってきました。 http://ml.tietew.jp/cppll/cppll/thread_articles/123 やっぱ地雷臭がプンプン・・・。char 型の符号の定義自体が曖昧なんで、気持ち悪さも加速していると思います。  ごちゃごちゃ考えてても目の前の現実には向き合わないといけないので、boost/spirit/home/support/char_encoding/standard.hpp を眺めてみました。
  ...

    struct standard
    {
        typedef char char_type;

        static bool
        isascii_(int ch)
        {
            return 0 == (ch & ~0x7f);
        }

        static bool
        ischar(int ch)
        {
            // uses all 8 bits
            // we have to watch out for sign extensions
            return (0 == (ch & ~0xff) || ~0 == (ch | 0xff)) ? true : false;
        }

        static int
        isalnum(int ch)
        {
            return std::isalnum(ch);
        }

        static int
        isalpha(int ch)
        {
            return std::isalpha(ch);
        }

  ...

ぬぉおおおお、char 型から、int 型へキャストする時点で、もうオーバーフローしてるやん・・・。入り口が、あちこちに分散・・・。悲惨な状況・・・。UTF-8では、そもそも、8bit目以後ならば、false になる事が確定するので、
  if( ch >= 128 ) return false;
してしまう?それとも、地雷臭漂うロケールでも設定してみる?思うんだけど、この辺の is* 系って、そろそろ UTF-8 が標準の考え方でも全然差し支え無いんと違うのかな? 追記: 問題のパーサのコードは、こちら 更に追記: どうもやっぱ この standard.hpp の部分以外で引っかかっている用であるいた。ちゃんとデバッガ使って、コールスタック調べる事にしよう…。リリース版で実行すると、特に弊害は無いように見える。 更に追記:vc9 で確認した。以下のような修正を入れるのがベストなのかどうか? is* 系に対しては、とする。
        static int
        isalnum(int ch)
        {
          // return std::isalnum(ch);
          return std::isalnum((ch < 0) ? 0 : ch);
        }

0 件のコメント: