1 #ifndef AL_FSTREAM_H 2 #define AL_FSTREAM_H 3 4 #ifdef _WIN32 5 6 #define WIN32_LEAN_AND_MEAN 7 #include <windows.h> 8 9 #include <array> 10 #include <string> 11 #include <fstream> 12 13 14 namespace al { 15 16 // Windows' std::ifstream fails with non-ANSI paths since the standard only 17 // specifies names using const char* (or std::string). MSVC has a non-standard 18 // extension using const wchar_t* (or std::wstring?) to handle Unicode paths, 19 // but not all Windows compilers support it. So we have to make our own istream 20 // that accepts UTF-8 paths and forwards to Unicode-aware I/O functions. 21 class filebuf final : public std::streambuf { 22 std::array<char_type,4096> mBuffer; 23 HANDLE mFile{INVALID_HANDLE_VALUE}; 24 25 int_type underflow() override; 26 pos_type seekoff(off_type offset, std::ios_base::seekdir whence, std::ios_base::openmode mode) override; 27 pos_type seekpos(pos_type pos, std::ios_base::openmode mode) override; 28 29 public: 30 filebuf() = default; 31 ~filebuf() override; 32 33 bool open(const wchar_t *filename, std::ios_base::openmode mode); 34 bool open(const char *filename, std::ios_base::openmode mode); 35 is_open()36 bool is_open() const noexcept { return mFile != INVALID_HANDLE_VALUE; } 37 38 void close(); 39 }; 40 41 // Inherit from std::istream to use our custom streambuf 42 class ifstream final : public std::istream { 43 filebuf mStreamBuf; 44 45 public: 46 ifstream(const wchar_t *filename, std::ios_base::openmode mode = std::ios_base::in); 47 ifstream(const std::wstring &filename, std::ios_base::openmode mode = std::ios_base::in) 48 : ifstream(filename.c_str(), mode) { } 49 ifstream(const char *filename, std::ios_base::openmode mode = std::ios_base::in); 50 ifstream(const std::string &filename, std::ios_base::openmode mode = std::ios_base::in) 51 : ifstream(filename.c_str(), mode) { } 52 ~ifstream() override; 53 is_open()54 bool is_open() const noexcept { return mStreamBuf.is_open(); } 55 close()56 void close() { mStreamBuf.close(); } 57 }; 58 59 } // namespace al 60 61 #else /* _WIN32 */ 62 63 #include <fstream> 64 65 namespace al { 66 67 using filebuf = std::filebuf; 68 using ifstream = std::ifstream; 69 70 } // namespace al 71 72 #endif /* _WIN32 */ 73 74 #endif /* AL_FSTREAM_H */ 75