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);
        }

2012年4月28日土曜日

boost::spirit::qi ドキュメント探訪

boost::spirit は、構文解析を行うためのテンプレート・プログラミングです。一見、難しそうに見えますが、ドキュメントの歩き方を知っていれば、そんなに難しくありません。 boost 1_49_0 時点では、ドキュメントは、以下になります。 http://www.boost.org/doc/libs/1_49_0/libs/spirit/doc/html/

Quick Reference - Qi Parsers

まずは、Quick Reference の Qi Parsers を見ましょう。 http://www.boost.org/doc/libs/1_49_0/libs/spirit/doc/html/spirit/qi/quick_reference/qi_parsers.html

Character Parsers

http://www.boost.org/doc/libs/1_49_0/libs/spirit/doc/html/spirit/qi/quick_reference/qi_parsers/char.html 文字パーサでは、どの文字コードが、どんな表現に該当するか?が記述されています。 表のうちExpression が、プログラムコード中で書く表現です。ここに alnum と書いてあれば、namespace が boost::spirit::qi なので
  boost::spirit::qi::alnum
という表現をコード中に書く事になります。Expression 中の Ch とは、文字列で、
  boost::spirit::qi::lit("command")
というように文字列リテラルを指定します。Ch 等の説明は  Quick Reference の Common Notation に解説されています。  http://www.boost.org/doc/libs/1_49_0/libs/spirit/doc/html/spirit/qi/quick_reference/common_notation.html 表のうちAttribute は属性で 、Unused は「マッチしても値として取り出す事はしませんよ」という意味で、Ch は「マッチした文字をそのまま値として採用しますよ」という意味になります。 表の Description は、説明です。 参考までに、任意回数のマッチを表す * を利用すると
  *boost::spirit::qi::digit
は、138793285 などの連続した一連の数字にマッチし、マッチした文字列を抽出できます。

Numeric Parsers

http://www.boost.org/doc/libs/1_49_0/libs/spirit/doc/html/spirit/qi/quick_reference/qi_parsers/numeric.html 数値パーサでは、std::atoi std::atof といったような数値の文字列を解釈して数値に変換するための表現が記述されています。 Expression がコードに書く部分で、Attribute は変換される型になります。
  boost::spirit::qi::long_
とすると、12345 という表現にマッチした場合は、long 型に変換されます。

String Parsers

http://www.boost.org/doc/libs/1_49_0/libs/spirit/doc/html/spirit/qi/quick_reference/qi_parsers/string.html 少し、Character Parser と被ります。ここでの注目は、symbols でしょう。 symbols は、テンプレートになっており、任意の文字列を任意の型のある値へと変換するパーサのクラスを作成する事ができます。 たいていは、wchar_t ではなく char 型を構文解析にかけるので、symbols の最初に指定する型は char 型です。論理値 bool 型に変換したい場合は、以下のように書きます。
  boost::spirit::qi::symbols<char,bool>  bool_symbol;
とします。ただ、これではパースすべき文字列がひっかからないので add 関数で、以下のようにマッチすべき文字列と値の組み合わせを、事前に追加しておきます。
  bool_symbol.add("true",true)("false",false);
変な構文に見えますが、operator() ( const char*, bool ) を使ったトリックで構成されていたように思います(未確認なんで嘘かもしれません)。

Auxiliary Parsers

http://www.boost.org/doc/libs/1_49_0/libs/spirit/doc/html/spirit/qi/quick_reference/qi_parsers/auxiliary.html ここは、若干高度で説明も難しいです。Description を見てお茶を濁しておきましょうwww eol は改行なんで、わかると思いますが、パーサでスペースは対象外にするとか、skipするパターンの指定の仕方によっては、改行もスキップされるので、使い所が無いかもしれません。 eps だけ、ちょっと解説を・・・。
  boost::spirit::qi::int_ >> boost::spirit::qi::eps >> boost::spirit::qi::lit(",")
こうした場合に、123435という数値パターンにマッチした場合、空気のように eps は無条件にマッチします。実は、パーサ要素の後ろに [] をつけると、[]の中にアクション関数を設置すれば、[]の中のアクションをマッチ後に実行するという動作を記述できます。こうする事によって、地雷(踏んだら爆発する)を設置する事ができます。 特定の文字(例えば"hello")にマッチしたかどうかを調べて、何事も無かったかのように振る舞う事もできます。
  boost::spirit::qi::eps("hello") >> boost::spirit::qi::lit("hello")
上記の例では、epsで "hello" にマッチしなかった場合は次に通しませんが、"hello"にマッチした場合は、eps に入る前の状態に巻き戻して、次のパーサへ引き継ぎを行います。まるで忍者の偵察部隊のように何事も無かったかのように次の状態をアクション無し副作用無しで調査してくれるので、先読み条件分岐として利用する事ができます。
2013/2/16:訂正 旧spritのeps_pと異なり、現在は 式のみ受け付けるようです。従って、この構文はコンパイルを通りません(残念)。通過後に構文違反になったらエラーを確定させたい場合には、>> 演算子の代わりに > 演算子を利用します。先読みは、通常の構文解析 >> を利用して | 接続させてください。

Binary Parsers

http://www.boost.org/doc/libs/1_49_0/libs/spirit/doc/html/spirit/qi/quick_reference/qi_parsers/binary.html Numeric Parsers と似てるので飛ばします。

Auto Parser

http://www.boost.org/doc/libs/1_49_0/libs/spirit/doc/html/spirit/qi/quick_reference/qi_parsers/auto.html 何かにマッチする・・・便利すぎて効能をちょっと思いつきません

Parser Directives

http://www.boost.org/doc/libs/1_49_0/libs/spirit/doc/html/spirit/qi/quick_reference/qi_parsers/directive.html lexeme[] の[]中でのパースはスキップする文字列を無効にします。no_case[]の[]中でパースは大文字小文字の区別をしないようにします。raw[]の[]中をひとつの塊として見なします。repeat[]の[]中のパターンを指定回繰り返します。サンプルもあるので研究してみてください。

Parser Operators

http://www.boost.org/doc/libs/1_49_0/libs/spirit/doc/html/spirit/qi/quick_reference/qi_parsers/operator.html 何回マッチするか? - + *  
Aというパターンの次にはBというパターンにマッチします >>
AというパターンかBというパターンにマッチします |
といったパーサに対する関係演算子について書かれています。

Parser Semantic Actions

http://www.boost.org/doc/libs/1_49_0/libs/spirit/doc/html/spirit/qi/quick_reference/qi_parsers/action.html パーサを()で括って塊にし、 [] をつける事で、[]中にアクションを指定できます。アクションは、関数だったり、phoenix という奇天烈な template だったりします。

Quick Reference - Phoenix

http://www.boost.org/doc/libs/1_49_0/libs/spirit/doc/html/spirit/qi/quick_reference/phoenix.html …すみません、嘘をついてました。魔術でした。ごめんなさい…。

2012年4月25日水曜日

boost::filesystem 同一ファイルを削除

画像処理をしていて、空白だけの画像を削除したいと思って作りました。 空白だけの画像ファイルを、どっかにコピーして、そのファイルと同じファイルならディレクトリーをトラバースして削除していきます。シェルスクリプトでも書けるんでしょうけど、C++偏執狂には、こっちの方が処理コストが安い。
#include <boost/filesystem.hpp>
#include <boost/scoped_array.hpp>
#include <fstream>
#include <iomanip>
#include <iostream>
namespace fs = boost::filesystem;

void traverse_directory( const fs::path& p, int fsiz, const char* buf );

void traverse_directory( const fs::path& p, int fsiz, const char* buf ) {
  fs::directory_iterator de;
  fs::directory_iterator ds( p );
  for( fs::directory_iterator p = ds; p != de; ++p ) {
    if( fs::is_directory( *p ) ) {
      std::cout << "D:" << *p << std::endl;
      traverse_directory( (*p).path(), fsiz, buf );
    } else {
      int nsiz = fs::file_size( (*p).path() );
      if( nsiz == fsiz ) {
        boost::scoped_array<char> buf2( new char [nsiz] );
        {
          std::ifstream ifs( (*p).path().generic_string().c_str(), std::ios::in | std::ios::binary );
          ifs.read( buf2.get(), nsiz );
        }
        if( 0 == memcmp( buf, buf2.get(), nsiz ) ) {
          std::cout << "same delete : " << *p << std::endl;
          fs::remove( *p );
        }
      //} else {
        //std::cout << *p << std::endl;
      }
    }
  }
}

void show_usage() {
  std::cout << " ファイルをバイナリで比較して、同じものを削除します" << std::endl;
  std::cout << "nulldel [root folder] [compare file]" << std::endl;
}

int main(int argc, char* argv[] ) {
  if( argc != 3 ) {
    show_usage();
    return 1;
  }
  int fsiz = fs::file_size( argv[2] );
  boost::scoped_array<char> buf( new char [ fsiz ] );
  {
    std::ifstream ifs( argv[2], std::ios::in | std::ios::binary );
    ifs.read( buf.get(), fsiz );
  }
  traverse_directory( argv[1], fsiz, buf.get() );
  return 0;
}

2012年4月19日木曜日

boost::interprocess::unique_ptr 習作

カスタム・デリータが欲しくて、排他制御なんていらん。そういう場合。
#include <boost/interprocess/smart_ptr/unique_ptr.hpp>
#include <boost/function.hpp>
#include <iostream>

struct hoge {
  hoge() { std::cout << "hoge()" << std::endl; }
  ~hoge() { std::cout << "~hoge()" << std::endl; }
};

struct my_deletor {
  void operator() ( const hoge* h ) const {
    std::cout << "my_deletor" << std::endl;
    delete h;
  }
};

void my_deletor2( const hoge* h ) {
  std::cout << "my_deletor2" << std::endl;
  delete h;
}

using boost::interprocess::unique_ptr;


int main() {
  unique_ptr<hoge,my_deletor> hptr( new hoge, my_deletor() );

  unique_ptr<hoge,boost::function<void(const hoge*)> > hptr2( new hoge, my_deletor2 );

  return 0;
}

2012年4月17日火曜日

boost::any 習作



#include <iostream>
#include <boost/range/algorithm/for_each.hpp>
#include <boost/any.hpp>
#include <vector>


void disp( int n ) {
  std::cout << n << "," ;
}

int main(void) {
  std::vector<int> v;

  for( int i = 0; i < 5; ++i )  v.push_back( i );

  boost::any a = v;

  const std::vector<int>& vr = boost::any_cast< std::vector<int> >( a );

  boost::for_each( vr, disp );

  return 0;
}

boost::range 習作 (4)

#include <boost/bind.hpp>
#include <boost/ref.hpp>
#include <boost/range/algorithm/for_each.hpp>
#include <iostream>


bool hoge( int i, int j, int k ) {
   return (i+j) < k;
}

void hoge_comp( int i, int j, int k, bool& result ) {
  std::cout << i << "+" << j << " < " << k << std::endl;
  result &= hoge( i, j, k );
}

int main(void) {

  int x[] = { 1, 2, 3, 2, 1 };
  int y[] = { 1, 4, 9, 4, 9 };

  bool result = true;
  boost::for_each( x, boost::bind( hoge_comp, _1, 1, 5, boost::ref(result) ) );
  
  std::cout << "result = " << (result ? "true" : "false") << std::endl;

  result = true;
  boost::for_each( y, boost::bind( hoge_comp, _1, 1, 5, boost::ref(result) ) );

  std::cout << "result = " << (result ? "true" : "false") << std::endl;

  return 0;
}

2012年4月8日日曜日

google+ を続けて思った事など

以前は、Google+ ギークっぽい人が多くて面白い。と思っていたのですが、続けていて感覚もだいぶ変わってきました。自分は、Twitter をやらないんで、その辺の実感がなかったんですが、Google+ もTwitterに近いのかも?なんて感触に変わってきてます。サークルがあって、サークルのカテゴリで集結すると想像していたんですが、実際には、誰彼構わずサクって(サークルに入れる)、広告目的とか変なんじゃなければ、サクり返す。もうそんな感じです。あと、一般ユーザのレベルには、理解しにくいのか、やたらめったらプログラマー率が高いような気もしてます。  さて、いろんな人と接する機会が増えてくると、やっぱり、色々な考え方を持っている人と接する機会が増えてきます。自分は、大人げないところが多分にあって、ついつい主張が強くなる傾向があり、反省しなければいけないところです。それは、一旦、横に置いてみて、主張を押し付けてくる人というのは、傍から見ていて痛い感じがします。つか、痛い人だと思います。あと、ネガティブな人というのも、周囲に毒を撒いているようで、痛いのかなぁと思います。自分も「JAVA死ね、GC死ね」とかネガティブな事言ってるのは、痛いと思います。こんな事書いても、喜んでくれるのは、C++界隈のごく限られた人たちだけだとも思います。  人と人の間には、正負関係なくエネルギー差が生じるので、SNSにどっぷりはまると疲れます。特に、利害関係の無い者同士だと、押し付けは、理由無き理不尽に陥りがちなので、気をつけないといけない思います。利害関係があれば、その部分に関しては、共通の目的に向かっていけるんで、良いのですが・・・。  圏域も勝手に醸成されてるような気がしてます。サークルも複雑すぎて、結局は、ごった煮で、うまく行ってないような気がしてます。自分で言えば、一般公開・プログラミング・リアルの友人、以上の3択ですが、実質は、前者の2択です。情報の受け口がオープンすぎて、グーグルが勝手に調整してフィルタリングをかけて絞ってる感じです。なんでもかんでも入ってくると疲れます。あと、自分の希望とは違うものが、どんどん流れこんできます。入り口サークルと出口サークルの2系統に別けて、スライダーで流入量を調整しようかと思った事もありますが、正直、面倒くさいです。  あと、情報は、あくまで情報に過ぎないんで、必死になって収集してもしょうがないのかな〜というのを感じます。面白い反面、ちょっと疲れてきてるのかも・・・。という事で、SNSは利害関係のはっきりしているFACEBOOKの方に軍配が上がるのかもしれないな?とも思うようになりました。こんな事を書いたら、ググタスの人たちに叩かれないかな?といったところで、しめておきたいと思います。  また、Google+に対する感覚が変わったら、書くと思います。

2012年4月6日金曜日

windows7も眠らない?


 シャットダウン中に consent.exe が現れた!どうしますか?

 while(1) {

   タスクマネージャで consent.exe プロセスを終了する。

   権限がありません、拒否されました!どうしますか?

   シャットダウンを試みる

   返事が無い。ただの屍のようだ(黙りこくったまま、反応がありません)。
   

   秘儀、管理者権限でコマンドプロンプトからシャットダウンする事にする。
     右ボタンの管理者権限で、コマンドプロンプトを実行する

   おおーーーっと! consent.exe は仲間を呼んだ!どうしますか?

   if( あきらめた ) {
      // 電源を長押しして、強制終了してください
      // 死んでいいぞ

   }


 }