本の虫

著者:江添亮
ブログ: http://cpplover.blogspot.jp/
メール: boostcpp@gmail.com
Twitter: https://twitter.com/EzoeRyou
GitHub: https://github.com/EzoeRyou

アマゾンの江添のほしい物リストを著者に送るとブログ記事のネタになる

筆者にブログのネタになる品物を直接送りたい場合、住所をメールで質問してください。

C++のfilesystemライブラリが膨大すぎる

C++17の参考書はほぼ書き終えて、あとはfilesystemライブラリの解説を残すだけになっている。

EzoeRyou/cpp17book: textbook for C++17

ファイルシステムライブラリは、ext4とかbtrfsのようないわゆるファイルシステムに対するライブラリではなく、ディレクトリとファイルに対する操作を提供するライブラリだ。具体的にはファイルパスの文字列処理や、ファイルのコピーやリネーム、ディレクトリ構造のトラバースといった雑多なファイルシステム操作を提供する。

C++のファイルシステムライブラリは、原則としてPOSIX互換になっている。POSIXの規定するライブラリをモダンなC++風のライブラリとして設計したものだ。例えば、カレントディレクトリにあるファイルをすべて表示するプログラムは以下のように書ける。

#include <filesystem>
#include <iostream>
#include <iterator>
#include <algorithm>

int main()
{
    using namespace std::filesystem ;

    directory_iterator iter("."), end ;
    std::copy( iter, end, std::ostream_iterator<path>(std::cout, "\n") ) ;
}

C++らしく、ディレクトリー構造をイテレーターでたどることができる。イテレーターなのでアルゴリズムに突っ込むこともできる。

参考書としてファイルシステムライブラリを解説するにあたって、量が膨大すぎるという問題がある。POSIXのファイルシステム操作をカバーするライブラリなのだから膨大になるのは仕方がないが、まともに解説したのでは100ページを確実に超える解説が必要になる。

しかも、絶対パスを得るabsolute(path)とかcopyとかcreate_directoryとかcreate_symlinkとか、自明で雑多な関数が多すぎる。

これらの関数をいちいち解説しても、リファレンスにしかならない。リファレンスならばC++コンパイラーに付属のドキュメントを読めばいいはずだ。

そこで、今書いている参考書では、ファイルシステムライブラリのすべてを解説するのではなく、概要を解説しようと思う。

しかし、概要と言ってもやはり量が膨大で、エラー処理もあるし、なによりクラスpathの初期化だけで、C++が規定している文字列エンコードの話から始めなければならない始末だ。

path::value_typeがどのような文字型をもつのかは未規定で実装に依存する。pathはできるだけポータブルなコードを書けるように、文字コードの変換をサポートしている。


int main()
{
    using namespace std::filesystem ;

    // ネイティブナローエンコード
    path p1( "/dev/null" ) ;
    // ネイティブワイドエンコード
    path p2( L"/dev/null" ) ;
    // UTF-16エンコード
    path p3( u"/dev/null" ) ;
    // UTF-32エンコード
    path p4( U"/dev/null" ) ;
}

C++では、char型がネイティブナローエンコードとUTF-8エンコードの両方の文字型を兼ねているので、char型はネイティブナローエンコードであると解釈される。ネイティブナローエンコードがたまたまUTF-8ならばUTF-8を渡してもいいが、そうではない場合、おそらく動かない。


int main()
{
    using namespace std::filesystem ;

    // ネイティブナローエンコードとして解釈される
    path p(u8"ファイル名") ;
}

このコードは、ネイティブナローエンコードがUTF-8ではない場合、動く保証がない移植性の低いコードだ。

ファイルシステムライブラリは、移植性が高いUTF-8文字列でファイルパスを扱う方法としてu8path(Source)が用意されている。


int main()
{
    using namespace std::filesystem ;

    path p = u8path(u8"ファイル名") ;
}

こういった細かい落とし穴を解説していくだけでページ数がかさむ。早く完成させたい。