Improve exception messages of NativeFileStream
So one gets e.g. "open failed: Permission denied" instead of just "open failed: iostream error".
This commit is contained in:
parent
e5490d7c80
commit
1154ed4d1c
|
@ -198,18 +198,20 @@ StringData convertUtf8ToLatin1(const char *inputBuffer, std::size_t inputBufferS
|
|||
* - Only available under Windows.
|
||||
* - If \a inputBufferSize is -1, \a inputBuffer is considered null-terminated.
|
||||
*/
|
||||
WideStringData convertMultiByteToWide(const char *inputBuffer, int inputBufferSize)
|
||||
WideStringData convertMultiByteToWide(std::error_code &ec, const char *inputBuffer, int inputBufferSize)
|
||||
{
|
||||
// calculate required size
|
||||
WideStringData widePath;
|
||||
widePath.second = MultiByteToWideChar(CP_UTF8, 0, inputBuffer, inputBufferSize, nullptr, 0);
|
||||
if (widePath.second <= 0) {
|
||||
ec = std::error_code(GetLastError(), std::system_category());
|
||||
return widePath;
|
||||
}
|
||||
// do the actual conversion
|
||||
widePath.first = make_unique<wchar_t[]>(static_cast<size_t>(widePath.second));
|
||||
widePath.second = MultiByteToWideChar(CP_UTF8, 0, inputBuffer, inputBufferSize, widePath.first.get(), widePath.second);
|
||||
if (widePath.second <= 0) {
|
||||
ec = std::error_code(GetLastError(), std::system_category());
|
||||
widePath.first.reset();
|
||||
}
|
||||
return widePath;
|
||||
|
@ -219,10 +221,32 @@ WideStringData convertMultiByteToWide(const char *inputBuffer, int inputBufferSi
|
|||
* \brief Converts the specified multi-byte string to a wide string using the WinAPI.
|
||||
* \remarks Only available under Windows.
|
||||
*/
|
||||
WideStringData convertMultiByteToWide(const std::string &inputBuffer)
|
||||
WideStringData convertMultiByteToWide(std::error_code &ec, const std::string &inputBuffer)
|
||||
{
|
||||
return convertMultiByteToWide(
|
||||
inputBuffer.data(), inputBuffer.size() < (numeric_limits<int>::max() - 1) ? static_cast<int>(inputBuffer.size() + 1) : -1);
|
||||
ec, inputBuffer.data(), inputBuffer.size() < (numeric_limits<int>::max() - 1) ? static_cast<int>(inputBuffer.size() + 1) : -1);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Converts the specified multi-byte string to a wide string using the WinAPI.
|
||||
* \remarks
|
||||
* - Only available under Windows.
|
||||
* - If \a inputBufferSize is -1, \a inputBuffer is considered null-terminated.
|
||||
*/
|
||||
WideStringData convertMultiByteToWide(const char *inputBuffer, int inputBufferSize)
|
||||
{
|
||||
std::error_code ec;
|
||||
return convertMultiByteToWide(ec, inputBuffer, inputBufferSize);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Converts the specified multi-byte string to a wide string using the WinAPI.
|
||||
* \remarks Only available under Windows.
|
||||
*/
|
||||
WideStringData convertMultiByteToWide(const std::string &inputBuffer)
|
||||
{
|
||||
std::error_code ec;
|
||||
return convertMultiByteToWide(ec, inputBuffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
#include <vector>
|
||||
|
||||
namespace CppUtilities {
|
||||
|
@ -49,6 +50,8 @@ CPP_UTILITIES_EXPORT StringData convertUtf8ToLatin1(const char *inputBuffer, std
|
|||
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
using WideStringData = std::pair<std::unique_ptr<wchar_t[]>, int>;
|
||||
CPP_UTILITIES_EXPORT WideStringData convertMultiByteToWide(std::error_code &ec, const char *inputBuffer, int inputBufferSize = -1);
|
||||
CPP_UTILITIES_EXPORT WideStringData convertMultiByteToWide(std::error_code &ec, const std::string &inputBuffer);
|
||||
CPP_UTILITIES_EXPORT WideStringData convertMultiByteToWide(const char *inputBuffer, int inputBufferSize = -1);
|
||||
CPP_UTILITIES_EXPORT WideStringData convertMultiByteToWide(const std::string &inputBuffer);
|
||||
#endif
|
||||
|
|
|
@ -2,6 +2,24 @@
|
|||
|
||||
#ifdef CPP_UTILITIES_USE_NATIVE_FILE_BUFFER
|
||||
|
||||
/*!
|
||||
* \class
|
||||
* So one gets e.g. "open failed: Permission denied" instead of
|
||||
just "open failed: iostream error".
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \class NativeFileStream
|
||||
* \brief Provides a standard IO stream instantiated using native APIs.
|
||||
*
|
||||
* Using this class instead of `std::fstream` has the following benefits:
|
||||
* - Under Windows, the specified file path is interpreted as UTF-8 and passed to Windows' unicode API
|
||||
* to support any kind of non-ASCII characters in file paths.
|
||||
* - It is possible to open a file from a native file descriptor. This is for instance useful when dealing with
|
||||
* Android's `content://` URLs.
|
||||
* - Better error messages at least when opening a file, e.g. "Permission denied" instead of just "basic_ios::clear".
|
||||
*/
|
||||
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
#include "../conversion/stringconversion.h"
|
||||
#endif
|
||||
|
@ -159,12 +177,12 @@ NativeFileStream::FileBuffer::FileBuffer(const string &path, ios_base::openmode
|
|||
#ifdef PLATFORM_WINDOWS
|
||||
descriptor = _wopen(widePath.get(), nativeParams.openMode, nativeParams.permissions);
|
||||
if (descriptor == -1) {
|
||||
throw std::ios_base::failure("_wopen failed");
|
||||
throw std::ios_base::failure("_wopen failed", std::error_code(errno, std::system_category()));
|
||||
}
|
||||
#else
|
||||
descriptor = ::open(path.data(), nativeParams.openFlags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
if (descriptor == -1) {
|
||||
throw std::ios_base::failure("open failed");
|
||||
throw std::ios_base::failure("open failed", std::error_code(errno, std::system_category()));
|
||||
}
|
||||
#endif
|
||||
buffer = make_unique<StreamBuffer>(descriptor, openMode);
|
||||
|
@ -172,14 +190,14 @@ NativeFileStream::FileBuffer::FileBuffer(const string &path, ios_base::openmode
|
|||
#ifdef PLATFORM_WINDOWS
|
||||
handle = CreateFileW(widePath.get(), nativeParams.access, nativeParams.shareMode, nullptr, nativeParams.creation, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if (handle == INVALID_HANDLE_VALUE) {
|
||||
throw std::ios_base::failure("CreateFileW failed");
|
||||
throw std::ios_base::failure("CreateFileW failed", std::error_code(GetLastError(), std::system_category()));
|
||||
}
|
||||
buffer = make_unique<StreamBuffer>(handle, boost::iostreams::close_handle);
|
||||
// if we wanted to open assign the descriptor as well: descriptor = _open_osfhandle(reinterpret_cast<intptr_t>(handle), nativeParams.flags);
|
||||
#else
|
||||
descriptor = ::open(path.data(), nativeParams.openFlags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
if (descriptor == -1) {
|
||||
throw std::ios_base::failure("open failed");
|
||||
throw std::ios_base::failure("open failed", std::error_code(errno, std::system_category()));
|
||||
}
|
||||
buffer = make_unique<StreamBuffer>(descriptor, boost::iostreams::close_handle);
|
||||
#endif
|
||||
|
@ -312,9 +330,10 @@ void NativeFileStream::setData(FileBuffer data, std::ios_base::openmode openMode
|
|||
*/
|
||||
std::unique_ptr<wchar_t[]> NativeFileStream::makeWidePath(const std::string &path)
|
||||
{
|
||||
auto widePath = ::CppUtilities::convertMultiByteToWide(path);
|
||||
auto ec = std::error_code();
|
||||
auto widePath = ::CppUtilities::convertMultiByteToWide(ec, path);
|
||||
if (!widePath.first) {
|
||||
throw std::ios_base::failure("Unable to convert path to UTF-16");
|
||||
throw std::ios_base::failure("converting path to UTF-16", ec);
|
||||
}
|
||||
return std::move(widePath.first);
|
||||
}
|
||||
|
|
|
@ -443,12 +443,12 @@ void IoTests::testNativeFileStream()
|
|||
} catch (const std::ios_base::failure &failure) {
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
#ifdef CPP_UTILITIES_USE_BOOST_IOSTREAMS
|
||||
CPPUNIT_ASSERT_EQUAL("CreateFileW failed: iostream error"s, string(failure.what()));
|
||||
TESTUTILS_ASSERT_LIKE("expected error with some message", "CreateFileW failed: .+", failure.what());
|
||||
#else
|
||||
CPPUNIT_ASSERT_EQUAL("_wopen failed: iostream error"s, string(failure.what()));
|
||||
TESTUTILS_ASSERT_LIKE("expected error with some message", "_wopen failed: .+", failure.what());
|
||||
#endif
|
||||
#else
|
||||
CPPUNIT_ASSERT_EQUAL("open failed: iostream error"s, string(failure.what()));
|
||||
TESTUTILS_ASSERT_LIKE("expected error with some message", "open failed: .+", failure.what());
|
||||
#endif
|
||||
}
|
||||
fileStream.clear();
|
||||
|
|
Loading…
Reference in New Issue