WideStudio Programming (2-3)~fstreamもどきのクラス

ワイドキャラクタのファイル名に対応したfstreamもどきのクラス、名付けてalfstream。接頭辞の“al”はAttoCraft Libraryの積もりです。

このクラスのミソは、libc++の__gnu_cxx::stdio_filebufクラス(ext/stdio_filebuf.h)です。

_wopen(低レベル入出力の方)でファイルをオープンして、それのfdを使ってstdioのバッファを生成し、それとstreambufクラスを組み合わせることで、ファイルストリームとしての入出力が出来るようにしています。

実際にタンゴレンでどうやっているか、簡単に仕掛けを説明すると、まず、

#include <ext/stdio_filebuf.h>
template
class alstdiofilebuf : public alstdiofile {
    __gnu_cxx::stdio_filebuf<_CharT,_Traits>* _buf;
public:
    alstdiofilebuf() : _buf() {}
    void connect_to_buffer() {
        _buf = new __gnu_cxx::stdio_filebuf<_CharT>(fd(),mode());
    }
    template
    void open(_filenameCharT* __filename, std::ios_base::openmode __mode,
    bool __make_read_only) {
        open_file(__filename,__mode,__make_read_only);
        connect_to_buffer();
    }
    void close() { ... }
    __gnu_cxx::stdio_filebuf<_CharT>* rdbuf() const { return _buf; }
};

みたいなテンプレートクラスを作ります。

open_file()は基底クラスalstdiofileのメンバで、char*かwchar_t*かどっちかの__filenameを受け取ってオープンして、_fd を保持するようになっています。connect_to_buffer()で使われているfd()とmode()はその辺の保持値を返してきます。(__make_read_only引数はファイルが無い時(生成時)にのみ使われるもので、大抵は指定しなくて良いですから、気にしないでいいです。)

connect_to_buffer() の中ではバッファを生成すると同時にクラスのコンストラクタを使ってファイルディスクリプタと連結させています。

で、このalstdiofilebufクラスを基底クラスに持つ、下記のようなテンプレートクラスを宣言します。(実際にはエラーチェックして例外を発生するようになっていたり、コンストラクタでもファイル名を指定出来るようになっていますが、煩雑なので割愛しています)

template
class basic_alfstream
    : public alstdiofilebuf<_CharT,_Traits,BASE_OPENMODE>,
    public _baseclassT
{
    .....
    template
    void open(_filenameCharT* __path,
        std::ios_base::openmode __openmode=static_cast<std::ios_base::openmode>(0),
        bool __make_read_only=false)
    {
        alstdiofilebuf<_CharT,_Traits,BASE_OPENMODE>::open(__path,__openmode,__make_read_only);
        _baseclassT::rdbuf(alstdiofilebuf<_CharT,_Traits,BASE_OPENMODE>::rdbuf());
        _baseclassT::imbue(std::locale());
    }
};

openの中の、

_baseclassT::rdbuf(alstdiofilebuf<_CharT,_Traits,BASE_OPENMODE>::rdbuf());

がキモです。_baseclassT::rdbuf()がalstdiofilebuf::rdbuff()が返してくるバッファとストリームを関連付けています。ちなみに_baseclassTはiostream/istream/ostreamのどれかを想定しています。

これを使って下記のようにtypedefすれば、ファイル名がchar*で与えられてもwchar_t*で与えられてもopen出来るfstreamもどきの出来上がりです。

typedef basic_alfstream<std::iostream,char,std::char_traits,
                    static_cast<std::ios_base::openmode>(0)> alfstream;
typedef basic_alfstream<std::istream,char,std::char_traits,
                    std::ios_base::in> alifstream;
typedef basic_alfstream<std::ostream,char,std::char_traits,
                    std::ios_base::out> alofstream;

こんな風に使えます。

    alifstream ifs;
    ifs.open(L"unicode漢字ファイル名");
    while( !ifs.eof() ) {
        ifs >> .....

fstreamを派生させている訳ではないのがある意味面白いところかも知れません。

より詳しいことが知りたい方は、 http://www.attocraft.jp/contents/archive/alfstream.zip に関連のソースを置いておきますので、よろしければご覧下さい。