Created
October 10, 2016 18:11
-
-
Save Alexhuszagh/3cd3aefac3e4a43724723edf2a01bc53 to your computer and use it in GitHub Desktop.
Filestream with support for Windows wide API file names.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** \brief FStream-like handle for Wide API support. | |
* | |
* Although the C standard makes no guarantees a C FILE descriptor will | |
* be used internally, both GCC and Clang do so, with GCC providing | |
* an extension to create a basic_filebuf from an existing C FILE or | |
* POSIX file descriptor[1]. As basic_fstream is merely a wrapper around | |
* basic_iostream with a basic_filebuf, this provides a full API | |
* that should have native performance. | |
* | |
* By using a GNU extension to solve a GNU-specific problem, or std::fstream | |
* elsewhere, this code should work with MSVC and MinGW on Windows, and | |
* with any compiler elsewhere. | |
* | |
* 1. http://cs.brown.edu/~jwicks/libstdc++/html_user/stdio__filebuf_8h-source.html | |
* 2. https://github.com/llvm-mirror/libcxx/blob/master/include/fstream#L132 | |
* | |
* \license MIT or BDS-like, same as the LLVM source code from which | |
* it is derived. | |
*/ | |
#include <fstream> | |
#include <sstream> | |
#include <string> | |
#include <iostream> | |
#ifdef __MINGW32__ | |
# include <ext/stdio_filebuf.h> | |
#endif | |
#ifdef _WIN32 | |
# include <windows.h> | |
#endif | |
#ifdef __MINGW32__ | |
namespace detail | |
{ | |
/** \brief Convert C++ mode to C mode. | |
*/ | |
const char *cMode(const std::ios_base::openmode mode) | |
{ | |
using namespace std; | |
switch (mode & ~ios_base::ate) { | |
case ios_base::out: | |
case ios_base::out | ios_base::trunc: | |
return "w"; | |
case ios_base::out | ios_base::app: | |
case ios_base::app: | |
return "a"; | |
case ios_base::in: | |
return "r"; | |
case ios_base::in | ios_base::out: | |
return "r+"; | |
case ios_base::in | ios_base::out | ios_base::trunc: | |
return "w+"; | |
case ios_base::in | ios_base::out | ios_base::app: | |
case ios_base::in | ios_base::app: | |
return "a+"; | |
case ios_base::out | ios_base::binary: | |
case ios_base::out | ios_base::trunc | ios_base::binary: | |
return "wb"; | |
case ios_base::out | ios_base::app | ios_base::binary: | |
case ios_base::app | ios_base::binary: | |
return "ab"; | |
case ios_base::in | ios_base::binary: | |
return "rb"; | |
case ios_base::in | ios_base::out | ios_base::binary: | |
return "r+b"; | |
case ios_base::in | ios_base::out | ios_base::trunc | ios_base::binary: | |
return "w+b"; | |
case ios_base::in | ios_base::out | ios_base::app | ios_base::binary: | |
case ios_base::in | ios_base::app | ios_base::binary: | |
return "a+b"; | |
default: | |
return nullptr; | |
} | |
} | |
/** \brief Convert narrow, UTF-8 code-page to wide UTF-16. | |
*/ | |
inline wchar_t * toWide(const char *narrow) | |
{ | |
int size = MultiByteToWideChar(CP_UTF8, 0, narrow, -1, nullptr, 0 ); | |
wchar_t *wide = new wchar_t[size]; | |
MultiByteToWideChar(CP_UTF8, 0, narrow, -1, wide, size); | |
return wide; | |
} | |
/** \brief Get narrow, C FILE pointer. | |
*/ | |
FILE *getFile(const std::string &narrow, | |
const std::ios_base::openmode mode) | |
{ | |
const char *modeString = cMode(mode); | |
if (modeString != nullptr) { | |
return fopen(narrow.data(), modeString); | |
} | |
return nullptr; | |
} | |
/** \brief Get wide, C FILE pointer. | |
*/ | |
FILE *getFile(const std::wstring &wide, | |
const std::ios_base::openmode mode) | |
{ | |
const char *cModeString = cMode(mode); | |
if (cModeString != nullptr) { | |
wchar_t *modeString = toWide(cModeString); | |
FILE *file = _wfopen(wide.data(), modeString); | |
delete[] modeString; | |
return file; | |
} | |
} | |
} /* detail */ | |
/** \brief Stream supporting narrow and wide APIs on Windows. | |
* | |
* Only used if MinGW (which does not overload wchar_t for std::fstream) | |
* and Windows is used. | |
* | |
* \note ifstream and ofstream are not shown, for brevity, but are | |
* merely replacing the base class with istream and ostream, respectively, | |
* and the default openmode for the constructor. | |
*/ | |
class FStream: public std::iostream | |
{ | |
FILE *file = nullptr; | |
__gnu_cxx::stdio_filebuf<char> buffer; | |
typedef char char_type; | |
typedef std::char_traits<char> traits_type; | |
typedef typename traits_type::int_type int_type; | |
typedef typename traits_type::pos_type pos_type; | |
typedef typename traits_type::off_type off_type; | |
public: | |
FStream(); | |
FStream(const std::string &narrow, | |
const std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out); | |
FStream(const std::wstring &wide, | |
const std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out); | |
virtual ~FStream(); | |
__gnu_cxx::stdio_filebuf<char>* rdbuf() const; | |
bool is_open() const; | |
void close(); | |
}; | |
/** \brief Null constructor. | |
*/ | |
FStream::FStream() | |
{} | |
/** \brief Narrow constructor. | |
*/ | |
FStream::FStream(const std::string &narrow, | |
const std::ios_base::openmode mode): | |
file(detail::getFile(narrow, mode)), | |
buffer(file, mode), | |
std::iostream(&buffer) | |
{} | |
/** \brief Wide constructor. | |
*/ | |
FStream::FStream(const std::wstring &wide, | |
const std::ios_base::openmode mode): | |
file(detail::getFile(wide, mode)), | |
buffer(file, mode), | |
std::iostream(&buffer) | |
{} | |
/** \Brief Virtual dtor. | |
*/ | |
FStream::~FStream() | |
{ | |
close(); | |
} | |
/** \brief Access underlying buffer. | |
*/ | |
__gnu_cxx::stdio_filebuf<char> * FStream::rdbuf() const | |
{ | |
return const_cast<__gnu_cxx::stdio_filebuf<char>*>(&buffer); | |
} | |
/** \brief Check if stream is open. | |
*/ | |
bool FStream::is_open() const | |
{ | |
return buffer.is_open(); | |
} | |
/** \Brief Ensure file handle and buffer closed. | |
*/ | |
void FStream::close() | |
{ | |
if (file != nullptr) { | |
buffer.close(); | |
fclose(file); | |
} | |
file = nullptr; | |
} | |
#else | |
typedef std::fstream FStream; | |
#endif | |
int main(int argc, char *argv[]) | |
{ | |
// Hangeul, UTF-16LE or UTF-8, "한국어" | |
#ifdef _WIN32 | |
std::wstring name = {0xd55c, 0xad6d, 0xc5b4}; | |
std::wcout << name << std::endl; | |
#else | |
std::string name = {-19, -107, -100, -22, -75, -83, -20, -106, -76}; | |
std::cout << name << std::endl; | |
#endif | |
// write bytes | |
FStream output(name, std::ios_base::out); | |
output << "This\n\nis a\n\n\n\nsample\n\nfile.\n"; | |
output.close(); | |
// read bytes | |
std::string line; | |
FStream input(name, std::ios_base::in); | |
while (std::getline(input, line)) { | |
std::cout << line << std::endl; | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment