1 // 2 // Copyright (c) 2012 Artyom Beilis (Tonkikh) 3 // 4 // Distributed under the Boost Software License, Version 1.0. (See 5 // accompanying file LICENSE or copy at 6 // http://www.boost.org/LICENSE_1_0.txt) 7 // 8 #ifndef BOOST_NOWIDE_FSTREAM_HPP_INCLUDED 9 #define BOOST_NOWIDE_FSTREAM_HPP_INCLUDED 10 11 #include <boost/nowide/config.hpp> 12 #include <boost/nowide/filebuf.hpp> 13 #include <istream> 14 #include <ostream> 15 16 namespace boost { 17 namespace nowide { 18 /// \cond INTERNAL 19 namespace detail { 20 // clang-format off 21 struct StreamTypeIn 22 { modeboost::nowide::detail::StreamTypeIn23 static std::ios_base::openmode mode() { return std::ios_base::in; } mode_modifierboost::nowide::detail::StreamTypeIn24 static std::ios_base::openmode mode_modifier() { return mode(); } 25 template<typename CharType, typename Traits> 26 struct stream_base{ 27 typedef std::basic_istream<CharType, Traits> type; 28 }; 29 }; 30 struct StreamTypeOut 31 { modeboost::nowide::detail::StreamTypeOut32 static std::ios_base::openmode mode() { return std::ios_base::out; } mode_modifierboost::nowide::detail::StreamTypeOut33 static std::ios_base::openmode mode_modifier() { return mode(); } 34 template<typename CharType, typename Traits> 35 struct stream_base{ 36 typedef std::basic_ostream<CharType, Traits> type; 37 }; 38 }; 39 struct StreamTypeInOut 40 { modeboost::nowide::detail::StreamTypeInOut41 static std::ios_base::openmode mode() { return std::ios_base::in | std::ios_base::out; } mode_modifierboost::nowide::detail::StreamTypeInOut42 static std::ios_base::openmode mode_modifier() { return std::ios_base::openmode(); } 43 template<typename CharType, typename Traits> 44 struct stream_base{ 45 typedef std::basic_iostream<CharType, Traits> type; 46 }; 47 }; 48 // clang-format on 49 50 /// Base class for all basic_*fstream classes 51 /// Contains basic_filebuf instance so its pointer can be used to construct basic_*stream 52 /// Provides common functions to reduce boilerplate code including inheriting from 53 /// the correct std::basic_[io]stream class and initializing it 54 /// \tparam T_StreamType One of StreamType* above. 55 /// Class used instead of value, because openmode::operator| may not be constexpr 56 /// \tparam FileBufType Discriminator to force a differing ABI if depending on the contained filebuf 57 template<typename CharType, 58 typename Traits, 59 typename T_StreamType, 60 int FileBufType = BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT> 61 class fstream_impl; 62 63 template<typename Path, typename Result> 64 struct enable_if_path; 65 } // namespace detail 66 /// \endcond 67 68 /// 69 /// \brief Same as std::basic_ifstream<char> but accepts UTF-8 strings under Windows 70 /// 71 template<typename CharType, typename Traits = std::char_traits<CharType> > 72 class basic_ifstream : public detail::fstream_impl<CharType, Traits, detail::StreamTypeIn> 73 { 74 typedef detail::fstream_impl<CharType, Traits, detail::StreamTypeIn> fstream_impl; 75 76 public: basic_ifstream()77 basic_ifstream() 78 {} 79 basic_ifstream(const char * file_name,std::ios_base::openmode mode=std::ios_base::in)80 explicit basic_ifstream(const char* file_name, std::ios_base::openmode mode = std::ios_base::in) 81 { 82 open(file_name, mode); 83 } 84 #if BOOST_NOWIDE_USE_WCHAR_OVERLOADS basic_ifstream(const wchar_t * file_name,std::ios_base::openmode mode=std::ios_base::in)85 explicit basic_ifstream(const wchar_t* file_name, std::ios_base::openmode mode = std::ios_base::in) 86 { 87 open(file_name, mode); 88 } 89 #endif 90 basic_ifstream(const std::string & file_name,std::ios_base::openmode mode=std::ios_base::in)91 explicit basic_ifstream(const std::string& file_name, std::ios_base::openmode mode = std::ios_base::in) 92 { 93 open(file_name, mode); 94 } 95 96 template<typename Path> basic_ifstream(const Path & file_name,typename detail::enable_if_path<Path,std::ios_base::openmode>::type mode=std::ios_base::in)97 explicit basic_ifstream( 98 const Path& file_name, 99 typename detail::enable_if_path<Path, std::ios_base::openmode>::type mode = std::ios_base::in) 100 { 101 open(file_name, mode); 102 } 103 using fstream_impl::open; 104 using fstream_impl::is_open; 105 using fstream_impl::close; 106 using fstream_impl::rdbuf; 107 using fstream_impl::swap; 108 basic_ifstream(const basic_ifstream&) = delete; 109 basic_ifstream& operator=(const basic_ifstream&) = delete; basic_ifstream(basic_ifstream && other)110 basic_ifstream(basic_ifstream&& other) noexcept : fstream_impl(std::move(other)) 111 {} operator =(basic_ifstream && rhs)112 basic_ifstream& operator=(basic_ifstream&& rhs) noexcept 113 { 114 fstream_impl::operator=(std::move(rhs)); 115 return *this; 116 } 117 }; 118 119 /// 120 /// \brief Same as std::basic_ofstream<char> but accepts UTF-8 strings under Windows 121 /// 122 123 template<typename CharType, typename Traits = std::char_traits<CharType> > 124 class basic_ofstream : public detail::fstream_impl<CharType, Traits, detail::StreamTypeOut> 125 { 126 typedef detail::fstream_impl<CharType, Traits, detail::StreamTypeOut> fstream_impl; 127 128 public: basic_ofstream()129 basic_ofstream() 130 {} basic_ofstream(const char * file_name,std::ios_base::openmode mode=std::ios_base::out)131 explicit basic_ofstream(const char* file_name, std::ios_base::openmode mode = std::ios_base::out) 132 { 133 open(file_name, mode); 134 } 135 #if BOOST_NOWIDE_USE_WCHAR_OVERLOADS basic_ofstream(const wchar_t * file_name,std::ios_base::openmode mode=std::ios_base::out)136 explicit basic_ofstream(const wchar_t* file_name, std::ios_base::openmode mode = std::ios_base::out) 137 { 138 open(file_name, mode); 139 } 140 #endif basic_ofstream(const std::string & file_name,std::ios_base::openmode mode=std::ios_base::out)141 explicit basic_ofstream(const std::string& file_name, std::ios_base::openmode mode = std::ios_base::out) 142 { 143 open(file_name, mode); 144 } 145 template<typename Path> basic_ofstream(const Path & file_name,typename detail::enable_if_path<Path,std::ios_base::openmode>::type mode=std::ios_base::out)146 explicit basic_ofstream( 147 const Path& file_name, 148 typename detail::enable_if_path<Path, std::ios_base::openmode>::type mode = std::ios_base::out) 149 { 150 open(file_name, mode); 151 } 152 153 using fstream_impl::open; 154 using fstream_impl::is_open; 155 using fstream_impl::close; 156 using fstream_impl::rdbuf; 157 using fstream_impl::swap; 158 basic_ofstream(const basic_ofstream&) = delete; 159 basic_ofstream& operator=(const basic_ofstream&) = delete; basic_ofstream(basic_ofstream && other)160 basic_ofstream(basic_ofstream&& other) noexcept : fstream_impl(std::move(other)) 161 {} operator =(basic_ofstream && rhs)162 basic_ofstream& operator=(basic_ofstream&& rhs) 163 { 164 fstream_impl::operator=(std::move(rhs)); 165 return *this; 166 } 167 }; 168 169 #ifdef BOOST_MSVC 170 #pragma warning(push) 171 #pragma warning(disable : 4250) // <class> : inherits <method> via dominance 172 #endif 173 /// 174 /// \brief Same as std::basic_fstream<char> but accepts UTF-8 strings under Windows 175 /// 176 template<typename CharType, typename Traits = std::char_traits<CharType> > 177 class basic_fstream : public detail::fstream_impl<CharType, Traits, detail::StreamTypeInOut> 178 { 179 typedef detail::fstream_impl<CharType, Traits, detail::StreamTypeInOut> fstream_impl; 180 181 public: basic_fstream()182 basic_fstream() 183 {} basic_fstream(const char * file_name,std::ios_base::openmode mode=std::ios_base::in|std::ios_base::out)184 explicit basic_fstream(const char* file_name, 185 std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) 186 { 187 open(file_name, mode); 188 } 189 #if BOOST_NOWIDE_USE_WCHAR_OVERLOADS basic_fstream(const wchar_t * file_name,std::ios_base::openmode mode=std::ios_base::in|std::ios_base::out)190 explicit basic_fstream(const wchar_t* file_name, 191 std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) 192 { 193 open(file_name, mode); 194 } 195 #endif basic_fstream(const std::string & file_name,std::ios_base::openmode mode=std::ios_base::in|std::ios_base::out)196 explicit basic_fstream(const std::string& file_name, 197 std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) 198 { 199 open(file_name, mode); 200 } 201 template<typename Path> basic_fstream(const Path & file_name,typename detail::enable_if_path<Path,std::ios_base::openmode>::type mode=std::ios_base::in|std::ios_base::out)202 explicit basic_fstream(const Path& file_name, 203 typename detail::enable_if_path<Path, std::ios_base::openmode>::type mode = 204 std::ios_base::in | std::ios_base::out) 205 { 206 open(file_name, mode); 207 } 208 209 using fstream_impl::open; 210 using fstream_impl::is_open; 211 using fstream_impl::close; 212 using fstream_impl::rdbuf; 213 using fstream_impl::swap; 214 basic_fstream(const basic_fstream&) = delete; 215 basic_fstream& operator=(const basic_fstream&) = delete; basic_fstream(basic_fstream && other)216 basic_fstream(basic_fstream&& other) noexcept : fstream_impl(std::move(other)) 217 {} operator =(basic_fstream && rhs)218 basic_fstream& operator=(basic_fstream&& rhs) 219 { 220 fstream_impl::operator=(std::move(rhs)); 221 return *this; 222 } 223 }; 224 template<typename CharType, typename Traits> swap(basic_filebuf<CharType,Traits> & lhs,basic_filebuf<CharType,Traits> & rhs)225 void swap(basic_filebuf<CharType, Traits>& lhs, basic_filebuf<CharType, Traits>& rhs) 226 { 227 lhs.swap(rhs); 228 } 229 template<typename CharType, typename Traits> swap(basic_ifstream<CharType,Traits> & lhs,basic_ifstream<CharType,Traits> & rhs)230 void swap(basic_ifstream<CharType, Traits>& lhs, basic_ifstream<CharType, Traits>& rhs) 231 { 232 lhs.swap(rhs); 233 } 234 template<typename CharType, typename Traits> swap(basic_ofstream<CharType,Traits> & lhs,basic_ofstream<CharType,Traits> & rhs)235 void swap(basic_ofstream<CharType, Traits>& lhs, basic_ofstream<CharType, Traits>& rhs) 236 { 237 lhs.swap(rhs); 238 } 239 template<typename CharType, typename Traits> swap(basic_fstream<CharType,Traits> & lhs,basic_fstream<CharType,Traits> & rhs)240 void swap(basic_fstream<CharType, Traits>& lhs, basic_fstream<CharType, Traits>& rhs) 241 { 242 lhs.swap(rhs); 243 } 244 245 /// 246 /// Same as std::filebuf but accepts UTF-8 strings under Windows 247 /// 248 typedef basic_filebuf<char> filebuf; 249 /// 250 /// Same as std::ifstream but accepts UTF-8 strings under Windows 251 /// and *\::filesystem::path on all systems 252 /// 253 typedef basic_ifstream<char> ifstream; 254 /// 255 /// Same as std::ofstream but accepts UTF-8 strings under Windows 256 /// and *\::filesystem::path on all systems 257 /// 258 typedef basic_ofstream<char> ofstream; 259 /// 260 /// Same as std::fstream but accepts UTF-8 strings under Windows 261 /// and *\::filesystem::path on all systems 262 /// 263 typedef basic_fstream<char> fstream; 264 265 // Implementation 266 namespace detail { 267 /// Holds an instance of T 268 /// Required to make sure this is constructed first before passing it to sibling classes 269 template<typename T> 270 struct buf_holder 271 { 272 T buf_; 273 }; 274 template<typename CharType, typename Traits, typename T_StreamType, int> 275 class fstream_impl : private buf_holder<basic_filebuf<CharType, Traits> >, // must be first due to init order 276 public T_StreamType::template stream_base<CharType, Traits>::type 277 { 278 typedef basic_filebuf<CharType, Traits> internal_buffer_type; 279 typedef buf_holder<internal_buffer_type> base_buf_holder; 280 typedef typename T_StreamType::template stream_base<CharType, Traits>::type stream_base; 281 282 public: 283 using stream_base::setstate; 284 using stream_base::clear; 285 286 protected: 287 using base_buf_holder::buf_; 288 fstream_impl()289 fstream_impl() : stream_base(&buf_) 290 {} 291 fstream_impl(const fstream_impl&) = delete; 292 fstream_impl& operator=(const fstream_impl&) = delete; 293 294 // coverity[exn_spec_violation] fstream_impl(fstream_impl && other)295 fstream_impl(fstream_impl&& other) noexcept : base_buf_holder(std::move(other)), 296 stream_base(std::move(other)) 297 { 298 this->set_rdbuf(rdbuf()); 299 } operator =(fstream_impl && rhs)300 fstream_impl& operator=(fstream_impl&& rhs) noexcept 301 { 302 base_buf_holder::operator=(std::move(rhs)); 303 stream_base::operator=(std::move(rhs)); 304 return *this; 305 } swap(fstream_impl & other)306 void swap(fstream_impl& other) 307 { 308 stream_base::swap(other); 309 rdbuf()->swap(*other.rdbuf()); 310 } 311 open(const std::string & file_name,std::ios_base::openmode mode=T_StreamType::mode ())312 void open(const std::string& file_name, std::ios_base::openmode mode = T_StreamType::mode()) 313 { 314 open(file_name.c_str(), mode); 315 } 316 template<typename Path> open(const Path & file_name,std::ios_base::openmode mode=T_StreamType::mode ())317 typename detail::enable_if_path<Path, void>::type open(const Path& file_name, 318 std::ios_base::openmode mode = T_StreamType::mode()) 319 { 320 open(file_name.c_str(), mode); 321 } open(const char * file_name,std::ios_base::openmode mode=T_StreamType::mode ())322 void open(const char* file_name, std::ios_base::openmode mode = T_StreamType::mode()) 323 { 324 if(!rdbuf()->open(file_name, mode | T_StreamType::mode_modifier())) 325 setstate(std::ios_base::failbit); 326 else 327 clear(); 328 } 329 #if BOOST_NOWIDE_USE_WCHAR_OVERLOADS open(const wchar_t * file_name,std::ios_base::openmode mode=T_StreamType::mode ())330 void open(const wchar_t* file_name, std::ios_base::openmode mode = T_StreamType::mode()) 331 { 332 if(!rdbuf()->open(file_name, mode | T_StreamType::mode_modifier())) 333 setstate(std::ios_base::failbit); 334 else 335 clear(); 336 } 337 #endif is_open()338 bool is_open() 339 { 340 return rdbuf()->is_open(); 341 } is_open() const342 bool is_open() const 343 { 344 return rdbuf()->is_open(); 345 } close()346 void close() 347 { 348 if(!rdbuf()->close()) 349 setstate(std::ios_base::failbit); 350 } 351 rdbuf() const352 internal_buffer_type* rdbuf() const 353 { 354 return const_cast<internal_buffer_type*>(&buf_); 355 } 356 }; 357 #ifdef BOOST_MSVC 358 #pragma warning(pop) 359 #endif 360 /// Trait to heuristically check for a *\::filesystem::path 361 /// Done by checking for make_preferred and filename member functions with correct signature 362 template<typename T> 363 struct is_path 364 { 365 typedef char one; 366 struct two 367 { 368 char dummy[2]; 369 }; 370 371 template<typename U, U& (U::*)(), U (U::*)() const> 372 struct Check; 373 template<typename U> 374 static one test(Check<U, &U::make_preferred, &U::filename>*); 375 template<typename U> 376 static two test(...); 377 378 enum 379 { 380 value = sizeof(test<T>(0)) == sizeof(one) 381 }; 382 }; 383 template<bool B, typename T> 384 struct enable_if 385 {}; 386 template<typename T> 387 struct enable_if<true, T> 388 { 389 typedef T type; 390 }; 391 /// SFINAE trait which has a member "type = Result" if the Path is a *\::filesystem::path 392 template<typename Path, typename Result> 393 struct enable_if_path : enable_if<is_path<Path>::value, Result> 394 {}; 395 } // namespace detail 396 } // namespace nowide 397 } // namespace boost 398 399 #endif 400