boost.勉強会 #21...
TRANSCRIPT
C++1zにstring_viewが 導入されてうれしいので
紹介します
H.Hiro (@h_hiro_)
お久しぶりです H.Hiroです
•名古屋で研究の仕事しています •C++はかなり使います (新しいアルゴリズムを考えて試す) •Boostは最近ちょっとご無沙汰… •最近、開発したいプログラムのネタが 思い浮かぶも時間が取れず停滞中
今回のテーマ
部分文字列
みんな 使いますよね
std::string foo = "Boost C++ Library"; std::string bar = foo.substr(6, 3); std::cout << bar << std::endl; // prints "C++"
ただ、これ C++を使っている人だと
「使いたくない」 ってときもありませんか
ただ、これ C++を実行効率重視の ために使っている人だと
「使いたくない」 ってときもありませんか
std::string foo = "Boost C++ Library"; std::string bar = foo.substr(6, 3);
ヒープ領域 (newとかで確保されたメモリ領域に利用する)
std::string foo = "Boost C++ Library"; std::string bar = foo.substr(6, 3);
ヒープ領域 (newとかで確保されたメモリ領域に利用する)
B o o s t C + + L i b r a r y
std::string foo = "Boost C++ Library"; std::string bar = foo.substr(6, 3);
ヒープ領域 (newとかで確保されたメモリ領域に利用する)
B o o s t C + + L i b r a r y
C + +
std::string foo = "Boost C++ Library"; std::string bar = foo.substr(6, 3); std::cout << bar << std::endl; // prints "C++"
substrする 文字数ぶんの
メモリを 確保する必要
そこで、 考えられる方法
そもそも、文字列って 連続したメモリ領域に 確保されてるんだから
B o o s t C + + L i b r a r y
B o o s t C + + L i b r a r y
std::string foo = "Boost C++ Library"; std::pair<const char*, std::size_t> bar = std::make_pair( , );
別にこれで いいじゃないか
長ささえわかればよい 始点のポインタと
&foo[6] 3
これが 今回紹介する string_viewの 大枠です
std::string foo = "Boost C++ Library"; std::pair<const char*, std::size_t> bar = std::make_pair( , );
string_viewとは 単に「 と を組にして保持する クラス」です
&foo[6] 3
始点のポインタ 長さ
ちなみに
実装の説明は これでほとんど 終わりです
が、もちろん、 これで
終わるわけは ありません
「実際の使い方」 「どんな場面で使われているのか」 「私が何でこれを時間をかけて紹介 しようと思ったのか」 なども紹介します
使ってみる
string_viewの実装状況 gccの場合:gcc7で対応予定 https://gcc.gnu.org/onlinedocs/libstdc++/manual/status.html
clangの場合:? Visual Studioの場合:?
標準で入ること 前提でコードを
書くのは もう少し待つとして
Boost.string_view を使います
•もともと、Boostでは 「boost::string_ref」という クラス名でした •Boost 1.61.0以降 「boost::string_view」が追加され 若干機能が増えています ※C++標準として当初提案されたもの(N3442) のみならず、より新しいもの(N4480)を 反映させているようです
boost.string_viewにあって boost.string_refにないもの traits対応 move対応(?) noexcept対応
使う
ヘッダファイル だけで利用できる
Boost ライブラリなので
Boostのサイトで ダウンロードして
そのパスを読み込む ようにすればいいだけ
$ g++ sample.cpp -I ダウンロードしたパス
コード例
#include <boost/utility/string_view.hpp> // "utility"必要です!
// さっきのコードを // boost.string_viewで // 書き換えてみる std::string foo = "Boost C++ Library"; boost::string_view bar(&foo[6], 3); std::cout << bar << std::endl;
// 元の文字列が変わると、 // string_view側も変わる // (あくまで、元の文字列の // 一部を参照しているだけ) (前ページに続いて) foo[7] = 'P'; foo[8] = 'P'; std::cout << bar << std::endl;
// string_viewのコンストラクタ // 引数1つ boost::string_view sv1("foo"); // const char* std::string b("bar"); boost::string_view sv2(b); // std::string boost::string_view sv3(sv2); // string_view // 引数2つ // 「const char* ポインタ, 長さ」。 // 「std::string, 長さ」などはできない。 boost::string_view sv4(sv1.data(), 3); boost::string_view sv4(b.data(), 3);
// string_viewのAPI // std::stringと似たものが揃っている // ※内容を変更するAPIはない boost::string_view sv1("boost"); std::cout << sv1[2] << std::endl; // 'o' boost::string_view sv6 = sv1.substr(1, 3); std::cout << sv6 << std::endl; // "oos" std::cout << sv1.find("oo") << std::endl; // 見つけられる。この場合1になる
APIがstd::stringと 一貫性を持たせて
あるので
std::stringを 使い慣れていれば そんなに手間は 感じないだろうし
今まで引数をstd::stringに していたものを、string_viewに 置き換えるのも楽 char foo(const std::string & bar) { return bar[0]; } ↓ char foo(boost::string_view bar) { return bar[0]; } ※string_viewはstd::stringから暗黙に変換可
「本当はstd::stringじゃないんだけど std::stringみたいな性質を持ってる から、似た扱いができるようにしよう」 と設計されてるのがポイントです (デザインパターンでは 「Proxy(代理)パターン」といいます)
boost::string_viewで利用できるメソッド <std::stringと共通> size length max_size empty begin end (c, r付き含む) front back at data clear operator[] compare (各種比較演算子を含む) find (rfindなども含む) substr <独自のもの> remove_prefix remove_suffix
string_viewは どんな場面で
使われているのか
最初に 言いたかったこと
私も、ほぼ同じ 機能のライブラリが 欲しくて、作った ことがあった
fundoshi.hpp (2011年初版公開) https://github.com/maraigue/fundoshi.hpp
fundoshi.hpp (2011年初版公開) https://github.com/maraigue/fundoshi.hpp
参照:
2012.11.17 CLR/H&札幌C++勉強会 発表資料
http://www.slideshare.net/maraigue/20121117-clrhc-fundoshihpp
このときは、 文字列検索アルゴリズムのために
利用していた
かなり後になって 同じようなことを している人が
多数いると知った
汎用的なライブラリ •StringPiece (Google)
ライブラリの一機能として提供 •Qtの "QStringRef" •Swiftの "CFStringRef" •JUCEの "StringRef"
内部処理のために?利用 •LLVMの "StringRef" •gRPCの "string_ref"
だから、 Boostにも入ったし のちに標準化も されたといえる
実際、 どんな場面で
使われているのか
利用例: Qtの "QStringRef" がある箇所 •QRegularExpressionMatch (正規表現のマッチング結果) •XMLパーサーの解析結果
QString foo("boost C++ library"); QRegularExpression pat("C.."); QRegularExpressionMatch mat = pat.match(foo); mat.capturedRef(); // ↑マッチした部分をQStringRefで返す
Boost.String_Refのドキュメントに 書かれていた、利用が見込まれる ケース •HTTPレスポンスから、必要な 部分だけを返す
さっきの「XMLパーサーの解析結果」 と似た用法ですね
利用例: LLVMの "StringRef" がある箇所 •(おそらく) メソッド名等を保持する部分 (完全に読めているわけではなくて、 コード中にStringRefが見つかった場所 近辺を読んだだけですが、そんな雰囲気 だった)
string_refを標準化しようという 提案文書(N3442)にて 利用を想定していたケース
(1/3)
引数の型はstd::stringであるが、 「std::string以外の値を引数として 渡す場合」は呼び出す側でコピーを 取っておく必要がある場合
string_refを標準化しようという 提案文書(N3442)にて 利用を想定していたケース
(2/3)
char*ポインタと文字列長を 組にした引数を受け取っているような 箇所の置き換え •パフォーマンスを理由にそうする ことを想定? •内部的にC言語のAPIを呼ぶ場合 とか?
string_refを標準化しようという 提案文書(N3442)にて 利用を想定していたケース
(3/3)
•連続したメモリ上に確保されている ものであれば何でも扱える、という 型が必要な場合
std::stringもstd::vector<char>も 統一的に扱いたいとか?
部分文字列を 取る操作が あるならば、
これが使えないか 考えてみよう
といえるくらい 汎用的な
ライブラリです
おわりに
部分文字列を 取るときに
付きまとう問題 「余計なメモリ確保」
B o o s t C + + L i b r a r y
C + +
それをスマートに 解決してくれる string_view
祝・ 標準化が有力に!
参考資料
C++標準化提案 • N3442(string_refの提案)
http://www.open-std.org/jtc1/sc22/wg21/docs/ papers/2012/n3442.html
• N4606(C++1zのドラフト) http://www.open-std.org/JTC1/SC22/WG21/docs/ papers/2016/n4606.pdf
実装(本資料中で示したもの関連) • Boost http://www.boost.org/
• Qt https://www.qt.io/
• LLVM http://www.llvm.org/
ありがとう ございました