2012年5月31日木曜日

整列した std::vector に挿入して、またアクセス備忘録

未だに、結構こういう、くだらない事を忘れてしまい。試すとか、よくあります。

#include <vector>
#include <iostream>
#include <algorithm>

int main() {
  std::vector<int> v;
  // 10, 20, ..., 90 と整列した配列に対して
  for( int i = 1; i < 10; ++i )  v.push_back( i * 10 );
  // 25 を挿入したい
  std::vector<int>::iterator f = std::lower_bound( v.begin(), v.end(), 25 );
  // 挿入する位置は、わかったが、後からアクセスする場合に備えて
  // 場所を覚えておきたい
  int pos = std::distance( v.begin(), f );
  // とりあえず挿入
  v.insert( f, 25 );
  // 挿入された位置は、ここですよ
  std::cout << pos << std::endl;
  // ほんまかいな?
  std::cout << v[ pos ] << std::endl;
  // ちゃんと並んでますか?
  for( int i = 0; i < 10; ++i ) { std::cout << v[i] << ","; }
}

追記: コメントもらった。 C++11 だと、insert が 挿入位置を返すので単に
  f = v.insert( f, 25 );
で、OKのようです。

2012年5月18日金曜日

soci-3.1.0 on windows build 忘備録

SOCI 一部、boost::geometry のサンプルにも使われています。SQL-Client 接続のライブラリです。 個別のライブラリでゴリゴリ書くのが、だんだんと面倒くさくなってきたし、ビルドが cmake に移行してきたので、コンパイルに挑戦してみようかなぁと。  まず、soci-3.1.0 ディレクトリに移動して、
C:\Downloads\soci-3.1.0> mkdir build
と、build ディレクトリを作成します。 今回は、sqlite3, postgresql, odbc をターゲットに構築してみました。sqlite3 や postgresql のライブラリの構築は、ここでは扱いません。 以下のような build.bat を作成しました。
cd build
cmake .. -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release ^
  -DWITH_SQLITE3=ON ^
  -DSQLITE3_INCLUDE_DIR=c:/libs/sqlite3/include ^
  -DSQLITE3_LIBRARIES=c:/libs/sqlite3/lib ^
  -DSQLITE3_LIBRARY=c:/libs/sqlite3/lib/sqlite3.lib ^
  -DSQLITE_INCLUDE_DIR=c:/libs/sqlite3/include ^
  -DSQLITE_LIBRARIES=c:/libs/sqlite3/lib ^
  -DSQLITE_LIBRARY=c:/libs/sqlite3/lib/sqlite3.lib ^
  -DWITH_ODBC=ON ^
  -DWITH_POSTGRESQL=ON ^
  -DPOSTGRESQL_INCLUDE_DIR=C:/libs/postgres/include ^
  -DPOSTGRESQL_LIBRARIES=C:/libs/postgres/lib/libpq.lib ^
  -DBOOST_RELEASE_VERSION=1.49.0 ^
  -DBOOST_INCLUDE_DIR=C:/downloads/boost-1_49_0 ^
  -DBOOST_LIBRARIES=C:/downloads/boost-1_49_0/stage/lib
cd ..
変な ^ マークは、バッチファイルを複数行連結させる時のおまじないです。linux 系では \ に相当します。
ドキュメントには、SQLITE_INCLUDE_DIR, SQLITE_LIBRARIES を設定しろと書いていますが、間違ってます。
SQLITE3_INCLUDE_DIR が正しく、同様に SQLITE3_LIBRARIES かと思いきや CMakeFiles.txt 中で SQLITE3_LIBRARY から SQLITE3_LIBRARIES が思い切り上書きされています。ですので、SQLITE3_LIBRARY に sqlite3 の libディレクトリファイルを指定します。
postgresql も libpq.lib を指定します。

初期ポストで、ここに示していた修正は間違っていたので消去しました


windows だと、リンクに失敗しますので、soci-3.1.0/backends/soci-postgresql.h を修正します。
...
#ifndef SOCI_POSTGRESQL_H_INCLUDED
#define SOCI_POSTGRESQL_H_INCLUDED

#ifdef _WIN32
# ifdef SOCI_DLL
#  ifdef SOCI_POSTGRESQL_SOURCE
#   define SOCI_POSTGRESQL_DECL __declspec(dllexport)
#  else
#   define SOCI_POSTGRESQL_DECL __declspec(dllimport)
#  endif // SOCI_POSTGRESQL_SOURCE
# endif // SOCI_DLL
// ここの2行を追加します。
#pragma comment( lib, "ws2_32.lib")
#pragma comment( lib, "secur32.lib")
#endif // _WIN32
//
// If SOCI_POSTGRESQL_DECL isn't defined yet define it now
...
それでは、準備が整いましたので、soci-3.1.0 のディレクトリに移動して
C:\Downloads\soci-3.1.0> build
C:\Downloads\soci-3.1.0> cd build
C:\Downloads\soci-3.1.0\build> nmake
以上で完了です。 今回、spatialite は使用しませんでした。 インストールは、管理者権限で nmake install です。 2012/05/18 追記:どうも、うまくコンパイルできていないくさい・・・ backends/*/CMakelists.txt の変更が間違ってると思われるので削除。 2012/05/18 追記:修正して追記。 2012/05/18 追記:同じ所で、ひっかかった。http://www.mail-archive.com/soci-users@lists.sourceforge.net/msg00931.html 2012/05/18 追記:うーん・・・やっぱ、ゴリゴリ書くか・・・

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