c++
rust vs. python
前回の最後に眼をつけておいた事例をやってみた。と、言っても例によってrustの部だけだけどね。これを走らせるには、コンパイラーのrustと、そのパッケージングシステムのcargoが必要。
初回起動時にはネットから178個ものパッケージを自動でDLしてコンパイルするんで、待ちくたびれるはめになる。どうも、次回の記事向けに余分なパッケージもDLしてる節がある。だって、今回の記事には不必要なhttp関連もやつもDLしてるからね。
sakae@deb:/tmp/web_engineer_in_rust/chapter2/rust$ cat .rust-version rustc 1.56.1 (59eed8a2a 2021-11-01)
記事では、ここが1.56.0になってたんで、自システムに合わせておいた。元のままだと、微妙にバージョンが違うrustcが用意されるのだろうか? こういう暗部には関わりたくない。
DLされて事前にコンパイルされた奴はtarget内に格納される。
sakae@deb:/tmp/web_engineer_in_rust/chapter2/rust$ du -sh target/ 461M target/
ちっともエコ・システムになっていないのが気に食わん。まあ、バージョンが固定され、事前コンパイルで、省時間を達成するなら、しょうがないんかな。
sakae@deb:/tmp/web_engineer_in_rust/chapter2/rust$ cargo run --release --bin run_calc_stream_channel Finished release [optimized] target(s) in 0.19s Running `target/release/run_calc_stream_channel` 移動平均の長さ:7 移動平均の最後の要素:1428573.5714285714 計算にかかった時間:75.004044646秒 Vecの使用メモリ量(参考):79.999952MB プロセスの使用メモリ量(参考):82.993152MB 移動平均の長さ:5000 移動平均の最後の要素:1428216.9284 計算にかかった時間:74.76793098秒 Vecの使用メモリ量(参考):79.960008MB プロセスの使用メモリ量(参考):163.606528MB
記事では4種類の環境を試せるようになっているけど、上記はそのうちの一つの実行例。事前にコンパイルをしてあるので、実行に取り掛かるまでの時間は、気にならない。言わば、Makefileが内蔵されてて、makeが走っているんだな。
それにしても、どんな機能が有るんか分からない、tokioなんて名前のパッケージがDLされるのは、少々気味が悪いと思うのは、オイラーだけかな? そんな事を言ってたら、世の中にある言語システムなんて、何も使えなくなるぞ!!
とか言いながら、くだんのやつを使ってるのを探してみた。
use tokio_stream::StreamExt as _; fn get_csv_path(relative_path: &str) -> std::path::PathBuf { let project_path = env!("CARGO_MANIFEST_DIR"); std::path::Path::new(project_path) .parent() .unwrap() .join(relative_path) }
Crate tokiostream 有名なやつなのか。知らないともぐりなのか。 こういうURLから連鎖で、Rust by Example こういう所に飛べるって、勢いが有るな。
Pythonプログラミング入門 (by 東京大学) なんてのもある。ブランドで対抗しますって訳だな。
c++
著者が無料で公開して下さっているので、手元に置いておきたい。
sakae@pen:/tmp$ w3m -dump_head https://ezoeryou.github.io/cpp17book/ HTTP/1.1 200 OK Connection: close Content-Length: 106433 Server: GitHub.com Content-Type: text/html; charset=utf-8 Content-Encoding: gzip :
URLのヘッドを確認すると、gzipで圧縮されている。ならば下記のようにして、お取り寄せと開梱を行う。
sakae@pen:/tmp$ w3m -dump_source https://ezoeryou.github.io/cpp17book/ >z sakae@pen:/tmp$ file z z: gzip compressed data, from Unix, original size modulo 2^32 639660 sakae@pen:/tmp$ gzip -dc z >c17.html sakae@pen:/tmp$ w3m c17.html
これを家宝にしておこう。c++11以降はモダンC++って扱いらしいからね。
devel with c++
入門本に掲載されてたMakefileをBSDMakeでも動くように、ちと改変。
gcc_options = -std=c++17 -Wall --pedantic-errors -g run : program ./a.out program : main.cpp all.h all.h.gch c++ $(gcc_options) -include all.h main.cpp -o a.out all.h.gch : all.h c++ $(gcc_options) -x c++-header -o all.h.gch all.h clean : rm -f ./a.out rm -f ./all.h.gch .PHONY : run clean
こうしておけば、emacsでソースを編集して、直にコンパイルと実行が出来る。変な何とか-modeってやつを入れなくても、動く。もう、rubyとかを編集して実行って感覚で使える。有り難い事です。
read book
少し入門辺を読んでみた。アルゴリズムってのが面白そう。
int main() { std::vector<int> v = {1,2,3,4,5} ; std::for_each( std::begin(v), std::end(v), []( auto value ) { std::cout << value ; } ) ; }
ヴェクターの内容を出力する奴。begin,endは、ヴェクターの範囲を示す。次の行は、無名関数を定義してる。[]がLAMBDAに相当するのかな。次の括弧は、仮引数、最後のブラケット内は、関数本体。
なんだ、昔のschemeなら、とっくに実現してたのが、やっとこさ実現したって事か。でも、最初と最後を指定しないといけないなんて、不恰好だ。c++20 の時代になって、やっとこの不恰好さが取れて、この場合なら、v だけを指定すればよくなるらしい。
(for-each display '("a" "b" "c")) (for-each cons '(1 2 3) '(4 5 6))
引数の順番がCフラフラとは逆になってるけど、やらせる事は一緒。二番目の例なんて、Cフラフラ語では、スイスイと書けるのか? きっとフラフラするだろうね。 何十年も遅れて、やって来た言語、だな。
例は例な奴 (csv read by c++)
新しい言語を習得する時、オイラーはやる事が決っている。そう、CSVで保存してる血圧・脈拍のデータをハンドリングする事だ。今迄、これをお題にpython,haskell,scheme,golang等でやってみたんだ。
んで、難関はCSVを読み込む所。そんなのCSVのモジュールになってるでしょ。探してみても容易に見付からなかった。でも、こんな例が有ったので、肴にしてみる。Cフラフラには、splitも無いんかい。遅れているな。で、その代わりにgetlineなんて言う思いもしない奴を使うようだ。 所変れば、作法も違うのね。splitぐらいは、自作するのが流儀とな。
#include <fstream> #include <string> #include <sstream> #include <vector> using namespace std; vector<string> split(string& input, char delimiter) { istringstream stream(input); string field; vector<string> result; while (getline(stream, field, delimiter)) { result.push_back(field); } return result; } int main() { ifstream ifs("current.csv"); string line; while (getline(ifs, line)) { vector<string> strvec = split(line, ','); for (int i=0; i<strvec.size();i++){ printf("%5d\n", stoi(strvec.at(i))); } } }
早速、上で出て来たmakeのお世話になってみる。
sakae@pen:/tmp/cpp$ make g++ -std=c++17 -Wall --pedantic-errors -g -x c++-header -o all.h.gch all.h g++ -std=c++17 -Wall --pedantic-errors -g -include all.h main.cpp -o a.out main.cpp: In function ‘int main()’: main.cpp:23:24: warning: comparison of integer expressions of different signedne ss: ‘int’ and ‘std::vector<std::__cxx11::basic_string<char> >::size_type’ {aka ‘ long unsigned int’} [-Wsign-compare] 23 | for (int i=0; i<strvec.size();i++){ | ~^~~~~~~~~~~~~~ ./a.out 21120505 142 76 47 21120521 127 :
警告を出しつつ、取り敢えず動いてくれた。でも、気持悪いな。こいつの修整の仕方は、知ってるぞ(勉強したぞ)。
for (size_t i=0; i < strvec.size(); i++){
インデックス番号に負数は有り得ません。何処かのrubyみたいに、負数は配列の尻尾からの番号になりますって言う、気の効いた機能なんて無いんです。
rubyのそれは、配列はリング状になってる。だから、先頭を表すインデックス番号0の前は、ー1。それは、配列の尻尾って考えに及ばないのさ。なんたって、Cフラフラ語は、国際機関が制定するISO規格の言語だから、柔軟性が欠如してるのさ。
intは負数も許す型。それは不味いって事で。正数のみを許す整数にしとけ。そう、その為の型が、 size_t
なのさ。
getline
上の肴を良くみると、getlineが2箇所に出て来る。一つはsplitの中の、getline(stream, field, delimiter)。もう一つは、mainの中のgetline(ifs, line)。違いは何処ですか? って、幼稚園児向けの問題だな。引数が2つの場合と3つの場合が有ります。そういうあんたは、幼稚園児並み。侮ってはいけないんだろうね。
どう解剖する? そりゃ、中を追跡するに限る。となると、debuggerの出番だな。
(gdb) b getline Breakpoint 1 at 0x7ffff7c5ddf0: getline. (7 locations) (gdb) r : 51 [Inferior 1 (process 2592) exited normally]
ちゃんとBPがセットされたはずなんだけど、全くヒットしない。一応どんな所にセットされたか、確認しとく。
(gdb) info break in __getline at getline.c:28 <std::basic_istream<wchar_t, std::char_traits<wchar_t> >::getline(wchar_t*, long, wchar_t)@plt> <std::istream::getline(char*, long, char)@plt> <std::istream::getline(char*, long, char)> <std::basic_istream<wchar_t, std::char_traits<wchar_t> >::getline(wchar_t*, long, wchar_t)> <std::istream::getline(char*, long)> <std::basic_istream<wchar_t, std::char_traits<wchar_t> >::getline(wchar_t*, long)>
非常に長い行のデータが出て来るんで、emacsの矩形選択削除を使った。始点を C-x spaceで選択。終点に移動して、C-x r d する。こうして、前半を削除。後半のWhat部分だけを残した。
debianのgdbって、鉄板な奴だと思っていたけど、どうも阿呆っぽい所があるな。そんじゃ、今迄嫌われていたOpenBSDのlldbで試してみる。
at lldb
64Bitな版なんで、アドレス表示が長いのは勘弁。
ob$ lldb a.out (lldb) target create "a.out" Current executable set to '/tmp/cpp/a.out' (x86_64). (lldb) b getline Breakpoint 1: 2 locations. (lldb) break list Current breakpoints: 1: name = 'getline', locations = 2 1.1: where = a.out`std::__1::basic_istream<char, std::__1::char_traits<char> > & std::__1::getline<char, std::__1::char_traits<char>, std::__1::allocator<char> >(std::__1::basic_istream<char, std::__1::char_traits<char> >&, std::__1::basic _string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 29 at istream:1564:20, address = a.out[0x0000000000007a8d], unresolved, hit count = 0 1.2: where = a.out`std::__1::basic_istream<char, std::__1::char_traits<char> > & std::__1::getline<char, std::__1::char_traits<char>, std::__1::allocator<char> >(std::__1::basic_istream<char, std::__1::char_traits<char> >&, std::__1::basic _string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char) + 32 at istream:1510:23, address = a.out[0x0000000000007530], unresolved, hit cou nt = 0
ごちゃごちゃしてるけど、よく見ると、被試験プログラムのgetlineを対象に選んでくれている。走らせてみる。
(lldb) r Process 13691 launched: '/tmp/cpp/a.out' (x86_64) 4 locations added to breakpoint 1 1 location added to breakpoint 1 Process 13691 stopped * thread #1, stop reason = breakpoint 1.1 frame #0: 0x00000e85631f5a8d a.out`std::__1::basic_istream<char, std::__1::c har_traits<char> >& std::__1::getline<char, std::__1::char_traits<char>, std::__ 1::allocator<char> >(__is=0x00007f7ffffc43e0, __str="") at istream:1564:20 1561 getline(basic_istream<_CharT, _Traits>& __is, 1562 basic_string<_CharT, _Traits, _Allocator>& __str) 1563 { -> 1564 return getline(__is, __str, __is.widen('\n')); ^ 1565 } 1566 1567 #ifndef _LIBCPP_CXX03_LANG (lldb) bt * thread #1, stop reason = breakpoint 1.1 * frame #0: 0x00000e85631f5a8d a.out`std::__1::basic_istream<char, std::__1::c har_traits<char> >& std::__1::getline<char, std::__1::char_traits<char>, std::__ 1::allocator<char> >(__is=0x00007f7ffffc43e0, __str="") at istream:1564:20 frame #1: 0x00000e85631f5186 a.out`main at main.cpp:21:12 frame #2: 0x00000e85631f4d98 a.out`__start + 312 (lldb) up frame #1: 0x00000e85631f5186 a.out`main at main.cpp:21:12 18 int main() { 19 ifstream ifs("current.csv"); 20 string line; -> 21 while (getline(ifs, line)) { ^ 22 vector<string> strvec = split(line, ','); 23 for (int i=0; i<strvec.size();i++){ 24 printf("%5d\n", stoi(strvec.at(i)));
mainの中でヒットしたな。getlineのソースは、istreamの中にある。これって、/usr/include/c++/xx/istreamだな。
(lldb) c Process 13691 resuming Process 13691 stopped * thread #1, stop reason = breakpoint 1.2 frame #0: 0x00000e85631f5530 a.out`std::__1::basic_istream<char, std::__1::c har_traits<char> >& std::__1::getline<char, std::__1::char_traits<char>, std::__ 1::allocator<char> >(__is=0x00007f7ffffc4230, __str="", __dlm=',') at istream:15 10:23 1507 getline(basic_istream<_CharT, _Traits>& __is, 1508 basic_string<_CharT, _Traits, _Allocator>& __str, _CharT __dlm) 1509 { -> 1510 ios_base::iostate __state = ios_base::goodbit; ^ 1511 typename basic_istream<_CharT, _Traits>::sentry __sen(__is, true); 1512 if (__sen) 1513 { (lldb) up frame #1: 0x00000e85631f5025 a.out`split(input="21120505,142,76,47", delimiter=' ,') at main.cpp:12:12 9 istringstream stream(input); 10 string field; 11 vector<string> result; -> 12 while (getline(stream, field, delimiter)) { ^ 13 result.push_back(field); 14 } 15 return result;
何度か継続すると、splitの中のそれが現れた。
後は丁寧に/usr/include/c++ の中にあるヘッダーファイルを見ていけばいいんだな。直接のコードがヘッダーファイル内に記述されてるってのは、Cフラフラの流儀なのかな。そもそもフラフラにフラフラと手を出したのは、wyhashが原因だったけど、あやつもヘッダー内にコードが書かれていたな。
一応32Bit環境でも、確認しておく。FreeBSDな。
[sakae@fb /tmp/cpp]$ lldb a.out (lldb) target create "a.out" Current executable set to '/tmp/cpp/a.out' (i386). (lldb) b getline Breakpoint 1: 2 locations. (lldb) break list Current breakpoints: 1: name = 'getline', locations = 2 1.1: where = a.out`std::__1::basic_istream<char, std::__1::char_traits<char> >& std::__1::getline<char, std::__1::char_traits<char>, std::__1::allocator<char> >(std::__1::basic_istream<char, std::__1::char_traits<char> >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 15 at istream:1564:20, address = a.out[0x00406e9f], unresolved, hit count = 0 1.2: where = a.out`std::__1::basic_istream<char, std::__1::char_traits<char> >& std::__1::getline<char, std::__1::char_traits<char>, std::__1::allocator<char> >(std::__1::basic_istream<char, std::__1::char_traits<char> >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, char) + 17 at istream:1510:23, address = a.out[0x004069f1], unresolved, hit count = 0
相変わらず、目がクラクラ、体がフラフラするのが、この言語の特徴です。メニエール症とかが本物だっtかな。
(lldb) r Process 7065 launching Process 7065 launched: '/tmp/cpp/a.out' (i386) 4 locations added to breakpoint 1 1 location added to breakpoint 1 Process 7065 stopped * thread #1, name = 'a.out', stop reason = breakpoint 1.1 frame #0: 0x00406e9f a.out`std::__1::basic_istream<char, std::__1::char_traits<char> >& std::__1::getline<char, std::__1::char_traits<char>, std::__1::allocator<char> >(__is=0xffbfea6c, __str="") at istream:1564:20 1561 getline(basic_istream<_CharT, _Traits>& __is, 1562 basic_string<_CharT, _Traits, _Allocator>& __str) 1563 { -> 1564 return getline(__is, __str, __is.widen('\n')); 1565 } 1566 1567 #ifndef _LIBCPP_CXX03_LANG (lldb) c Process 7065 resuming Process 7065 stopped * thread #1, name = 'a.out', stop reason = breakpoint 1.2 frame #0: 0x004069f1 a.out`std::__1::basic_istream<char, std::__1::char_traits<char> >& std::__1::getline<char, std::__1::char_traits<char>, std::__1::allocator<char> >(__is=0xffbfea6c, __str="", __dlm='\n') at istream:1510:23 1507 getline(basic_istream<_CharT, _Traits>& __is, 1508 basic_string<_CharT, _Traits, _Allocator>& __str, _CharT __dlm) 1509 { -> 1510 ios_base::iostate __state = ios_base::goodbit; 1511 typename basic_istream<_CharT, _Traits>::sentry __sen(__is, true); 1512 if (__sen) 1513 { (lldb) bt * thread #1, name = 'a.out', stop reason = breakpoint 1.2 * frame #0: 0x004069f1 a.out`std::__1::basic_istream<char, std::__1::char_traits<char> >& std::__1::getline<char, std::__1::char_traits<char>, std::__1::allocator<char> >(__is=0xffbfea6c, __str="", __dlm='\n') at istream:1510:23 frame #1: 0x00406ee4 a.out`std::__1::basic_istream<char, std::__1::char_traits<char> >& std::__1::getline<char, std::__1::char_traits<char>, std::__1::allocator<char> >(__is=0xffbfea6c, __str="") at istream:1564:12 frame #2: 0x004066d6 a.out`main at main.cpp:24:12 frame #3: 0x00406326 a.out`_start1(cleanup=0x2041eb80, argc=1, argv=0xffbfec68) at crt1_c.c:72:7 frame #4: 0x00406480 a.out`_start at crt1_s.S:46
mainが終了したら直にexitするように、仕込まれているのね。
(lldb) up frame #3: 0x00406326 a.out`_start1(cleanup=0x2041eb80, argc=1, argv=0xffbfec68) at crt1_c.c:72:7 69 #endif 70 71 handle_static_init(argc, argv, env); -> 72 exit(main(argc, argv, env)); 73 } 74 75 __asm(".hidden _start1");