ICU charset detector
ICU(International Components for Unicode)というライブラリのなかにcharset detectorという文字コード/文字種判定器がはいってます。
使い方は下のような感じ
#include <iostream> #include <fstream> #include <unicode/ucsdet.h> #include <string> int main(int argc, char** argv) { if (argc > 2) return -1; std::string filename(argv[argc-1]); std::ifstream ifs(filename.c_str(), std::ios_base::in); if (!ifs.is_open()) return -1; UErrorCode error = U_ZERO_ERROR; UCharsetDetector* detector = ucsdet_open(&error); if (U_FAILURE(error)) return -1; std::string buffer; buffer.resize(65536); ifs.read(&*buffer.begin(), buffer.length()); buffer.resize(ifs.gcount()); ucsdet_setText(detector, buffer.c_str(), buffer.length(), &error); if (U_FAILURE(error)) return -1; int32_t founded = 0; const UCharsetMatch** matchers = ucsdet_detectAll(detector, &founded, &error); if (U_FAILURE(error)) return -1; if (founded > 0) std::cout << "filename: " << filename << " name: " << ucsdet_getName(matchers[0], &error) << " language:" << ucsdet_getLanguage(matchers[0], &error) << std::endl; ucsdet_close(detector); }
まぁこんな感じでさして使うのも難しくないライブラリなんですが、クラスでラッピングしていったら何を判定してもShift JISとしか判定しない@Windows Visual C++ 2008 Express と言う現象が・・・
最初はwindows版のbuild済みライブラリを使ってたのでそっちが変なのか?と思ってソース落としてきてbuildしてみて動かす・・・駄目。
しょうがないので上の様に素直なコードをクラス版と併記して動かしてみると素直バージョンの方ではちゃんと判定しやがってます・・・。
クラス内部ではメソッドごとにUErrorCode変数を作ってたのでもしかしてUErrorCode変数のアドレスにコンテキスト束縛してるかも?(ないない)と思ってメンバ変数にしてみるも駄目。
あとは怪しそうなのはucsdet_setText()に対しての文字列データ渡してる部分くらいしか・・・ということでucsdet_setText()をを呼び出してるメソッドを見直し。
最初の時点では以下の様になってました。
template <typename iteratorT> void operator()(iteratorT head, iteratorT tail) { std::vector<char> buffer(head, tail); UErrorCode error = U_ZERO_ERROR; ucsdet_setText(detector, &*buffer.begin(), buffer.size(), &error); ... }
こんな感じでイテレータを受け取って内部でバッファに詰め込んでから改めてucsdet_setText()にながしこんでます。
で、以下の様に変更
void operator()(const char* head, size_t length) { UErrorCode error = U_ZERO_ERROR; ucsdet_setText(detector, head, length, &error); ... }
そうすると・・・動きやがったよ・・・
まぁ、大体その時点で想像はついてたんですが裏を取るためにICUのソースを読みにいきました。
まずはucsdet_setText()・・・
U_CAPI void U_EXPORT2 ucsdet_setText(UCharsetDetector *ucsd, const char *textIn, int32_t len, UErrorCode *status) { if(U_FAILURE(*status)) { return; } ((CharsetDetector *) ucsd)->setText(textIn, len); }
これだけです。
一応ucsdの具象クラスをucsdet_open()みて確認します。
CharsetDetector* csd = new CharsetDetector(*status);
とやってるのでポリモルフィックではないようです。
安心してCharsetDetector::setText()を見にいきます。
void CharsetDetector::setText(const char *in, int32_t len) { textIn->setText(in, len); fFreshTextSet = TRUE; }
さらにメンバ変数に渡してました。
型を調べるとtextInはInputText型だそうです。
さらに追跡。
void InputText::setText(const char *in, int32_t len) { fInputLen = 0; fC1Bytes = FALSE; fRawInput = (const uint8_t *) in; fRawLength = len == -1? uprv_strlen(in) : len; }
旅が終わりました・・・中でコピーしてucsdet_detect/ucsdet_detectAllに備えてるはず!!と最初に思ってたところは見事に参照してるだけでした・・・
要するに自分のクラスのコードでは・・・
- std::vector
にデータをコピー - ucsdet_setText()に渡す
- 中ではstd::vector
のメモリ位置を保存して帰る
- 中ではstd::vector
- メソッド抜けてstd::vector
が破棄される - detect/detectAll()するメソッドを呼び出す
と言う愉快なことになっていた様です・・・
linuxとwindowsで試してみてたんですがlinuxだとちゃんと動いてたんですが削除時に0xddで埋める、というwindowsのデバッグモード用アロケータの効果が働いていたおかげだった模様。
まぁ、勝手に安全側に振ってあるだろう(内部でコピー持ってるはず)と思ってた自分が悪いんですが、ライブラリなんだから安全側に振っといてほしいってもんです・・・
型配列
複数の型をlistに束縛してnthで取り出してみる。
中身はlispちっくにconsセルでつなげてます。
#include <iostream> #include <typeinfo> struct nil_t {}; template <typename... args> struct cons_cell; template <typename first, typename... rest> struct cons_cell<first, rest...> { typedef first car; typedef cons_cell<rest...> cdr; }; template <typename last> struct cons_cell<last> { typedef last car; typedef cons_cell<nil_t> cdr; }; template <> struct cons_cell<nil_t> { typedef cons_cell<nil_t> car; typedef cons_cell<nil_t> cdr; }; template <typename ... args> struct list; template <typename first, typename... rest> struct list<first, rest...> { typedef cons_cell<first, rest...> type; }; template <typename type_list, int number> struct nth { typedef typename nth<typename type_list::cdr, number-1>::type type; }; template <typename type_list> struct nth<type_list, 0> { typedef typename type_list::car type; }; int main() { typedef list<int, char, short, long, double, float, unsigned int>::type types_list; std::cout << typeid(types_list::cdr::car).name() << std::endl; std::cout << typeid(nth<types_list, 5>::type).name() << std::endl; std::cout << typeid(nth<types_list, 6>::type).name() << std::endl; std::cout << typeid(nth<types_list, 7>::type).name() << std::endl; std::cout << typeid(nth<types_list, 8>::type).name() << std::endl; return 0; }
で
$ g++ -g -Wall -std=c++0x -o type_list type_list.cpp
$ ./type_list | c++filt -t
char
float
unsigned int
cons_cell
cons_cell
てなかんじです。
うーん、nil_tは中にcar, cdr用意しといた方がよかったな・・・
variadic template事始め
C++0xでvariadic templateを使って可変数引数の数を数えるプログラムを組んでみる。
使ったのはg++ Ubuntu 4.3.2-1ubuntu12
オプションは
g++ -g -Wall -std=c++0x -o variadic_templ variadic_templ.cpp
#include <iostream> #include <typeinfo> template <typename... arg> struct length; template <typename arg, typename... rest> struct length<arg, rest...> { enum { value = 1 + length<rest...>::value }; }; template <typename arg> struct length<arg> { enum { value = 1 }; }; int main() { typedef length<int, short, char, long, unsigned int> type_list; std::cout << typeid(type_list).name() << std::endl; std::cout << type_list::value << std::endl; return 0; }
最初はよー分からんかったが、id:faith_and_braveさんとこのコードを見てて不完全型宣言してから特殊化としてtemplate argumentが可変のものを用意すればいいのかなー?とやったら通った。
あとで規格の該当個所読んで裏取りしないとな・・・
しかしなれるまで苦労しそうだ・・・
lisp風連結リストだとわかりやすかったんだけどなぁ・・・
Y combinator
asin:4320122089 を読んでたら不動点演算子が出てきて組みたくなったのでJavaで久しぶりに書いてみた。
interface Command { public void exec(Command self, Object[] args); } public class YCombinatorTest { public static void main(String args[]) { int argValue = 10; if (args.length == 1) argValue = Integer.parseInt(args[0]); new Command() { public void exec(Command executor, Object[] args) { executor.exec(executor, args); } }.exec(new Command() { public void exec(Command self, Object[] args) { assert args.length == 2; int value = ((Integer)args[0]).intValue(); assert value > 0; int contValue = ((Integer)args[1]).intValue(); if (value == 1) System.out.println("value: " + contValue); else { self.exec(self, new Object[] { new Integer(value - 1), new Integer(value * contValue)}); } } }, new Object[] { new Integer(argValue), new Integer(1) }); } }
追記
あー、call 40が消えてるんじゃなくて下駄が4なのかな?
だとするとcallがrelocation情報ときれいに合うな。
けどそうするとなんで_allocaがchkstkに?という疑問が・・・
オブジェクトファイルの奇妙なcall命令
http://lucille.atso-net.jp/blog/?p=603
これなんですが、たしか参照解決前の関数エントリのインデックスだったような・・・
ということで確認してみる。
まずはコンパイルの準備。
ヘッダファイル。
extern "C" void test_c_func(); void test_cpp_func(); extern "C" void test_inline_c_func() {} void test_inline_cpp_func() {}
test.hとして保存。
んで本体。
#include "test.h" int main() { test_c_func(); test_cpp_func(); test_inline_c_func(); test_inline_cpp_func(); return 0; }
test.cppとして保存
で、コンパイル。
$ g++ -g -c test.cpp
そして逆アセンブル
$ objdump -dS test.o | c++filt -t test.o: file format pe-i386 Disassembly of section .text: 00000000 <_test_inline_c_func>: void test_cpp_func(); extern "C" void test_inline_c_func() {} 0: 55 push %ebp 1: 89 e5 mov %esp,%ebp 3: 5d pop %ebp 4: c3 ret 5: 90 nop 00000006 <test_inline_cpp_func()>: void test_inline_cpp_func() {} 6: 55 push %ebp 7: 89 e5 mov %esp,%ebp 9: 5d pop %ebp signed char: c3 ret bool: 90 nop 0000000c <_main>: char: 55 push %ebp double: 89 e5 mov %esp,%ebp float: 83 ec 08 sub $0x8,%esp 12: 83 e4 f0 and $0xfffffff0,%esp 15: b8 00 00 00 00 mov $0x0,%eax a: 83 c0 0f add $0xf,%eax d: 83 c0 0f add $0xf,%eax 20: c1 e8 04 shr $0x4,%eax 23: c1 e0 04 shl $0x4,%eax 26: 89 45 fc mov %eax,0xfffffffc(%ebp) 29: 8b 45 fc mov 0xfffffffc(%ebp),%eax 2c: e8 00 00 00 00 call 31 <_main+0x25> 31: e8 00 00 00 00 call 36 <_main+0x2a> 36: e8 00 00 00 00 call 3b <_main+0x2f> 3b: e8 00 00 00 00 call 40 <_main+0x34> 40: e8 bb ff ff ff call 0 <_test_inline_c_func> 45: e8 bc ff ff ff call 6 <test_inline_cpp_func()> 4a: b8 00 00 00 00 mov $0x0,%eax 4f: c9 leave 50: c3 ret 51: 90 nop 52: 90 nop 53: 90 nop 54: 90 nop 55: 90 nop 56: 90 nop 57: 90 nop 58: 90 nop 59: 90 nop 5a: 90 nop 5b: 90 nop 5c: 90 nop 5d: 90 nop 5e: 90 nop 5f: 90 nop
で、call部分の抜粋
2c: e8 00 00 00 00 call 31 <_main+0x25> 31: e8 00 00 00 00 call 36 <_main+0x2a> 36: e8 00 00 00 00 call 3b <_main+0x2f> 3b: e8 00 00 00 00 call 40 <_main+0x34> 40: e8 bb ff ff ff call 0 <_test_inline_c_func> 45: e8 bc ff ff ff call 6 <test_inline_cpp_func()>
inline化してるものはobjdumpでの逆アセンブル結果で関数名まで出てます。
問題はその上の4行分。
でobjdumpでいろいろみてたらそれっぽいのを見つけた。
$ objdump -r test.o test.o: file format pe-i386 RELOCATION RECORDS FOR [.text]: OFFSET TYPE VALUE 0000002d DISP32 __alloca 00000032 DISP32 ___main 00000037 DISP32 _test_c_func 0000003c DISP32 __Z13test_cpp_funcv RELOCATION RECORDS FOR [.stab]: OFFSET TYPE VALUE 00000014 dir32 .text 00000020 dir32 .text 00000158 dir32 .text 00000164 dir32 .text 00000194 dir32 .text 000001d0 dir32 .text 000001dc dir32 .text 00000254 dir32 .text
リロケーション情報にあった。なんでか1ずつオフセットずれてるみたいだけど・・・
確認のため、実行ファイルにして付き合わせてみる。
まずは外部関数の用意
extern "C" void test_c_func() {} void test_cpp_func() {}
test_link.cppとして保存。
で、コンパイルとリンク
$ g++ -g -c test_link.cpp $ g++ -g -o test test.o test_link.o
そして逆アセンブル
$ objdump -dS test.exe | c++filt -t (中略) 004012fc <_main>: 4012fc: 55 push %ebp 4012fd: 89 e5 mov %esp,%ebp 4012ff: 83 ec 08 sub $0x8,%esp 401302: 83 e4 f0 and $0xfffffff0,%esp 401305: b8 00 00 00 00 mov $0x0,%eax 40130a: 83 c0 0f add $0xf,%eax 40130d: 83 c0 0f add $0xf,%eax 401310: c1 e8 04 shr $0x4,%eax 401313: c1 e0 04 shl $0x4,%eax 401316: 89 45 fc mov %eax,0xfffffffc(%ebp) 401319: 8b 45 fc mov 0xfffffffc(%ebp),%eax 40131c: e8 4f 05 00 00 call 401870 <___chkstk> 401321: e8 ca 00 00 00 call 4013f0 <___main> 401326: e8 25 00 00 00 call 401350 <_test_c_func> 40132b: e8 26 00 00 00 call 401356 <test_cpp_func()> 401330: e8 bb ff ff ff call 4012f0 <_test_inline_c_func> 401335: e8 bc ff ff ff call 4012f6 <test_inline_cpp_func()> 40133a: b8 00 00 00 00 mov $0x0,%eax 40133f: c9 leave 401340: c3 ret 401341: 90 nop (後略)
スタック検査が挿入されてます。
あとcall 40が消えてる・・・
perlの多次元配列
perlの二次元配列の作り方がさっぱり - 誰か助けて orz - ir9Ex’s diaryの話題について反応。
ぶっちゃけperlなんてもう5, 6年まともに触ってないのでぜんぜん覚えてないのでもっときれいなやり方とかあったら誰か教えてください。
今回の件がcsvっぽいデータから2次元配列への格納ってことだったのでそれを踏襲して書いてみた。
まずはテストデータ、test.csv
1, 2, 3, 4, 5, 6, 7 21, 22, 23 31, 32, 33, 34, 35
で、コード本体、test.pl
#!/usr/bin/perl open FILE, "<./test.csv" or die; @result = (); while (<FILE>) { @columns = split /,/, $_; push (result, \@columns); } print $result[0][1] . "\n"; # = 2 print $result[1][2] . "\n"; # = 23 print $result[2][3] . "\n"; # = 34
で、実行結果
$ ./test.pl 32 33 34
はい、見事に失敗です。
どうも参照は配列自体ではなくてポインタで指し示してるような感じっぽい。
んじゃ配列コピーして参照渡したらどうか?と思ってぐぐってみたら角括弧でアレイのコピーになる - 西尾泰和のはてなダイアリーが見つかりました。
id:uskzさんが書いてた方法はこれを使ってるってことなのね。
ということでtest.pl書き換え。
#!/usr/bin/perl open FILE, "<./test.csv" or die; @result = (); while (<FILE>) { @columns = split /,/, $_; push (result, [@columns]); } print $result[0][1] . "\n"; # = 2 print $result[1][2] . "\n"; # = 23 print $result[2][3] . "\n"; # = 34
pushするときに角括弧でくくるようにしただけです。
で、実行結果。
$ ./test.pl 2 23 34
おっとっと、$result[1][2]の要素が改行削ってなかったのでおかしいことになってますが、多次元配列はうまく動いたようです。