#pragma once
//--------------------------------------------------- Dependencies:
#include "throwx.h" // throwX, hopefully
#include "NullPointer.h" // nullPointer
#include "is_explicitly_convertible.h" // IsExplicitlyConvertibleTo template.
#include "wrapper/windows_h.h"
#include <array> // std::array
#include <algorithm> // std::swap
#include <fstream> // std::ifstream, std::ofstream
#include <stdlib.h> // wcstombs
#include <string> // std::string, std::wstring
#include <type_traits> // std::conditional
#include <iostream>
//--------------------------------------------------- Interface:
namespace windows {
using std::array;
using std::ifstream;
using std::exception;
using std::ofstream;
using std::string;
using std::wstring;
inline wstring shortPathFor( wstring const& filename )
{
array< wchar_t, MAX_PATH > buffer;
DWORD const nChars =
GetShortPathName( filename.c_str(), buffer.data(), buffer.size() );
hopefully( 0 < nChars && nChars < buffer.size() )
|| throwX( "windows::shortPathFor failed because GetShortPathName failed" );
return buffer.data();
}
inline bool getPerfectAnsiFor( wstring const& ws, string& perfectAnsi )
{
using std::swap;
std::string result( ws.length() + 1, '@' );
size_t const nChars = wcstombs( &result[0], ws.c_str(), result.size() );
if( nChars == size_t( -1 ) )
{
return false;
}
result.resize( nChars );
swap( result, perfectAnsi );
return true;
}
inline string ansiInfileName(
wstring const& filename,
bool tryPerfectConversionFirst = true
)
{
string result;
if( tryPerfectConversionFirst )
{
if( getPerfectAnsiFor( filename, result ) )
{
return result;
}
}
if( getPerfectAnsiFor( shortPathFor( filename ), result ) )
{
return result;
}
throwX( "windows::ansiInfileName failed" );
}
inline string ansiOutfileName( wstring const& filename )
{
try
{
return ansiInfileName( filename );
}
catch( exception const& )
{
HANDLE const f = CreateFile(
filename.c_str(), 0, 0, nullPointer, CREATE_NEW, 0, 0
);
hopefully( f != INVALID_HANDLE_VALUE )
|| "ansiOutfileName failed because CreateFile failed";
CloseHandle( f );
return ansiInfileName( filename, false ); // Have tried perfect conversion.
}
}
namespace detail {
class InFStreamAdapter
: public ifstream
{
public:
InFStreamAdapter() {}
explicit
InFStreamAdapter( char const* filename, ios_base::openmode mode )
: ifstream( filename, mode )
{}
explicit
InFStreamAdapter( wchar_t const* filename, ios_base::openmode mode )
: ifstream( ansiInfileName( filename ).c_str(), mode )
{}
};
class OutFStreamAdapter
: public ofstream
{
public:
OutFStreamAdapter() {}
explicit
OutFStreamAdapter( char const* filename, ios_base::openmode mode )
: ofstream( filename, mode )
{}
explicit
OutFStreamAdapter( wchar_t const* filename, ios_base::openmode mode )
: ofstream( ansiOutfileName( filename ).c_str(), mode )
{}
};
bool const streamsSupportWideNames =
IsExplicitlyConvertibleTo< ofstream, wchar_t const* >::yes;
typedef std::conditional<
streamsSupportWideNames, ifstream, InFStreamAdapter
>::type
InFStreamBase;
typedef std::conditional<
streamsSupportWideNames, ofstream, OutFStreamAdapter
>::type
OutFStreamBase;
} // namespace detail
class InFStream
: public detail::InFStreamBase
{
private:
typedef detail::InFStreamBase Base;
public:
InFStream() {}
explicit
InFStream( char const* filename, ios_base::openmode mode = ios_base::in )
: Base( filename, mode )
{}
explicit
InFStream( string const& filename, ios_base::openmode mode = ios_base::in )
: Base( filename.c_str(), mode )
{}
explicit
InFStream( wchar_t const* filename, ios_base::openmode mode = ios_base::in )
: Base( filename, mode )
{}
explicit
InFStream( wstring const& filename, ios_base::openmode mode = ios_base::in )
: Base( filename.c_str(), mode )
{}
};
class OutFStream
: public detail::OutFStreamBase
{
private:
typedef detail::OutFStreamBase Base;
public:
OutFStream() {}
explicit
OutFStream( char const* filename, ios_base::openmode mode = ios_base::out )
: Base( filename, mode )
{}
explicit
OutFStream( string const& filename, ios_base::openmode mode = ios_base::out )
: Base( filename.c_str(), mode )
{}
explicit
OutFStream( wchar_t const* filename, ios_base::openmode mode = ios_base::out )
: Base( filename, mode )
{}
explicit
OutFStream( wstring const& filename, ios_base::openmode mode = ios_base::out )
: Base( filename.c_str(), mode )
{}
};
} // namespace windows